Entendendo o arquivo /etc/hosts

Mascote do LinuxPro escrevendo com um marcador no quadro que mapeia endereços a nomes, com o atalho local mais curto que o caminho até o servidor DNS

Nota (2026): post de 2017, revisado. Uma correção importante: o texto original sugeria domínios .dev para desenvolvimento local. Não use. O .dev virou um TLD real do Google e está na lista de HSTS preload — os navegadores forçam HTTPS nele, e o seu site local em HTTP simplesmente deixa de abrir. O certo hoje é .test ou .localhost. A explicação está na seção sobre nomes.

O /etc/hosts é a lista de nomes que a sua máquina resolve sozinha, sem perguntar a ninguém. Uma linha ali tem precedência sobre o DNS inteiro da internet — o que faz dele uma ferramenta ótima para desenvolvimento e testes, e uma armadilha das boas quando alguém esquece uma linha lá dentro.

O formato

Cada linha mapeia um endereço IP para um ou mais nomes. O # comenta:

endereço_IP      nome_canônico      [apelido...]

Um arquivo típico:

127.0.0.1       localhost
127.0.1.1       minha-maquina
192.168.0.10    servidor.local     servidor

# IPv6
::1             localhost ip6-localhost ip6-loopback

O 127.0.0.1 é o loopback: sua própria máquina. O 127.0.1.1, que aparece no Debian e no Ubuntu, existe para o hostname da máquina resolver mesmo sem rede configurada — não é engano nem duplicata.

Para ver o seu:

cat /etc/hosts

E para editar, precisa de privilégio, porque é arquivo de sistema:

sudo nano /etc/hosts

Como testar (e por que não com ping)

A ferramenta certa é o getent, porque ele consulta exatamente a mesma cadeia de resolução que os programas usam:

getent hosts servidor.local
# 192.168.0.10    servidor.local servidor

Aqui mora uma confusão clássica, com uma reviravolta moderna. Historicamente, dig e nslookup falam direto com o servidor DNS e ignoram o /etc/hosts — daí o conselho, repetido há anos, de que testar com dig não serve.

Só que nas distribuições com systemd-resolved (Ubuntu moderno, Fedora) o dig consulta o stub em 127.0.0.53, e o resolved responde a partir do /etc/hosts. Ou seja: ali o dig enxerga o arquivo. Para ver o que o DNS externo realmente responde, aponte um servidor explicitamente:

getent hosts site.exemplo         # o que a maquina resolve (le /etc/hosts)
dig +short @8.8.8.8 site.exemplo  # o que o DNS externo responde
curl -I http://site.exemplo       # o teste de verdade, ponta a ponta

O getent continua sendo a resposta certa para “o que esta máquina realmente resolve”, porque é o único que percorre a mesma cadeia que os programas usam.

Quem decide a ordem: o nsswitch.conf

O /etc/hosts ter prioridade sobre o DNS não é lei da natureza — é configuração, e ela vive em outro arquivo:

grep hosts /etc/nsswitch.conf
# algo como: hosts: files mdns4_minimal [NOTFOUND=return] dns mymachines

A linha exata varia: o Debian puro traz apenas files dns, e o Fedora traz files myhostname resolve [!UNAVAIL=return] dns. O que importa é a posição relativa.

Lê-se da esquerda para a direita. files é o /etc/hosts, e por vir primeiro é consultado antes do dns. Se alguém inverter essa ordem, o arquivo perde a precedência — é o primeiro lugar para olhar quando uma entrada aparentemente correta é ignorada.

Nas distribuições que usam systemd-resolved (Ubuntu moderno, Fedora), o /etc/resolv.conf costuma apontar para 127.0.0.53 e o cache fica com o serviço. Isso não muda a precedência do /etc/hosts, que continua sendo consultado antes — mas muda o lugar de investigar o resto:

resolvectl status              # quais servidores DNS estao em uso
resolvectl query site.exemplo  # como o resolved resolve esse nome
sudo resolvectl flush-caches   # limpar o cache do resolvedor

Nomes para desenvolvimento local: use .test

