A chave é a diretiva Limit . A pista está presente na seção <Limit> Directive do Apache core mod página doc.
When using
<Limit>or<LimitExcept>directives with theRequiredirective, note that the firstRequireto succeed authorizes the request, regardless of the presence of otherRequiredirectives.
Como se constata, o primeiro Require que corresponde ao caso em questão decide como é processado. Com a configuração da minha pergunta, isto é:
<Limit GET POST OPTIONS>
Require all granted
</Limit>
<LimitExcept GET POST OPTIONS>
Require all denied
</LimitExcept>
AuthType Basic
AuthName "Personal HTML"
AuthUserFile "/usr/share/apache2/passwd/passwords"
Require valid-user
com um simples pedido GET, é o Require all granted na segunda linha da listagem acima que faz a coisa. O AuthType Basic e outras linhas abaixo dele são código morto. Um caminho certo a seguir é:
<Limit GET POST OPTIONS>
AuthType Basic
AuthName "Personal HTML"
AuthUserFile "/usr/share/apache2/passwd/passwords"
Require valid-user
</Limit>
<LimitExcept GET POST OPTIONS>
Require all denied
</LimitExcept>
Como um aparte, aqui está um caso em que a diretiva Limit é ignorada.
Require all granted
<Limit GET POST OPTIONS>
AuthType Basic
AuthName "Personal HTML"
AuthUserFile "/usr/share/apache2/passwd/passwords"
Require valid-user
</Limit>
<LimitExcept GET POST OPTIONS>
Require all denied
</LimitExcept>
A primeira linha é bem-sucedida e autoriza a solicitação.