Hosting·7 min read·By the OmegaCloud team

Why is your localhost app not visible from the internet?

Developer's laptop showing a localhost app in the browser next to a router and a server rack

Introduction

Localhost is your own computer: 127.0.0.1 in IPv4, ::1 in IPv6. An application listening there only accepts connections from that machine. As far as the internet is concerned, it is also hidden behind your router (NAT) and a firewall. To open it to the world, you need to host it on a machine with a public IP address, such as a VPS.

This is normal behaviour, not a fault. Let's follow a request on its way in and see where it stops.

In brief

  • Localhost is a loopback address: packets never leave your machine.
  • Three barriers block outside access: the listening interface, your router's NAT and the firewall.
  • --host 0.0.0.0 opens the app to your local network, not to the internet.
  • For a lasting service, the clean solution is a VPS with a public IP, an application server, systemd, Nginx and HTTPS.

What is localhost?

Localhost is a reserved hostname that points to the loopback interface. RFC 6761 reserves the name and says it should always resolve to a loopback address. The whole 127.0.0.0/8 range is set aside for this purpose, and ::1 is the IPv6 equivalent.

The hosts file maps names to addresses before any DNS lookup happens: /etc/hosts on Linux and macOS, C:\Windows\System32\drivers\etc\hosts on Windows. You can declare several local names to test several projects, for example app.localhost and api.localhost. Recent browsers generally resolve .localhost subdomains to the loopback address without any configuration, but check on your own setup before relying on it.

The port is what tells services apart on a single machine: localhost:5000 (Flask), :8000 (Uvicorn), :3000 or :8080 depending on your tools.

Localhost, 127.0.0.1, 0.0.0.0 and a public IP: what is the difference?

Address Meaning Who can connect Typical use
localhost Hostname, resolved through the hosts file or DNS This machine only Testing in the browser
127.0.0.1 / ::1 IPv4 / IPv6 loopback addresses This machine only Development, local databases
0.0.0.0 Listening address meaning "all interfaces". Not something you type into a browser Depending on the firewall, any device that can reach the machine The --host option of a server
192.168.x.x Local (private) IP Devices on the same network Testing from a phone on Wi-Fi
Public IP Address visible on the internet The whole internet, if a port is open Production

One trap comes up again and again: localhost sometimes resolves to ::1 while your app only listens on IPv4. Depending on the client, the first attempt fails. If localhost falls over but 127.0.0.1 answers, that is almost always the cause.

Why a localhost application is not reachable from the internet

Take a request coming from outside. It first lands on your router's public IP, then has to find your PC, then has to get into your application. It can be turned away at each of those steps.

Barrier 1: the application only listens on the loopback interface

By default, Flask's development server listens on 127.0.0.1, port 5000, and Uvicorn on 127.0.0.1, port 8000. A request arriving through the network card is therefore rejected. To check the listening address:

# Linux
ss -tlnp | grep 8000
# Windows (PowerShell)
netstat -ano | findstr :8000

If the column shows 127.0.0.1:8000, only your machine can connect. With 0.0.0.0:8000, the app listens on every interface.

Barrier 2: the router and NAT

Your router shares a single public IP between all your devices. Without a port forwarding rule, it has no idea which device an incoming request is meant for, so it drops it.

Some broadband providers also put you behind CGNAT: the public IP is shared between several customers, and port forwarding becomes impossible on your side. IPv6 partly changes the picture, since each device can have its own address, but the router's firewall still needs configuring.

Barrier 3: the firewall

Windows Firewall, UFW on Linux and your router's firewall all block incoming connections by default. Each port has to be allowed explicitly, one by one.

Suppose you remove all three barriers at home. Your PC must now stay switched on around the clock, and your home IP changes from time to time. For a lasting service, that does not hold up. A high-performance Linux VPS solves the problem: a machine that is always online, with a fixed IP.

How to make a Flask or FastAPI application reachable from outside

There are three levels, from the simplest to the sturdiest.

1. Local network. Make the app listen on all interfaces:

flask run --host=0.0.0.0
uvicorn main:app --host 0.0.0.0 --port 8000

From a phone on the same Wi-Fi, open http://192.168.x.x:8000 for Uvicorn or http://192.168.x.x:5000 for Flask (replace 192.168.x.x with your PC's local IP). Allow the port through the firewall if needed.

2. One-off demo. A temporary tunnel or a port forwarding rule on your router is enough to show a prototype. The risks are real: your PC is exposed, there is no HTTPS, and debug=True is dangerous. The Werkzeug documentation states that the debugger allows arbitrary code execution and is a major security risk. The PIN shown in the terminal is a safeguard, not a guarantee. Turn debug mode off as soon as the machine is visible from outside.

FLASK_DEBUG=0 flask run --host=0.0.0.0