Aqui está o erro mais comum, e a razão desta revisão. Durante anos se usou .dev para projeto local:

127.0.0.1    meuapp.dev      # NAO FACA ISSO

A cronologia costuma ser contada errado, e ela importa. O .dev é um TLD do Google desde 2014, mas o que quebrou o uso local não foi a venda de domínios: foi o HSTS preload. Em setembro de 2017 o TLD inteiro entrou na lista embutida do Chromium e, a partir do Chrome 63, em dezembro de 2017, qualquer endereço .dev passou a ser forçado para HTTPS antes mesmo de sair uma requisição. O registro público só abriu em fevereiro de 2019 — ou seja, o .dev já estava inviável para desenvolvimento local mais de um ano antes de existir um único site .dev no mundo.

E não há contorno: a entrada cobre o TLD inteiro com include_subdomains, a lista vem embutida no Chrome, no Firefox e no Edge, e domínio em HSTS preload também não oferece o botão de “prosseguir mesmo assim” na tela de erro de certificado. Seu servidor local em HTTP simplesmente nunca é alcançado.

Use os nomes que a RFC 6761 reserva justamente para isso e que nunca serão vendidos como domínio:

127.0.0.1    meuapp.test
127.0.0.1    api.meuapp.test
127.0.0.1    blog.meuapp.test

São reservados .test, .example, .invalid e .localhost. Para desenvolvimento, .test é o indicado. E há um bônus no .localhost: onde existe systemd-resolved ou o módulo myhostname no nsswitch.conf, qualquer *.localhost já vai para o loopback automaticamente — meuapp.localhost funciona sem editar arquivo nenhum. Isso vem do systemd, não da glibc: numa máquina sem nenhum dos dois, não funciona. Confira com getent hosts meuapp.localhost.

Usos que valem no dia a dia

Testar um servidor antes de virar o DNS. É o uso mais útil: você aponta o domínio real para o servidor novo só na sua máquina, confere que está tudo certo, e só então muda o DNS para valer.

203.0.113.50    www.meusite.com.br    meusite.com.br

O navegador vai ao servidor novo enquanto o mundo inteiro continua indo ao antigo. Terminado o teste, apague a linha — esquecê-la ali é a origem clássica do “o site mudou para todo mundo, menos para mim”.

Bloquear domínios. Apontar um nome para 0.0.0.0 faz a conexão falhar imediatamente:

0.0.0.0    dominio-indesejado.com

Com uma ressalva importante no Linux: conectar a 0.0.0.0 cai no seu próprio 127.0.0.1. Se você roda um servidor web local, o “bloqueio” vai exibir o seu site em vez de falhar. Só há recusa imediata quando não há nada escutando naquela porta.

Funciona para bloqueio pontual. Para bloqueio amplo, uma lista gigante no /etc/hosts penaliza toda resolução de nome da máquina — aí o caminho é um resolvedor com filtro, como Pi-hole ou dnsmasq.

Encurtar nomes de máquinas da rede local, para não decorar IP:

192.168.0.10    nas
192.168.0.20    impressora
192.168.0.30    servidor-web    web

Com isso, ssh web e ping nas passam a funcionar. Combina bem com o ~/.ssh/config do post sobre chaves SSH.

Quando não funciona

Três causas explicam quase todos os casos:

  • Cache do navegador. Chrome e Firefox mantêm cache de DNS próprio. Teste antes com getent hosts nome o curl; se responder certo no terminal e errado no navegador, o problema é o cache dele (no Chrome, chrome://net-internals/#dns).
  • Falta o nome exato. meusite.com.br e www.meusite.com.br são nomes diferentes. Coloque os dois na linha.
  • Ordem no nsswitch.conf alterada, com dns antes de files.

Vale saber também que o /etc/hosts resolve apenas endereços. Não há como mapear registro MX, SRV ou TXT ali — quem tenta configurar e-mail pelo arquivo bate a cabeça à toa.

E o aviso que fecha o assunto: o /etc/hosts só vale na máquina onde ele está. Não existe “propagar” — em servidor, o lugar de mapear nome é o DNS, assunto do post Conhecendo o DNS.

Para continuar