Hacker News new | past | comments | ask | show | jobs | submit login
How to make “localhost” slightly less local (brycewray.com)
48 points by brycewray on April 2, 2022 | hide | past | favorite | 44 comments



Your computer has multiple network interfaces, and it has routing tables that tell it which interface to use for each IP it tries to talk to. E.g. your home router basically has one interface talking to the outside world and another talking to your local network, and its whole job is patching together connections through each of those interfaces.

Your personal computer will have ethernet and/or wifi, and those are entirely separate network interfaces, but it will also have a loopback interface (which is a dummy software-only interface that just connects back to your computer). The "localhost" hostname points at 127.0.0.1, and that IP is bound to the loopback interface, so all connections through localhost won't even hit the network, they'll just be looped back in (hence the name).

What all those -H/--host/--bind options do is tell your service which of those network interfaces to bind to. Instead of specifying an IP (and, therefore, an interface) to bind on, you can use 0.0.0.0 as a catch-all "meta-IP" that will tell your service to listen on all interfaces.

My personal preference (though it does require a bit more setup ahead of time) is to keep all these (HTTP) services listening on localhost, and then configure one single NGINX instance that binds on the external interface and proxies each individual service.


"The "localhost" hostname points at 127.0.0.1, but it will also have a loopback interface (which is a dummy software-only interface that just connects back to your computer)."

AFAIK, 127.0.0.1 localhost as a HOSTS file entry is merely a convention. For example, the name "localhost" can point to any address in the 127.x.x.x address space or any address in general. Similarly, a loopback interface can be assigned any 127.x.x.x address. The convention is to assign 127.0.0.1.

One of the nice things about NetBSD (IMO) is that when compiling one's own system, a loopback is not necessarily automatically created. It may be left up to the user to create one and assign it with an address. This can teach the user what software depends on the presence of a loopback interface that is assigned "127.0.0.1".


Yeah, and you can also have multiple loopback interfaces in the 127.0.0.0/8 range if you want, no reason why you can only have one. That said, enough stuff out there assumes localhost = 127.0.0.1 = loopback interface that I wouldn't want to make my life complicated by breaking the convention.

Also, that comment was already a bit muc, and I opted to simplify a bit.


I lost a lot of the blind faith I had in people who make http servers when I realized some network configurations just can't be expressed.

Like next.js's "Production Server"[1] uses Node's builtin server, which doesn't natively let you listen on more than one IP. And because next.js hardcodes 0.0.0.0, it doesn't also listen on the IPv6 equivalent if available, which breaks some clients when using "localhost" as the hostname (BusyBox wget as a notable example I found).

[1]: https://nextjs.org/docs/api-reference/cli#production


But then Busybox wget is simply broken, sorry. It’s still entirely normal for a hostname to resolve to both IPv4 and IPv6 addresses, yet have some services not listening on both. Good TCP clients implement a fallback mechanism that could be so fast the user doesn’t even notice.


I've encountered a similar failure, when some old process still claimed [::]:80 when I restarted the web server, probably a lingering socket or something like that, which made the new process only bind to 0.0.0.0:80. It took me a while to find out why my changes weren't being reflected when I could see the binding _right there_ in `ss -tlpn`!

Honestly, I don't think it's too unreasonable to argue that if a client has IPv6 connectivity and a hostname has a valid AAAA record, the connection should be established over IPv6. Browsers tend to have fallbacks to IPv4, but outside browsers you'll be surprised how little actual system software cares about DNS fallback.