3. Production. Flask is explicit about it: its development server is only meant for local development, and is not designed to be efficient, stable or secure. You need a dedicated machine with a public IP.

Deploying a Python application to a VPS in production

Here is the checklist, step by step. It assumes a VPS running Ubuntu or Debian, with an app in /opt/myapp.

  1. Order a Linux VPS and connect to it over SSH. The Python VPS for bots, APIs and scripts online 24/7 comes with a dedicated IPv4+IPv6 address and root access. If you work from Windows, our guide to managing a VPS from a Windows PC gets you started with SSH. Then run ssh root@VPS_IP.
  2. Install Python and create a venv.
    apt update && apt install -y python3-venv nginx
    cd /opt/myapp && python3 -m venv venv
    venv/bin/pip install -r requirements.txt gunicorn
    
  3. Run the app with an application server, on 127.0.0.1:8000. Gunicorn for Flask (WSGI), Uvicorn for FastAPI (ASGI). The paradox deserves a pause: in production you listen on localhost again, and it is Nginx that exposes the app.
    venv/bin/gunicorn -w 3 -b 127.0.0.1:8000 app:app
    venv/bin/uvicorn main:app --host 127.0.0.1 --port 8000 --workers 3
    
  4. Create a systemd service in /etc/systemd/system/myapp.service, so the app restarts by itself and starts at boot:
    [Unit]
    Description=My Python application
    After=network.target
    
    [Service]
    User=www-data
    WorkingDirectory=/opt/myapp
    ExecStart=/opt/myapp/venv/bin/gunicorn -w 3 -b 127.0.0.1:8000 app:app
    Restart=always
    
    [Install]
    WantedBy=multi-user.target
    
    Then run systemctl enable --now myapp.
  5. Configure Nginx as a reverse proxy (ports 80 and 443) in /etc/nginx/sites-available/myapp:
    server {
        listen 80;
        server_name example.co.uk;
        location / {
            proxy_pass http://127.0.0.1:8000;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    }
    
  6. Add a DNS record: an A record pointing to the VPS's IPv4 address, an AAAA record pointing to its IPv6 address.
  7. Enable HTTPS with Let's Encrypt: apt install -y certbot python3-certbot-nginx && certbot --nginx -d example.co.uk. Certificates are currently valid for 90 days. Let's Encrypt has announced a reduction to 45 days by 2028: run certbot renew --dry-run to check that automatic renewal works.
  8. Close the firewall except for ports 22, 80 and 443. Allow SSH before enabling UFW, otherwise you will lock yourself out:
    ufw allow OpenSSH && ufw allow 80/tcp && ufw allow 443/tcp && ufw enable
    
    To harden the machine further, read how to secure a Linux VPS with UFW, fail2ban and SSH hardening.

The logic is the same with other languages. For a JavaScript API, the same approach applies when you deploy a Node.js application on a VPS.

Frequently asked questions

Why can't I access localhost?

Most of the time, the application is not running, or it is listening on a different port or a different address family (IPv4 versus IPv6). Check with ss -tlnp or netstat -ano, then try 127.0.0.1 instead of localhost.

What does "localhost" mean?

It is the reserved name for your own machine. It points to the loopback interface, 127.0.0.1 in IPv4 and ::1 in IPv6, and the traffic never goes out onto the network.

What is the difference between localhost and 127.0.0.1?

127.0.0.1 is an IPv4 address. Localhost is a name, which can resolve to 127.0.0.1 or to ::1 depending on the system. If the app only listens on IPv4, one can work while the other fails.

Is it dangerous to run Flask with host 0.0.0.0?

On a trusted home network, the risk is limited. On public Wi-Fi, or with debug=True, anyone on the network can reach your app and even run code through the debugger. As soon as the app leaves your machine, run it without debug.

How do I give someone else access to my local application?

On the same network, use --host 0.0.0.0 and share your local IP. For someone elsewhere, a temporary tunnel is fine for a short demo. Beyond that, deploy to a server with a public IP.

Do I need a domain name to put a Python API online?

No, the API answers on the server's public IP. You do need a domain to get a Let's Encrypt HTTPS certificate, and it is also needed for webhooks that require a secure URL.

Moving from localhost to a public IP

Run through the eight steps above on a fresh server with a minimal app of a few lines. Once the request gets from Nginx to Gunicorn, the rest is just configuration.

OmegaCloud's Python VPS for bots, APIs and scripts online 24/7 covers this need: a dedicated IPv4+IPv6 address, a machine that stays on without your PC, root access for Gunicorn, Uvicorn, systemd and Nginx, 1 Tbit/s Anti-DDoS protection included, and GDPR-compliant hosting in France. The plans are currently available for pre-order, with availability announced for 1 November 2026.

Related articles

Hosting

Navigation