211 comments

[ 2.8 ms ] story [ 228 ms ] thread
(comment deleted)
(comment deleted)
I use .localhost for all my projects. Just one annoying note: Safari doesn't recognize the TLD localhost so it will try to perform a search. Adding a slash at the end will fix this; ie example.localhost/
Luckily the easy fix is available: Do not use Safari.
Avoid using `.local`. In my experience Chrome does not like it with HTTPS. It takes much much longer to resolve. I found a Chrome bug relating to this but do not have it handy to share. `.localhost` makes more sense for local development anyways.
Actually, MacOS gives your computer a .local domain on DHCP and Bonjour usually
if you add your CA to the list of trusted certificate, everything will be fine. I do not recommend using custom certificates and would stick to http, unless you really know what you are doing
I use .local all the time and it works just fine. For TLS I use my existing personal CA, but HTTP links don't cause issues for me.

That said, I do use mDNS/Bonjour to resolve .local addresses (which is probably what breaks .local if you're using it as a placeholder for a real domain). Using .local as a imaginary LAN domain is a terrible idea. These days, .internal is reserved for that.

.local is mDNS/Rendezvous/Bonjour territory. In some cases it takes longer to resolve because your machine will multicast a query for the owner of the name.

I use it extensively on my LAN with great success, but I have Macs and Linux machines with Avahi. People who don't shouldn't mess with it...

(comment deleted)
Honestly, if I had my druthers there would be a standardized exception for .local domains that self-signed HTTPS certs would be accepted without known roots. It's insane how there's no good workflow for HTTPS on LAN-only services.
It’s actually gotten worse, you need to run a CA or use a public domain where it’s easy to get your internal naming schemes in a transparency log.
The easy workaround I've seen companies use for that is by using a basic wildcard certificate (*.local.mydomain.biz).
Technically speaking you could use DANE with mDNS. Nobody does it, browser don't implemented it, but you can follow the spec if you'd like.

Practically speaking, HTTPS on LAN is essentially useless, so I don't see the benefits. If anything, the current situation allows the user to apply TOFU to local devices by adding their unsigned certs to the trust store.

Some more modern browser APIs only work in HTTPS. That's why I had to do it.
Modern browsers only enable those APIs because of security concerns, and those security concerns aren't lifted just because you're connected locally.

The existing exception mechanisms already work for this, all you need to do is click the "continue anyway" button.

Browsers won't use http2 unless https is on — chrome only allows six concurrent requests to the same domain if you're not using https!
> HTTPS on LAN is essentially useless

Public wifi isn't a thing? Nobody wants to admin the router on a wifi network where there might be untrusted machines running around?

Sure, but you can connect those devices to a real domain and use Let's Encrypt on them, or you can TOFU and add the self-signed cert to your browser; after you've verified that you're not being MitM'd by one of those untrusted devices, of course (I dunno, by printing the public key on the side of the device or something?).

In practice, you probably want an authorized network for management, and an open network with the management interface locked out, just in case there's a vulnerability in the management interface allowing auth bypass (which has happened more often than anyone would like).

The former just aren't practical for small business and home consumers, though. Browsers just don't have good workflow for TOFU.

I agree on the latter, but that means your IoT devices being accessible through both networks and being able to discriminate which requests are coming from the insecure interface and which are coming from secure admin, which isn't practical for lay users to configure as well. I mean, a router admin screen can handle that but what about other devices?

I know it seems pedantic, but this UI problem is one of many reasons why everything goes through the Cloud instead of our own devices living on our own networks, and I don't like that controlling most IoT devices (except router admin screens) involves going out to the Internet and then back to my own network. It's insecure and stupid and violates basic privacy sensibilities.

Ideally I want end users to be able to buy a consumer device, plug it into their router, assign it a name and admin-user credentials (or notify it about their credential server if they've got one), and it's ready and secure without having to do elaborate network topology stuff or having to install a cert onto literally every LAN client who wants to access its public interface.

I recommend using the .test TLD.

* It's reserved so it's not going to be used on the public internet.

* It is shorter than .local or .localhost.

* On QWERTY keyboards "test" is easy to type with one hand.

Once again, .local should _never_ have been assigned to any organization. Just like .lan should also be reserved like the private IP blocks.
Don't we have ".internal" for that?
The TLD hasn't been registered, but it has been added to the list of reserved names so effectively that's the domain you should use if you don't want to use real names.

.local also works fine, of course, if you enable mDNS and don't try to use normal DNS.

.local wasn't assigned to an organization, it was assigned to mDNS: multicast DNS. mDNS is the ask everyone on the local network if they like to be called that name which used to be better known under Apple's brand/trademark Bonjour, but now is a true standard.
Yes, but why couldn't they have assigned .mdns for that instead? Or even better given it it's own .arpa domain? E.G. .mdns(.arpa) rather than the .local TLD? ( https://en.wikipedia.org/wiki/.arpa )
Because .local looks nice and is a better name/explainer for what mDNS does than the standard name or the old brand name? Because the old brand was already using .local even if Apple Devices were somewhat a minority at the time?

At this point a lot of TLD changes are going to step on someone's project or home/business/private network. I think .local is a good name for mDNS. I appreciate why you maybe aren't happy with it, but don't share your concern.

Those are both reasons that .local should be static DNS on the _network_ like localhost is a standard name for the loopback address(es).

There's no reason .mdns or .mdns.arpa couldn't have just been added to the default domains search list (the list of suffixes tried for non FQDN searches); which given it ISN'T a nice human obvious word to append wouldn't have conflicted with anyone who'd already had a .local at the time, and anyone else in the future who thinks an obvious phrase like .local would not be in use by some other resolver system.

Wow! Today I learned that you can have subdomains of localhost. Never realized it!
.localhost is in the same list as .example and .invalid when it comes to reserved names: https://datatracker.ietf.org/doc/html/rfc2606

It's a neat trick, but it comes with some caveats. For instance, `localhost` often resolves to both 127.0.0.1 and ::1, but `.localhost` is described in RFC2606 as "traditionally been statically defined in host DNS implementations as having an A record pointing to the loop back IP address and is reserved for such use". In other words, your server may be binding to ::1 but your browser may be resolving 127.0.0.1. I'm sure later RFCs rectify the lack of IPv6 addressing, but I wouldn't assume everyone has updated to support those.

Another neat trick to combine with .localhost is using 127.0.0.0/8. There's nothing preventing you from binding server/containers to 127.0.0.2, 127.1.2.3, or 127.254.254.1. Quite useful if you want to run multiple different web servers together.

But is "foo.localhost" a valid domain name, for cookies and such?
The RFC treats .localhost as a full TLD. I believe Windows does as well, as does Ubuntu (using default systemd-resolved), but macOS doesn't seem to resolve .localhost by default, necessitating the host file trickery.

Of course, in the early internet, the difference between a TLD and a host name weren't quite as clear as they are right now.

We still need the Public Suffix List because of how inconsistent it was
> I believe Windows does as well

I cannot ping xyz.localhost because it doesn't resolve it.

I haven't done it for a while (I've mostly just used 127.*), but I found the best one to use for dev purposes was the IETF-reserved `.test` TLD [0]. The main benefit at the time I was messing with this (10ish years ago now) was that all the browsers I needed to test on would actually attempt to resolve `.test`. If I remember correctly, firefox seemed to have issues with `localhost` being anything other than 127.0.0.1 (and would simply go to that address ignoring whatever was in /etc/hosts or DNS, again IIRC). It's been a while, though, so that behavior might have changed.

[0] https://datatracker.ietf.org/doc/rfc2606/

.test seems like an excellent choice for testing/debugging/developing applications, but for running services you want to use I'd stick to .internal these days, as it was reserved for local domains last year.
This is what I've done for years: doing app development using the .test TLD, and .internal for, well, internal services that are more like "production IT".

I've had nothing but trouble with .local and .localhost. Specifically, .local is intended for other purposes (multicast DNS) and .localhost has a nasty habit of turning into just "localhost" and in some cases, resolvers don't like to allow that to point to anything other than 127.0.0.1.

More recently, I've stuck to following the advice of RFC 6762 and use an actual registered TLD for internal use, and then sub-domain from there. I don't use my "production" TLD, but some other, unrelated TLD. For example, if my company is named FOO and our corporate homepage is at foo.com, I'll have a separate TLD like bar.com that I'll use for app development (and sub-domain as dev.bar.com, qa.bar.com, and maybe otherqa.bar.com as needed for different environments). That helps avoid any weirdness around .localhost, works well in larger dev/qa environments that aren't running on 127.0.0.1, and allows me to do things like have an internal CA for TLS instead of using self-signed certs with all of their UX warts.

For "local network" stuff, I stick to ".internal" because that's now what IANA recommends. But I would distinguish between how I use ".internal" to mean "things on my local network" from "this is where my team deploys our QA environment", because we likely aren't on the same network as where that QA environment is located and my ".internal" might overlap with your ".internal", but the QA environment is shared.

For development, "localhost" has a convenience bonus: it has special treatment in browsers. Many browser APIs like Service Workers are only available on pages with a valid WebPKI cert, except for localhost.
On my Linux machine with systemd-resolved, this even works out the box:

  $ resolvectl query foo.localhost
  foo.localhost: 127.0.0.1                       -- link: lo
                 ::1                             -- link: lo
Another benefit is being able to block CSRF using the reverse proxy.
Yeah, I've been using localhost domains on Linux for a while. Even on machines without systemd-resolved, you can still usually use them if you have the myhostname module in your NSS DNS module list.

https://www.man7.org/linux/man-pages/man8/libnss_myhostname....

(There are lots of other useful NSS modules, too. I like the libvirt ones. Not sure if there's any good way to use these alongside systemd-resolved.)

I ended up writing a similar plugin[1] after searching in vain for a way to add temporary DNS entries.

The ability to add host entries via an environment variable turned out to be more useful than I'd expected, though mostly for MITM(proxy) and troubleshooting.

1: https://github.com/aib/nss-userhosts

Neat setup! I do something similar on OpenBSD. I have a CSV file that maps IP, MAC address, and hostname for various devices on my LAN. A shell script reads the file and creates a matching hosts file, dhcpd config, and unbound config, then restarts dhcpd and unbound (caching DNS server).

Whenever a host requests a DHCP lease it receives its assigned IP which matches the unbound record, then I can always access it by hostname.

I think you don't really need the /etc/hosts entry, I use this since google started using .dev domains and switched to using .localhost for everything local.

Never needed the entry

and you're still setting up a proxy for the port forwarding?
You might check out .internal instead which was recently approved [1] for local use.

[1]: https://en.wikipedia.org/wiki/.internal

In that case I would prefer naming as

  <virtual>.<physical-host>.internal
So for example

  phpbb.mtndew.internal
And I’d probably still add

  phpbb.localhost 
To /etc/hosts on that host like OP does
I wrote a super basic DNS server in go (mostly fun and go practice) which allows you to specify hosts and ips in a json config file. This eliminates the need for editing your /etc/hosts file. If it matches a host in the json config file it returns that ip, else uses Cloudflare public DNS resolver as a fallback. Please; easy on my go code :-). I am a total beginner with go.

https://github.com/nodesocket/godns

The *.home.arpa domain in RFC 8375 has been approved for local use since 2018, which is long enough ago that most hardware and software currently in use should be able to handle it.
It's ugly and clunky, which is why after seven years it's had very little adoption. Home users aren't network engineers so these things actually do matter even if it seems silly in a technical sense.
(comment deleted)
Why use that over *.localhost which has been available since 1999 (introduced in RFC 2606)
From RFC 2606:

  The ".localhost" TLD has traditionally been statically defined in
  host DNS implementations as having an A record pointing to the
  loop back IP address and is reserved for such use
The RFC 8375 suggestion (*.home.arpa) allows for more than a single host in the domain. If not in name/feeling, but the strictest readings [and adherence] too.
RFC 8375 seems to have approved it specifically to use in Home Networking Control Protocol, though it also states "it is not intended that the use of 'home.arpa.' be restricted solely to networks where HNCP is deployed. Rather, 'home.arpa.' is intended to be the correct domain for uses like the one described for '.home' in [RFC7788]: local name service in residential homenets."

The OpenWrt wiki on Homenet suggests the project might be dead: https://openwrt.org/docs/guide-user/network/zeroconfig/hncp_...

Anyone familiar with HNCP? Are there any concerns of conflicts if HNCP becomes "a thing"? I have to say, .home.arpa doesn't exactly roll of the tongue like .internal. Some macOS users seem to have issues with .home.arpa too: https://www.reddit.com/r/MacOS/comments/1bu62do/homearpa_is_...

> I have to say, .home.arpa doesn't exactly roll of the tongue like .internal.

In my native language (Finnish) it's even worse, or better, depending on personal preference - it translates directly to .mildew.lottery-ticket.

It would be more like .mold.ticket
Thanks, I always mix up mold and mildew. However, "arpa" is specifically a lottery ticket, whereas there are tickets for concerts, tickets to ride, tickets in Jira etc...
Arpa is used for all kinds of random chance things, not specifically for lottery. I feel like ticket would still be the equivalent but I guess that would be more transliteration and opinion than direct translation? Also my view for lottery may be skewed due to the Finnish lottery culture, and how lottery has more meanings in English. Sorry turned ranty.
Arguably, Jira ‘has issues’.
Check the errata for RFC 7788. .home being listed in it is a mistake. .home has never been designated for this purpose.

home.arpa is for HNCP.

Use .internal.

I have been commandeering .home for the boxes on my LAN since forever. Why change it?

If I were going to do a bunch of extra work messing with configs I'd be far more inclined to switch all my personal stuff over to GNS for security and privacy reasons.

I simply quoted RFC 8375. It specifically called out that while RFC 7788 mentions ".home" (quoted below), it wasn't reserved, which ".home.arpa" aims to fix. But while you say "home.arpa is for HNCP", I also quoted RFC 8375 stating it's available for other uses as well.

> A network-wide zone is appended to all single labels or unqualified zones in order to qualify them. ".home" is the default; [...]

It would be great if there was an easy way to get trusted certificates for reserved domains without rolling out a CA. There are a number of web technologies that don't work without a trusted HTTPS origin, and it's such a pain in the ass to add root CAs everywhere.
You can configure them to send requests through http proxy.
It seems like it has not been standardized yet:

> As of March 7, 2025, the domain has not been standardized by the Internet Engineering Task Force (IETF), though an Internet-Draft describing the TLD has been submitted.

Too much typing, and Chromium-based browsers don't understand it yet and try to search for mything.internal instead, which is annoying - you have to type out the whole http://mything.internal.

This can be addressed by hijacking an existing TLD for private use, e.g. mything.bb :^)

eh, you can just add search domain via dhcp or static configuration and just type out http://mything/ no need to enter whole domain unless you need todo ssl
(comment deleted)
Isn't just typing the slash at the end enough to avoid it searching? e.g. mything/
mything/ will make the OS resolve various hosts: mything., mything.local (mDNS), mything.whateverdomainyourhomenetworkuses. (which may be what you wanted).

If you want to be sure, use mything./ : the . at the end makes sure no further domains are appended during DNS lookup, and the / makes the browser try to access to resource without Googling it.

> Chromium-based browsers don't understand it yet and try to search for mything.internal instead, which is annoying

That's hardly the only example of annoying MONOBAR behavior.

This problem could have been avoided if we had different widgets for doing different things. Someone should have thought of that.

*.localhost is reserved for accessing the loopback interface. It is literally the perfect use for it. In fact on many operating systems (apparently not macOS) anything.localhost already resolves to the loopback address.
I recently tried to use the dnsmasq method mentioned at the very end but had some issues with fallthrough, as most of my dns traffic went trough my dev setup then first, which I didn't want. In the end I configured a "real" domain and let it point to 127.0.0.1, because I need arbitrary wildcard subdomains.. but I'm still not very happy with it because it feels like an unecessary dependency.
I do the same at work (on premise machines on a private LAN).
In my case I just setup a subdomain 'local.<domain>' to my personal domain and had Let's Encrypt create valid certificates for it via Traefik.

Each service is then exposed via '<service>.local.<domain>'.

This has been working flawlessly for me for some time.

Chrome and i think Firefox resolve all <name>.localhost domains to localhost per default, so you don't have to add them to the hosts file. I setup a docker proxy on port 80 that resolves all requests from <containername>.localhost to the first exposed port of that container (in order of appearing in the docker compose file) automatically which makes everything smooth without manual steps for docker compose based setups.
Source for this? Are you sure it's not your system resolver doing it?
It's probably both. Browsers now have built-in DoH so they usually do their own resolving. Only if you disable "secure DNS" (or you use group policies) will you fall back to the system resolver anymore.
There is a draft spec over it, Ill find it later, but they do hardcode it now and never touch dns.
Pretty sure its hard coded in the browser and never touches any resolvers. It does not work the same in safari for example.
> I then run and configure Caddy to redirect traffic from 127.0.0.1 to the right port for the domain.

That's not redirection per se, a word that's needlessly overloaded to the point of confusion. It's a smart use of a reverse proxy.

It would be nice if you all reserved the word "redirect" for something like HTTP 3xx behavior.

Anyone care to shed why myapp.localhost:3000 (for the webapp I'm developing) is something that's useful for me rather than localhost:3000 ?

EDIT: on linux and don't use launchd, so I'd still the port number

Using real domain names lets you experience the web as it is in production. Localhost has a bunch of exceptions (i.e. HTTP URLs are treated as secure, CORS acts funny, etc.). Using domain names disables special handling of localhost URLs that'll help you spot problems before they hit production.
Thanks. This is the reason I wanted rather then convenience of not typing a port number (which I'd use a bookmark for, so I really don't care)
Trick: edit yuor /etc/hosts file and add a domain name.

Self sign a certificate and add it to your trusted certificate list.

Or - use https://pinggy.io

(comment deleted)
It's `myapp.localhost` (without the port number). It's more useful because it's easier to allocate and remember a unique name than a unique port number.
maybe you're running a reverse proxy? it can direct you differently depending on how you refer to it
The comparison is `myapp.localhost` vs `localhost:3000`. This is especially useful when you have web servers permanently running on your computer on ports, not just for momentaneous local development.
Maybe you have a stack of applications that needs to communicate. Seeing db.localhost is a little easier to read that db:3360, but especially if you have multiple web applications. It's easier to read sso.localhost, api.localhost, and www.localhost.

They also show having the webserver to the TLS, that might be helpful.

(comment deleted)
Note the use of the Caddy webserver (you could also use nginx or whatever), which proxies to the port, so it's just myapp.localhost. I like this because it mirrors our production site. We can have subdomain.myapp.localhost and subdomain.myapp.com so links and everything work properly in both environments (assuming you have an env variable for the base domain)
Could there be a way to setup the Caddy server dynamically using eg direnv so that it's only launched when I'm in my dev folder?
(comment deleted)
I pondered the question of local-only domains since long ago, and in consultation with Chris Siebenmann, I determined that the most courteous way was actually to subdomain from my ISP.

That’s right: I invented a fictitious subdomain under one my ISP controlled and I never registered it or deployed public DNS for it. It worked great, for my dumb local purposes.

Example:

  aten.mysub.isp.net.
  porta.mysub.isp.net.
  smartphone.mysub.isp.net.
Thus it was easy to remember, easy to add new entries, and was guaranteed to stay out of the way from any future deployments, as long as my ISP never chose to also use the unique subdomain tag I invented...
We have a separate domain registered, where you can add any wildcard subdomain, so webui.company-test.com and that will resolve to 127.0.0.1. Then we can do pretty much the same.

I'm not entirely sure how I feel about it, but at least it's on a completely separate domain.

We add a real actual DNS record for the local. subdomain pointing to 127.0.0.1

It works really well and means no setup on our developers machines

Can you expand on this? you redirect *.local.example.com to 127.0.0.1, and then how do you setup the local machine so that eg myservice.local.example.com hits the correct port? I guess you still need a proxy somewhere? or you specify eg myservice.local.example.com:3000 ?
It's not a redirect. It's an actual A record on the domain for local.example.com -> 127.0.0.1

Then we just have an entry for local.example.com in our vhosts and bam everything works as expected. No need to mess with /etc/hosts

Any subdomain of .localhost works out-of-the-box on Linux, OpenBSD and plenty of other platforms.

Of note, it doesn't work on macOS. I recall having delivered a coding assignment for a job interview long ago, and the reviewer said it didn't work for them, although the code all seemed correct to them.

It turned out on macOS, you need to explicitly add any subdomains of .localhost to /etc/hosts.

I'm still surprised by this; I always thought that localhost was a highly standard thing covered in the RFC long long ago… apparently it isn't, and macOS still doesn't handle this TLD.

Just did that on my mac and it seems to work?

    $ ping hello.localhost
    PING hello.localhost (127.0.0.1): 56 data bytes
    64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.057 ms
    64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.162 ms
Not for me, MacOS 15.4:

    $ ping hello.localhost
    ping: cannot resolve hello.localhost: Unknown host
I think I had mixed results on mac depending on the network I was connected to. I think it has something to do with ipv4 vs ipv6
Ironically for me it works on my MacOS laptop but not on my Debian 12 VPS
That's because your DNS server sends back 127.0.0.1. The query isn't resolved locally.
I am doing this on macOS with no problem.
> Any subdomain of .localhost works out-of-the-box on Linux

No, not here.

This usually happens because you have a Linux setup that doesn't use systemd-resolved and it also doesn't have myhostname early enough in the list of name resolvers. Not sure how many Linux systems default to this, but if you want this behavior, adjust your NSS configuration, most likely.
You don't need systemd-resolved for .localhost domains to work. It works on systemd-based distros using another resolver as well as non-systemd distros.

I've never seen this not-work on any distro, must be a niche thing.

It's easy to be tricked into thinking macOS supports it, because both Chrome and Curl support it. However, ping does not, nor do more basic tools like Python's request library (and I presume urllib as well).
As a reminder, lacolhost.com and all subdomains will forever resolve to localhost (well for as long as I'm around at least)
> As a reminder, lacolhost.com …

I’m assuming that typo is intentional?

> (well for as long as I'm around at least)

Rather big caveat IMO. As a side note, your domain doesn't seem to have an AAAA record (which [.]localhost binds to by default on most of my machines, at least).

wow this is hitting hacker news front page ? if we can change the hosts file - I want to propose you can have any domain name for local services.