Key idea
On your laptop, the browser and the app are on the same machine, so localhost works. Online, requests come from outside the machine. Your app has to listen on 0.0.0.0, not 127.0.0.1, and on the port the platform sends traffic to.
The symptom
It works on your laptop. You deploy it, and the log looks fine:
* Running on http://127.0.0.1:5000
But the URL answers with an error, often 502 Bad Gateway, or the platform says the app never became ready.
Why
localhost means the loopback address, 127.0.0.1 (or ::1 in IPv6): a network that never leaves the machine. The platform's traffic arrives on the machine's real network address, where nothing is listening. In a listen call, 0.0.0.0 means "every address this machine has", so outside traffic gets in. That's the right half of the diagram in the last lesson.
The port has to match too. Many platforms tell your app which port to use in a PORT environment variable, so read it instead of hard-coding one.
The fix, per framework
| Framework | Listens on by default | To run it online |
|---|---|---|
Express, Node listen(port) | All addresses, on the port you pass | app.listen(process.env.PORT ?? 3000) |
Next.js (next dev, next start) | All addresses, port 3000 or PORT | Nothing to change |
Flask (flask run) | 127.0.0.1:5000 | flask run --host 0.0.0.0 --port $PORT |
Django (runserver) | 127.0.0.1:8000 | python manage.py runserver 0.0.0.0:$PORT |
| FastAPI with Uvicorn | 127.0.0.1:8000 | uvicorn main:app --host 0.0.0.0 --port $PORT |
| Vite dev server | localhost:5173 | Don't deploy the dev server: vite build makes static files to serve |
Tools default to localhost on purpose. On your laptop, 0.0.0.0 opens your dev server to everyone on the same Wi-Fi, so set it in the deploy command rather than everywhere.
Flask's and Django's built-in servers
Both frameworks say their built-in servers are for development. In production, run the app under a production server, and give that server the same two things: 0.0.0.0 and the port.
The other localhost: in your frontend code
fetch("http://localhost:8000/api/items") works on your laptop. In a visitor's browser, localhost is the visitor's own computer, where your API isn't running, so the request fails.
Use a path on the same site, like /api/items, or the API's public URL from a variable set at build time.
Check yourself