Como criar um arquivo .htpasswd: guia de HTTP Basic Auth
Um arquivo .htpasswd é o repositório de credenciais do lado do servidor para a HTTP Basic Authentication: um arquivo de texto puro em que cada linha é um par username:hash. Para criar um arquivo .htpasswd, você gera essa linha com o hash e a salva em algum lugar que seu servidor web consiga ler. Há três maneiras de fazer isso:
- O comando
htpasswd(doapache2-utils/httpd-tools) — a ferramenta canônica. openssl passwd— já vem instalado em praticamente todo lugar, sem pacote extra.- No seu navegador — use o gerador de htpasswd para criar uma entrada localmente, sem instalar nada e sem enviar nada pela rede.
Este guia vai além do comando de uma linha. Veremos como o handshake do Basic Auth funciona de fato, como criar o arquivo de três formas, qual dos cinco formatos de hash escolher, como integrá-lo a Apache, nginx, Docker, Kubernetes, Caddy e Traefik, e como blindar tudo para você não publicar um arquivo de credenciais que qualquer um pode baixar.
O que é um arquivo .htpasswd?
Cada linha de um arquivo .htpasswd guarda as credenciais de um usuário como um par separado por dois-pontos. O nome de usuário é armazenado como está; a senha é armazenada apenas como um hash de mão única, então o texto puro nunca é gravado em disco. A anatomia de uma única linha bcrypt fica assim:
admin : $2y$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy
│ │
└─ username └─ hash (algorithm prefix $2y$ + cost + salt + digest)
Primeiro vem o nome de usuário, depois um único :, depois o hash. Um nome de usuário pode ter até 255 bytes e nunca pode conter dois-pontos, já que o dois-pontos é o separador de campos. O hash carrega seu próprio marcador de algoritmo como prefixo ($2y$ para bcrypt, $apr1$ para o MD5 do Apache, {SHA} para SHA-1), de modo que o servidor sabe como verificá-lo sem nenhuma configuração extra.
Para vários usuários, você adiciona uma linha por usuário:
admin:$2y$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy
alice:$2y$10$3bQ8xY7tLp2mZ0xW5cR4fO9vK1jH6sD2nG8aQ5wE3rT7uI4oP1cm
bob:$apr1$mZ0xW5cR$4fK1jH6sD2nG8aQ5wE3rT2
Os algoritmos podem ser misturados no mesmo arquivo. O servidor lê cada linha, detecta o formato pelo prefixo e verifica usando o que foi empregado. Aqui dois usuários bcrypt e um usuário apr1 convivem sem problema.
.htpasswd vs .htaccess
Esses dois são confundidos o tempo todo porque andam juntos no Apache, mas fazem trabalhos diferentes. O .htaccess é o arquivo de configuração por diretório do Apache. Ele contém diretivas — inclusive as que ativam o Basic Auth e apontam para o seu repositório de credenciais. O .htpasswd é o banco de credenciais — apenas as linhas username:hash, sem nenhuma configuração.
Em resumo: o .htaccess decide que um diretório exige login e onde encontrar a lista de usuários; o .htpasswd é essa lista de usuários. O nginx não usa .htaccess de jeito nenhum — sua configuração de Basic Auth fica no bloco server ou location da configuração principal, mas ele lê o mesmo formato de credenciais .htpasswd.
Como funciona a HTTP Basic Authentication
A HTTP Basic Authentication é um handshake de desafio-resposta embutido na especificação do HTTP. Entender as três etapas torna óbvio cada passo posterior de diagnóstico:
- O cliente requisita um recurso protegido sem credenciais.
- O servidor responde
401 Unauthorizede inclui um cabeçalhoWWW-Authenticate: Basic realm="..."— esse é o desafio. - O cliente repete a requisição com um cabeçalho
Authorization: Basic <base64(user:password)>. Se as credenciais coincidirem com uma linha do arquivo.htpasswd, o servidor devolve o recurso.
É esse o protocolo inteiro. Não há formulário de login, nem cookie de sessão, nem token. Toda requisição seguinte carrega o mesmo cabeçalho.
O desafio 401 / WWW-Authenticate
O cabeçalho WWW-Authenticate faz duas coisas. Seu token Basic informa ao cliente qual esquema usar, e sua string realm rotula o espaço de proteção. Os navegadores mostram o texto do realm na caixa de diálogo de login (“O site diz: …”) e o usam como chave de cache: as credenciais informadas para um realm são reutilizadas em outras URLs do mesmo realm, então o usuário não é solicitado de novo a cada página.
Aqui está a troca crua, capturada com curl -i:
$ curl -i https://example.com/admin/
HTTP/2 401
www-authenticate: Basic realm="Restricted Area"
$ curl -i -u admin:s3cret https://example.com/admin/
HTTP/2 200
O cabeçalho Authorization: Basic
A credencial que o cliente envia é base64(username:password). E aqui está o detalhe de segurança que pega muita gente: base64 é codificação, não criptografia. Qualquer pessoa reverte isso, então a credencial trafega, na prática, em texto puro. Você mesmo pode ver a ida e volta:
# Encode the credential the way a browser does
printf 'admin:s3cret' | base64
# → YWRtaW46czNjcmV0
# Anyone who captures the header can decode it instantly
printf 'YWRtaW46czNjcmV0' | base64 -d
# → admin:s3cret
Essa reversibilidade é o motivo de o Basic Auth ter de rodar sobre HTTPS — sem TLS, a senha fica legível para qualquer um no caminho da rede. Se você quiser construir ou inspecionar esse cabeçalho à mão, o codificador/decodificador Base64 faz a mesma transformação de user:password no navegador.
Como criar um arquivo .htpasswd
Há três formas práticas de criar o arquivo. Escolha com base no que está instalado e onde você quer que a senha fique.
Usando o comando htpasswd
O binário htpasswd vem no pacote de utilitários do Apache. Instale-o primeiro:
# Debian / Ubuntu
sudo apt install apache2-utils
# RHEL / CentOS / Fedora
sudo yum install httpd-tools
Crie o arquivo e seu primeiro usuário. A flag -c significa create, e ela vai sobrescrever um arquivo existente — use-a apenas na primeira vez:
htpasswd -c /etc/nginx/.htpasswd admin
# prompts twice for the password, then writes the file
Para adicionar mais usuários, tire o -c para anexar em vez de apagar tudo:
htpasswd /etc/nginx/.htpasswd alice
Para forçar o bcrypt em vez do padrão da plataforma, adicione -B. Para imprimir a entrada na saída padrão sem tocar em nenhum arquivo — útil para encadear em uma configuração ou em um Dockerfile — combine -b (senha na linha de comando) e -n (sem arquivo):
htpasswd -Bbn admin 's3cret'
# → admin:$2y$10$N9qo8uLOickgx2ZMRZoMye...
As flags que você realmente vai usar:
| Flag | Significado |
|---|---|
-c | Cria um novo arquivo (sobrescreve se já existir) — apenas o primeiro usuário |
-B | Usa bcrypt |
-b | Recebe a senha como argumento de linha de comando (sem prompt) |
-n | Imprime na saída padrão em vez de gravar um arquivo |
-D | Remove o usuário indicado do arquivo |
Uma ressalva sobre o -b: a senha acaba no histórico do seu shell. Para credenciais de produção pontuais, prefira a forma com prompt ou a opção do navegador abaixo.
Sem o apache2-utils — usando o OpenSSL
Não tem o binário htpasswd? O OpenSSL está em praticamente todo sistema e consegue produzir um hash apr1 diretamente. Envolva-o em printf para montar uma linha completa:
printf "admin:$(openssl passwd -apr1 's3cret')\n" >> /etc/nginx/.htpasswd
# admin:$apr1$k3l4Hj9.$qN8vY7tLp2mZ0xW5cR4f.
O formato apr1 é portável tanto no Apache quanto no nginx, o que faz dele a rota com menos dependências em uma máquina enxuta.
No navegador — sem instalar, sem vazar
Se você não quer instalar um pacote, ou prefere não digitar uma senha de produção num shell onde ela cai no ~/.bash_history, gere a entrada no lado do cliente. O gerador de htpasswd calcula hashes bcrypt, apr1 e SHA-1 inteiramente no seu navegador, entrega uma linha user:hash pronta para colar mais um bloco de configuração de servidor correspondente, e nunca transmite nada. Já que você está lá, crie uma senha forte e única com o gerador de senhas aleatórias em vez de reaproveitar uma.
Formatos de senha do htpasswd comparados
O comando htpasswd pode emitir cinco formatos, e eles não são equivalentes. Esta tabela é a referência rápida para escolher um:
| Formato | Prefixo | Com salt | Força | Use para |
|---|---|---|---|---|
| bcrypt | $2y$ | Sim | Mais forte | Apache, Docker Registry, Caddy, Traefik |
| apr1 (MD5 do Apache) | $apr1$ | Sim | Moderada | nginx (portável, padrão seguro) |
| SHA-1 | {SHA} | Não | Fraca | Apenas compatibilidade legada |
| crypt (DES) | (nenhum) | Sim (2 caracteres) | Muito fraca | Não use |
| plain | (nenhum) | Não | Nenhuma | Apenas testes locais |
Algumas observações que não cabem numa célula de tabela. O bcrypt usa um salt aleatório de 16 bytes e um fator de custo adaptativo (padrão 10, recomendação moderna 12), de modo que senhas idênticas produzem hashes diferentes e o fator de trabalho acompanha o hardware. Tem um detalhe peculiar: o bcrypt trunca a senha em 72 bytes, e qualquer coisa além disso é ignorada sem aviso. O apr1 roda 1.000 rodadas de MD5 com salt. É bem mais fraco que o bcrypt, mas tanto o Apache quanto o nginx o implementam nativamente, e é por isso que ele acaba sendo a escolha portável. O SHA-1 não usa salt, então senhas idênticas geram digests idênticos e tabelas rainbow se aplicam; guarde-o só para sistemas legados. O crypt e o plain existem por razões históricas e de teste, e nenhum dos dois tem lugar em produção.
Os prefixos $2a$ / $2b$ / $2y$
Você verá hashes bcrypt começando com $2a$, $2b$ ou $2y$. São o mesmo algoritmo e produzem hashes equivalentes e intercambiáveis; as letras de versão são resquícios de correções históricas de bugs na forma como certas bibliotecas tratavam caracteres de bit alto e o comprimento das strings. O htpasswd do Apache emite $2y$, e Caddy, Traefik e Docker Registry todos o verificam corretamente.
Se você quiser a comparação mais aprofundada do bcrypt frente a alternativas modernas, o guia bcrypt vs Argon2 vs scrypt explica como esses algoritmos de hash de senha diferem em custo, dureza de memória e modelo de ameaça.
Configurando o Basic Auth no seu servidor
Um arquivo de credenciais não faz nada sozinho: você precisa dizer ao servidor que ele é obrigatório. Aqui estão seis plataformas.
Apache (.htaccess)
Coloque isto em um arquivo .htaccess no diretório que você quer proteger (ou em um bloco <Directory> no seu vhost):
AuthType Basic
AuthName "Restricted Area"
AuthUserFile /etc/apache2/.htpasswd
Require valid-user
AuthName é a string de realm que o navegador mostra; AuthUserFile é o caminho absoluto até o seu arquivo de credenciais; Require valid-user aceita qualquer usuário listado nele.
nginx (auth_basic)
O nginx coloca a configuração em um bloco location ou server — não existe .htaccess:
location /admin/ {
auth_basic "Restricted Area";
auth_basic_user_file /etc/nginx/.htpasswd;
}
Recarregue com nginx -s reload. Use o formato apr1 aqui. O nginx delega a verificação do bcrypt ao crypt() do sistema, que falha em muitas builds (mais sobre isso na seção de diagnóstico), enquanto o apr1 é verificado internamente em toda plataforma.
Docker Registry e ingress-nginx do Kubernetes
O backend htpasswd de um Docker Registry privado aceita apenas bcrypt. Gere a entrada, monte-a e aponte o registry para ela:
# Generate a bcrypt entry into a file
htpasswd -Bbn admin 's3cret' > auth/htpasswd
# Run the registry with that file
docker run -d -p 5000:5000 \
-v "$(pwd)/auth:/auth" \
-e REGISTRY_AUTH=htpasswd \
-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
registry:2
Para o ingress-nginx do Kubernetes, armazene o arquivo como um Secret e referencie-o com anotações:
kubectl create secret generic basic-auth --from-file=auth=./auth/htpasswd
metadata:
annotations:
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: basic-auth
nginx.ingress.kubernetes.io/auth-realm: "Authentication Required"
Repare que a chave do Secret precisa se chamar auth — o ingress-nginx procura exatamente por essa chave.
Caddy e Traefik
Ambos esperam hashes bcrypt. O Caddy usa a diretiva basic_auth (cole o hash bcrypt, não o texto puro):
example.com {
basic_auth /admin/* {
admin $2y$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy
}
}
O Traefik usa um middleware basicauth, com pares user:bcrypt-hash (escape qualquer $ conforme o formato da sua configuração):
http:
middlewares:
admin-auth:
basicAuth:
users:
- "admin:$2y$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy"
Depois que um endpoint está protegido, verifique se funciona pela linha de comando. O construtor de comandos cURL monta a requisição -u user:pass para você, de modo que você consiga confirmar tanto o 401 quanto o 200 autenticado.
Boas práticas de segurança
O Basic Auth é simples, e isso torna fácil identificar as poucas formas de usá-lo mal.
- Sempre sirva por HTTPS. A credencial é base64 reversível, então o HTTP puro expõe a senha na rede. Faça a terminação do TLS na frente de qualquer endpoint protegido, sem exceções.
- Guarde o arquivo fora da raiz web. Se o
.htpasswdficar em um diretório servido, uma má configuração pode deixar que alguém o baixe. Mantenha-o em algum lugar como/etc/nginx/.htpasswd, definachmod 640e faça com que pertença ao usuário do servidor web (www-data,nginx), para que o servidor consiga lê-lo e as outras contas não. - Use senhas fortes e únicas. Cada conta deve receber sua própria senha de alta entropia do gerador de senhas aleatórias, nunca reutilizada. Se você quiser entender o que “forte o suficiente” significa em bits, o explicador de entropia de senhas detalha a matemática.
- Conheça os limites. O Basic Auth não tem logout e não tem sessão: o navegador faz cache da credencial por realm até você fechá-lo, e ela é reenviada a cada requisição. Para um checklist mais amplo sobre hashing, cabeçalhos e validação, veja nossas boas práticas de segurança web.
Resolvendo erros comuns
nginx: crypt_r() failed (22: Invalid argument)
Esta é a falha de Basic Auth no nginx mais comum de todas, e ela sempre significa a mesma coisa: o nginx tentou verificar um hash bcrypt ($2y$) numa libc que não inclui o esquema Blowfish — tipicamente o musl do Alpine ou uma glibc mais antiga. A correção é regerar a entrada como apr1, que o nginx verifica internamente em qualquer plataforma:
printf "admin:$(openssl passwd -apr1 's3cret')\n" > /etc/nginx/.htpasswd
nginx -s reload
Trocar por uma imagem base cuja libc suporte bcrypt também funciona, mas o apr1 é a solução mais simples e portável.
401 mesmo com a senha certa
Quando você tem certeza de que a senha está correta mas ainda recebe um 401, percorra este checklist em ordem:
- Caminho do arquivo. Confirme que o
AuthUserFile/auth_basic_user_fileaponta para o arquivo de verdade (caminho absoluto, sem erros de digitação). - Permissões. O usuário do servidor web precisa conseguir ler o arquivo. Verifique com
sudo -u www-data cat /etc/nginx/.htpasswd. - Quebras de linha / codificação. Um arquivo editado no Windows pode carregar caracteres
\rque corrompem o hash. Rodefile .htpasswde apliquedos2unixse necessário. - Cache velho do navegador. O navegador faz cache das credenciais por realm. Teste numa janela privada/anônima para descartar uma senha antiga lembrada.
- Hash incompatível. Verifique se o hash armazenado de fato corresponde à senha — cole os dois no modo de verificação do gerador de htpasswd para confirmar antes de culpar a configuração.
Quando NÃO usar o Basic Auth
O Basic Auth é a ferramenta certa para um conjunto restrito de tarefas: um site de staging, um caminho administrativo interno, um endpoint de artefatos de CI, um painel de métricas, um registry privado. É sem dependências e leva dois minutos para configurar.
É a ferramenta errada para o login de um produto. Não há logout, redefinição de senha, limitação de taxa, bloqueio de conta nem MFA. As credenciais são reenviadas a cada requisição e ficam em cache no navegador até ele fechar. Para qualquer coisa em que os usuários façam login, recorra a sessões, OAuth ou OIDC. Reconhecer esse limite é o que mantém o Basic Auth útil onde ele de fato se encaixa.
FAQ
O que uma única linha de um arquivo .htpasswd realmente contém?
Um par username:hash separado por dois-pontos. O hash começa com um prefixo de algoritmo ($2y$ para bcrypt, $apr1$ para o MD5 do Apache, {SHA} para SHA-1), seguido do salt e do digest. A senha em texto puro nunca aparece no arquivo.
Qual é a diferença entre .htpasswd e .htaccess?
O .htaccess é o arquivo de configuração por diretório do Apache — ele ativa o Basic Auth e aponta para o repositório de credenciais. O .htpasswd é esse repositório de credenciais, que guarda as linhas username:hash. O nginx usa o formato .htpasswd, mas configura a autenticação nos seus blocos server/location, não no .htaccess.
Como adiciono, altero ou removo um usuário em um arquivo .htpasswd?
Para adicionar ou alterar um usuário, rode htpasswd /path/.htpasswd username sem o -c — se o usuário existir, o hash dele é atualizado. Para remover um, rode htpasswd -D /path/.htpasswd username. Use o -c apenas para o primeiro usuário, já que ele sobrescreve o arquivo inteiro.
Como o navegador lembra as credenciais do Basic Auth, e como os usuários fazem logout?
O navegador faz cache das credenciais indexadas por realm e as reenvia automaticamente a cada requisição correspondente. Não há logout padrão: as únicas formas de limpá-las são fechar o navegador ou apagar o cache dele. Esse logout ausente é uma das razões pelas quais o Basic Auth não serve para autenticação de produto.
Posso usar o mesmo arquivo .htpasswd para Apache e nginx?
Sim, desde que o formato do hash seja suportado em ambos. O apr1 (MD5 do Apache) é verificado nativamente pelo Apache e pelo nginx em todo lugar, então é a escolha compartilhada mais segura. O bcrypt funciona no Apache, mas no nginx depende do crypt() do sistema, que falha em builds Alpine/musl.
A HTTP Basic Authentication ainda é relevante em 2026?
Sim. Como uma barreira leve em cima do HTTPS — ferramentas internas, ambientes de staging, registries privados, endpoints de monitoramento — ela continua prática e sem dependências. Só não a confunda com a autenticação de produto voltada ao usuário, que precisa de sessões, redefinições, limitação de taxa e MFA que o Basic Auth não consegue oferecer.
Revisado pela equipe Go Tools: cada comando, bloco de configuração e formato de hash deste guia foi verificado contra a saída de referência do Apache htpasswd (apache2-utils) e do OpenSSL.