127.0.0.1 is the IPv4 loopback address: it always means "this computer". Packets sent to it never reach a network card; the operating system hands them straight back to a program on the same machine. localhost is the name for it, so http://localhost:8080 and http://127.0.0.1:8080 normally reach the same server. Every device has its own 127.0.0.1, which is why your phone can't open your laptop's.
127.0.0.1, localhost, ::1 and 0.0.0.0
| Address | What it is | Use it to |
|---|---|---|
127.0.0.1 | IPv4 loopback | Reach a server on this machine |
127.0.0.0/8 | Whole loopback block (127.0.0.1 to 127.255.255.254) | Run several local services on separate IPs |
::1 | IPv6 loopback | Same as 127.0.0.1, over IPv6 |
localhost | Hostname for the loopback | Usually resolves to both 127.0.0.1 and ::1 |
0.0.0.0 | "All interfaces" when a server binds | Make a server reachable from other devices |
192.168.x.x, 10.x.x.x | Your LAN address | Reach this machine from another device on the network |
localhost is defined in your hosts file (/etc/hosts on macOS and Linux, C:\Windows\System32\drivers\etc\hosts on Windows), and modern browsers and resolvers always treat it as loopback.
Why "127.0.0.1 refused to connect"
ERR_CONNECTION_REFUSED on 127.0.0.1 means your machine answered, but no program is listening on that port.
- Include the port.
http://127.0.0.1means port 80. Dev servers use 3000, 5173, 8000, 8080 and so on. - Start the server and read the URL it prints.
- IPv4 vs IPv6. If
localhostworks but127.0.0.1doesn't (or the reverse), the server is bound to only::1or only127.0.0.1. Bind it explicitly to the one you use, or to0.0.0.0. - Check what's listening:
# macOS / Linux
lsof -iTCP -sTCP:LISTEN -n -P
# Windows
netstat -ano | findstr LISTENING
Free a port with kill -9 $(lsof -ti :PORT) on macOS and Linux, or taskkill /PID <pid> /F on Windows.
Docker, VMs and emulators
127.0.0.1 is local to each network namespace, which trips people up:
- Inside a Docker container,
127.0.0.1is the container, not your computer. To reach a service on the host, usehost.docker.internal(Docker Desktop, or--add-host=host.docker.internal:host-gatewayon Linux). - A server inside a container must bind
0.0.0.0, not127.0.0.1, or published ports (-p 8080:8080) won't reach it. - The Android emulator reaches your computer at
10.0.2.2; its own127.0.0.1is the emulated phone. - WSL2 forwards
localhostto Windows in most setups. If not, use the WSL VM's IP.
Binding to 127.0.0.1 vs 0.0.0.0
Binding a server to 127.0.0.1 is the safe default: only programs on the same machine can connect. Django, Flask, Vite, Jupyter, Ollama and PostgreSQL all start this way. Bind to 0.0.0.0 only when other devices need to connect, and only on a network you trust. Then open http://YOUR-LAN-IP:PORT from the other device, never 127.0.0.1.
127.0.0.1 and webhooks
A webhook provider can't deliver to http://127.0.0.1:3000/webhooks. To Stripe or GitHub, 127.0.0.1 is their own server, and most providers reject loopback and private addresses outright. Your local handler needs a public URL that forwards to it:
relay forward --bucket my-app http://127.0.0.1:3000/webhooks
The relay agent connects outbound, so there's no router or firewall change, and the bucket's public URL stays the same between sessions. To share a whole local site rather than just receive webhooks, relay connect --name my-app http://127.0.0.1:3000 gives the port a public HTTPS URL. See receive webhooks on localhost.
