Understanding the /etc/hosts file

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

Note (2026): 2017 post, revised. An important correction: the original text suggested domains .dev for local development. Do not use them. .dev became a real Google TLD and is on the HSTS preload list — browsers force HTTPS on it, and your local HTTP site simply stops opening. The correct choice today is .test or .localhost. The explanation is in the section on names.

The /etc/hosts is the list of names your machine resolves on its own, without asking anyone. A line there takes precedence over the entire Internet’s DNS — making it a great tool for development and testing, and quite a trap when someone forgets a line inside.

The format

Each line maps an IP address to one or more names. The # marks a comment:

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

A typical file:

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

The 127.0.0.1 is loopback: your own machine. The 127.0.1.1, which appears on Debian and Ubuntu, exists so the machine’s hostname resolves even without a configured network — it is not a mistake or a duplicate.

To see yours:

cat /etc/hosts

And editing requires privileges, because it is a system file:

sudo nano /etc/hosts

How to test (and why not with ping)

The right tool is getent, because it queries exactly the same resolution chain programs use:

getent hosts servidor.local
# 192.168.0.10    servidor.local servidor

Here lies a classic confusion, with a modern twist. Historically, dig and nslookup talk directly to the DNS server and ignore /etc/hosts — hence the advice, repeated for years, that testing with dig does not work.

But on distributions with systemd-resolved (modern Ubuntu, Fedora), dig queries the stub at 127.0.0.53, and resolved answers from /etc/hosts. In other words: there, dig sees the file. To see what external DNS actually answers, specify a server explicitly:

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

The getent remains the right answer to “what does this machine actually resolve”, because it is the only one that follows the same chain programs use.

Who decides the order: nsswitch.conf

The /etc/hosts taking priority over DNS is not a law of nature — it is configuration, and it lives in another file:

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

The exact line varies: plain Debian has only files dns, and Fedora has files myhostname resolve [!UNAVAIL=return] dns. What matters is relative position.

Read it from left to right. files is the /etc/hosts, and because it comes first it is consulted before dns. If someone reverses that order, the file loses precedence — it is the first place to look when an apparently correct entry is ignored.

On distributions that use systemd-resolved (modern Ubuntu, Fedora), /etc/resolv.conf usually points to 127.0.0.53 and the service manages the cache. This does not change the precedence of /etc/hosts, which is still consulted first — but it changes where to investigate the rest:

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

Names for local development: use .test

Here is the most common mistake, and the reason for this revision. For years people used .dev for local projects:

127.0.0.1    meuapp.dev      # NAO FACA ISSO

The chronology is often told incorrectly, and it matters. The .dev has been a Google TLD since 2014, but what broke local use was not domain sales: it was HSTS preload. In September 2017 the entire TLD entered Chromium’s built-in list and, starting with Chrome 63, in December 2017, any address .dev was forced to HTTPS before a request was even sent. Public registration only opened in February 2019 — meaning .dev was already unviable for local development more than a year before a single site .dev existed in the world.

And there is no workaround: the entry covers the entire TLD with include_subdomains, the list is built into Chrome, Firefox and Edge, and an HSTS-preloaded domain also does not offer the “proceed anyway” button on the certificate error screen. Your local HTTP server is simply never reached.

Use names that RFC 6761 reserves specifically for this purpose and that will never be sold as domains:

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

The reserved names are .test, .example, .invalid and .localhost. For development, .test is the recommended choice. And there is a bonus with .localhost: where there is systemd-resolved or the module myhostname in nsswitch.conf, any *.localhost already goes to loopback automatically — meuapp.localhost works without editing any files. This comes from systemd, not glibc: on a machine without either of the two, it does not work. Check with getent hosts meuapp.localhost.

Useful everyday applications

Test a server before switching DNS. This is the most useful application: point the real domain to the new server only on your machine, check that everything is right, and only then change DNS for real.

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

The browser goes to the new server while the entire world keeps going to the old one. Once the test is done, delete the line — forgetting it there is the classic cause of “the site changed for everyone except me”.

Block domains. Pointing a name to 0.0.0.0 makes the connection fail immediately:

0.0.0.0    dominio-indesejado.com

With an important caveat on Linux: connecting to 0.0.0.0 lands on your own 127.0.0.1. If you run a local web server, the “block” will display your site instead of failing. Immediate refusal only happens when nothing is listening on that port.

It works for targeted blocking. For broad blocking, a giant list in /etc/hosts penalizes every name resolution on the machine — then the way forward is a filtering resolver, such as Pi-hole or dnsmasq.

Shorten machine names on the local network, so you do not have to memorize IPs:

192.168.0.10    nas
192.168.0.20    impressora
192.168.0.30    servidor-web    web

With this, ssh web and ping nas start working. It pairs well with the ~/.ssh/config from the post about SSH keys.

When it does not work

Three causes explain almost every case:

  • Browser cache. Chrome and Firefox maintain their own DNS caches. Test first with getent hosts nome or curl; if the terminal answers correctly but the browser does not, the problem is its cache (in Chrome, chrome://net-internals/#dns).
  • The exact name is missing. meusite.com.br and www.meusite.com.br are different names. Put both on the line.
  • Order in nsswitch.conf changed, with dns before files.

It is also worth knowing that /etc/hosts only resolves addresses. There is no way to map MX, SRV or TXT records there — anyone trying to configure email through the file is wasting their time.

And the warning that closes the subject: /etc/hosts only applies on the machine where it resides. There is no “propagation” — on a server, the place to map names is DNS, the topic of the post Getting to Know DNS.

To keep going