Learning paths / Cloud foundations / IP addresses and ports

Why localhost doesn't work online

Reading · 5 min · Module 4, lesson 3 of 410 min left in this module

Module 4 · IP addresses and portsLesson 3 of 4

Goal: Make an app that works on localhost reachable once deployed, by listening on 0.0.0.0 and the port the platform expects.

3:54 · captions and chapters · narrated with an AI-generated voice
Transcript

Narration uses an AI-generated voice.

[00:00] Where we're going

By the end of this video, you'll know why an app that works on your laptop can stop answering once it's online. And you'll know the two things to change, so it answers.

[00:11] The symptom

It works on your laptop. You deploy it, and the log looks fine. It says the app is running, on one twenty-seven dot zero dot zero dot one, port five thousand. But the public URL answers with an error. Often a five oh two, bad gateway. Or the platform says the app never became ready.

[00:34] Why

So what's going on? On your laptop, the browser and the app are on the same machine. So localhost works. Localhost means the loopback address. That's one twenty-seven dot zero dot zero dot one. It's a network that never leaves the machine.

Online, the requests come from outside the machine. The platform's traffic arrives on the machine's real network address. But your app is only listening on the loopback. Nothing is listening where the traffic arrives. So the request is turned away. The app is running. It's just listening in the wrong place.

[01:14] The fix

The fix is one change to where your app listens. Listen on zero dot zero dot zero dot zero. In a listen call, that means every address this machine has. So the loopback still works, and outside traffic gets in.

The port has to match too. The platform sends traffic to one port. If your app listens on another, nothing answers there either. Many platforms tell your app which port to use, in an environment variable called PORT. So read it, instead of hard-coding one.

[01:49] Per framework

Some frameworks already listen on every address. Express and Next.js do. Others start on localhost. Flask, Django, and Uvicorn for FastAPI. For those, pass the host and the port in the command that starts your app online. They default to localhost on purpose. On your laptop, every address would open your dev server to everyone on the same Wi-Fi. So set it in the deploy command, not everywhere.

[02:20] The other localhost

There's one more localhost to watch for. It's in your frontend code. Say your page fetches its data from localhost, port eight thousand. On your laptop, that works. But in a visitor's browser, localhost is the visitor's own computer. Your API isn't running there, so the request fails. Use a path on the same site instead. Or the API's public URL, set when the app is built.

[02:48] On ComputeSphere

On ComputeSphere, a web service's Port is where traffic is sent. It's eighty eighty if you leave it out. Visitors use HTTPS on four forty-three, and never see it. If your app listens on another port, or only on localhost, it never answers. The deploy never reaches Running, and it ends Failed. The runtime log shows the address your app printed. So check that line first.

[03:16] Recap

So, a quick recap. Localhost only answers the machine it's on. To be reachable online, listen on every address, zero dot zero dot zero dot zero, and on the port the platform sends traffic to. And in frontend code, don't call localhost. Use a path on your own site.

Now try it with an app of your own. Start it, and find the line in its log that says where it's listening. Next, you'll check addresses and ports for yourself. I'll see you in the next lesson.

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

FrameworkListens on by defaultTo run it online
Express, Node listen(port)All addresses, on the port you passapp.listen(process.env.PORT ?? 3000)
Next.js (next dev, next start)All addresses, port 3000 or PORTNothing to change
Flask (flask run)127.0.0.1:5000flask run --host 0.0.0.0 --port $PORT
Django (runserver)127.0.0.1:8000python manage.py runserver 0.0.0.0:$PORT
FastAPI with Uvicorn127.0.0.1:8000uvicorn main:app --host 0.0.0.0 --port $PORT
Vite dev serverlocalhost:5173Don'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

Your Flask app's log says 'Running on http://127.0.0.1:5000', but its public URL returns 502. What do you change?
Your deployed site loads, but every call to fetch('http://localhost:8000/api') fails for visitors. Why?

In the docs