Reading the BusyBox source code (https://elixir.bootlin.com/busybox/latest/source/libbb/xconn...) it seems like it will pass AF_UNSPEC to str2sockaddr, which is packed into `addrinfo hint` and then passed on to getaddrinfo(). Only when IPv6 support is disabled or ENABLE_FEATURE_PREFER_IPV4_ADDRESS is enabled will busybox shuffle around the DNS results to return an IPv4 address.

If your service isn't available on IPv6 yet your hostname has a valid IPv6 record, it's your task to indicate this to the software (using the -4 flag, for example) or to configure it to retry.


If BusyBox did implement that kind of fallback, they'd need to set a default timeout. So the whole thing would still be broken because every other request would be delayed by the timeout.


That fallback is a workaround. Requiring it is simply broken.


I hope you’ll find this helpful whenever you’re web-dev’ing and just can’t remember that string of commands/flags/etc. for seeing “localhost” from your other devices.

EDIT: post is now available also on dev.to[0].

[0]: https://dev.to/brycewray/let-all-your-devices-see-your-site-...


Not sure how mDNS service (Bonjour I think) is setup on macOS, but you should be able to reach it by some domain name as well (hostname.local format), once you expose your service on the public IP address.

On Linux I have my VMs run avahi (the Linux mDNS service), and I access my VMs using vmname.local instead of using hardcoded IP addresses.


Android doesn't support mDNS at the OS level, so it is possible to bake it into an app, but i.e. chrome won't connect. It is a long standing issue that I don't think Google cares to prioritize:

https://issuetracker.google.com/issues/140786115?pli=1

I would have sworn I saw that the latest pixel / android versions could, but now I'm not finding one way or the other. Given the issue above is still open, I am assuming not.


I’ve always had issues using mDNS on a mixed os network. I assume both bonjour and avahi and whatever windows uses comply with some standard. But in (my) reality, it doesn’t work.


I just use ngrok.com. Makes specific localhost ports accessible regardless of network.


Localtunnel[1] is also great, you don't even have to sign up.

[1] https://theboroer.github.io/localtunnel-www/


I prefer Cloudflare Tunnels.

Just install cloudflared.


I love ngrok outside of two pain points - (1) the urls are kind of difficult to type into a phone, and (2) the latency can be pretty terrible at times.


I have switched over to using Tailscale. But that only works if the devices are mine before the friction of someone else’s device being set up happens


The basic paid plan is pretty cheap and lets you use your_subdomain_name.ngrok.com as the address.


In my opinion, (2) is a feature, not a bug. You never know what kind of connection your users have. Ngrok forces you to consider the "bad connection" factor early on in development, which can avoid many pitfalls that occur when network software assumes that latency is zero and bandwidth is unlimited.


It’s not a feature imo when there are other ways to force a “bad connection”. However with ngrok if latency is bad. You can’t ever change fhat.


Yeah I don't fault them at all - what they're doing is going to be inherently difficult from a latency POV. I'm really comparing it against just opening a port to your local network.


That's a fair point.

I suppose latency is important if you use ngrok for something other than development. I use ngrok exclusively for development, so that's where my comment comes from.


Back in the day, you used to be able to connect to services bound to 127.1 on host A from host B by deleting 127.1 on host B, then adding a manual route to host A's public IP. Host B stack sees packet for 127.1, says not me, routes it to host A, host A gets packet, looks at its IPs, sees 127.1, says oh this must be for me. Most systems are probably better about configuring host A to not let this happen now.


...and then you'll find out that all kinds of fancy JS features don't work any more because of something called a "secure origin"! Soon after you end up in a rabbit hole of acronyms like CORS, XHR, CSP, TLS, HSTS, (m)DNS, WSS and HTTPS, and soon enough you'll be copy/pasting a hundred of lines of configuration just to make your website work like it did on localhost.

I enjoy the ease of use that the special privileges localhost was given in browsers, but I'm 100% sure it's a pitfall for starting developers. It's still confusing to me that someone capable of developing a website needs an explainer on how basic computer networking works, but I guess other people learn about computers top-down rather than bottom-up. I have no idea what all of these servers are (most of them seem Javascript based?) but I'd expect someone knowing how to deal with a piece of complex server software like that to at least know where to find their machine's IP address!


Reminder that you should have your authention setup in whatever you are developing before you bind to 0.0.0.0 or a routable address. Even if you trust all the people and devices on your LAN. There are ways to port scan from within a webpage so it’s possible that someone on your LAN visits a malicious page and exposes your service to the internet as well. 127.* is treated as a different origin so doesnt have this problem. A decade a go many dev tools would bind to routable interfaces by default and this was frequently exploited. Loopback is a much safer default.


Is it possible to access the local site from another network? Like run a local server, that I can access from my phone outside of the house? I looked into this before, but I could never find a simple solution.


With IPv6: yes, if you open the port in your firewall.

With IPv4 you can do it with a port forward, but the IP listed isn't the same as the IP you'll actually receive the connection on. You can get your external IP relatively easily with `curl ifconfig.co`.

If firewalls or port forwards aren't available to you, pick any of the popular port forwarding methods (here's a list: https://github.com/anderspitman/awesome-tunneling). I'm a big fan of just SSH-ing into an internet connected machine myself (`ssh -R 0.0.0.0:3000:127.0.0.1:3000 server.on.the.inter.net` will expose localhost:3000 on server.on.the.inter.net:3000 and encrypt the traffic between you and the server as an added bonus!) but there are plenty of paid and sketchy alternatives if you don't have a server to SSH into.


> If firewalls or port forwards aren't available to you

I don't know if they are or not. I am just a regular person with a home computer. I know how to set up a run a basic localhost server, but I don't really know about the other stuff you're talking about.


You can follow any Minecraft server guide to make your web server accessible to other users; enter 80 where the guides enter 25565 and it should work relatively flawlessly.

If you're thinking of diving deeper into development, I'd recommend you take the time to learn about computer networking. It's not a necessity if your end goal is to stuff HTML/CSS/JS files into a server somewhere, but it'd make your life a lot easier down the line.


I asked for something simple, this is the opposite of that:

https://minecraft.fandom.com/wiki/Tutorials/Setting_up_a_ser...


I meant guides like these:

- https://www.wikihow.com/Portforward-Minecraft

- https://www.technewstoday.com/port-forward-minecraft/

- https://www.youtube.com/watch?v=X75GbRaGzu8

Most of the guide you linked is related to setting up Java and configuring Minecraft itself, you only need the port forwarding bit.


Tailscale as others have said. ZeroTier if not Tailscale. Both are top notch


Look into Tailscale. Ridiculously easy to set up and free for your use case.


+1 on Tailscale. It took me time to accept using a closed source free product, but it's just such a nice product -- they focussed on the essentials and it works really well.


It seems like they have gone past essentials now. Like being able to use any connected device as an endpoint? I haven’t used Tailscale yet past as a mesh network but regardless. It is so well done.


Use something like ngrok (or manual ssh tunnels), just be sure to have some authentication in place.


> On Linux, you have multiple choices (wouldn’t you know?), but the easiest is to enter this in your terminal app:

> hostname -I

    $ hostname -I
    hostname: invalid option -- 'I'
    Usage: hostname [-v] {hostname|-F file}      set hostname (from file)
       domainname [-v] {nisdomain|-F file}   set NIS domainname (from file)
       hostname [-v] [-d|-f|-s|-a|-i|-y|-n]  display formatted name
       hostname [-v]                         display hostname


Debian's hostname has -I. Turns out that's the hostname I have here on openSUSE.

https://tracker.debian.org/pkg/hostname


there are at least four common implementations of "hostname" on Linux: debian, coreutils, net-tools, busybox, and arguably toybox. there are also at least four implementations of su (util-linux, shadow, busybox, toybox) and five implementations of kill (coreutils, procps(-ng), util-linux, busybox, toybox).


Don't forget that the shell usually has a built-in kill as well.


Yep, net-tools doesn't have the "-I" option


Interesting. I used an alternate source[0] to check this and it says the same thing I had in the article.

[0]: https://www.tecmint.com/hostname-command-examples-for-linux/


You can follow these steps and learn about network interfaces and IP addresses or you can use my app (https://expose.sh) and get a public URL for your service in a single command.

Note: This won't only make your localhost service slightly less local, but also publicly accessible.

Example: `expose 80`


Or you can just bridge the connection and use mDNS?




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: