Pourquoi une app en localhost n'est pas visible depuis Internet ?

Introduction
Localhost désigne votre propre ordinateur : 127.0.0.1 en IPv4, ::1 en IPv6. Une application qui écoute dessus n'accepte que les connexions venant de cette machine. Pour Internet, elle est en plus masquée par la box (NAT) et par un pare-feu. Pour l'ouvrir au monde, il faut l'héberger sur une machine dotée d'une IP publique, comme un VPS.
C'est un comportement normal, pas une panne. Suivons le trajet d'une requête pour voir où elle s'arrête.
En bref
- Localhost est une adresse de bouclage : les paquets ne quittent jamais votre machine.
- Trois barrières bloquent l'accès externe : l'interface d'écoute, le NAT de la box et le pare-feu.
--host 0.0.0.0ouvre l'app au réseau local, pas à Internet.- Pour un service durable, la solution propre est un VPS avec IP publique, un serveur d'application, systemd, Nginx et HTTPS.
Qu'est-ce que localhost ?
Localhost est un nom d'hôte réservé qui pointe vers l'interface de bouclage (loopback). La RFC 6761 le réserve et prévoit qu'il renvoie toujours vers l'adresse de bouclage. Toute la plage 127.0.0.0/8 est destinée à cet usage, et ::1 en est l'équivalent IPv6.
Le fichier hosts fait la correspondance nom → adresse avant tout appel DNS : /etc/hosts sous Linux et macOS, C:\Windows\System32\drivers\etc\hosts sous Windows. Vous pouvez y déclarer plusieurs localhosts pour tester plusieurs projets, par exemple app.localhost et api.localhost. Les navigateurs récents résolvent généralement les sous-domaines en .localhost vers le loopback sans configuration, mais vérifiez chez vous avant de vous y fier.
Le port, lui, distingue les services d'une même machine : localhost:5000 (Flask), :8000 (Uvicorn), :3000 ou :8080 selon vos outils.
Localhost, 127.0.0.1, 0.0.0.0 et IP publique : quelle différence ?
| Adresse | Signification | Qui peut se connecter | Usage typique |
|---|---|---|---|
localhost |
Nom d'hôte, résolu via le fichier hosts ou le DNS | Cette machine uniquement | Tester dans le navigateur |
127.0.0.1 / ::1 |
Adresses de bouclage IPv4 / IPv6 | Cette machine uniquement | Développement, bases de données locales |
0.0.0.0 |
Adresse d'écoute : « toutes les interfaces ». Ne se tape pas dans un navigateur | Selon le pare-feu, tout appareil qui atteint la machine | Option --host d'un serveur |
192.168.x.x |
IP locale (privée) | Appareils du même réseau | Tester depuis un téléphone en Wi-Fi |
| IP publique | Adresse visible sur Internet | Tout Internet, si un port est ouvert | Production |
Un piège revient souvent : localhost se résout parfois en ::1 alors que votre app n'écoute qu'en IPv4. Selon le client, la première tentative échoue. Si localhost plante mais que 127.0.0.1 répond, la cause est presque toujours là.
Pourquoi une application en localhost n'est pas accessible depuis Internet
Prenons une requête venue de l'extérieur. Elle arrive d'abord sur l'IP publique de votre box, doit ensuite trouver votre PC, puis entrer dans votre application. À chacune de ces étapes, elle peut être refusée.
Barrière 1 : l'application n'écoute que sur la boucle locale
Par défaut, le serveur de développement de Flask écoute sur 127.0.0.1, port 5000, et Uvicorn sur 127.0.0.1, port 8000. Une requête qui arrive par la carte réseau est donc rejetée. Pour vérifier l'adresse d'écoute :
# Linux
ss -tlnp | grep 8000
# Windows (PowerShell)
netstat -ano | findstr :8000
Si la colonne affiche 127.0.0.1:8000, seule votre machine peut se connecter. Avec 0.0.0.0:8000, l'app écoute sur toutes les interfaces.
Barrière 2 : la box et le NAT
Votre box partage une seule IP publique entre tous vos appareils. Sans redirection de port, elle ignore à quel appareil destiner une requête entrante, et la jette.
Certains fournisseurs d'accès vous placent en plus derrière un CGNAT : l'IP publique est partagée entre plusieurs abonnés et la redirection de port devient impossible de votre côté. L'IPv6 change en partie la donne, puisque chaque appareil peut avoir sa propre adresse, mais le pare-feu de la box reste à configurer.
Barrière 3 : le pare-feu
Le pare-feu de Windows, UFW sous Linux et celui de la box bloquent par défaut les connexions entrantes. Chaque port doit être autorisé explicitement, un par un.
Admettons que vous leviez ces trois barrières chez vous. Votre PC doit alors rester allumé en permanence, et l'IP domestique change régulièrement. Pour un service durable, cela ne tient pas. Un VPS Linux haute performance règle le problème : machine toujours en ligne, IP fixe.
Comment rendre une application Flask ou FastAPI accessible depuis l'extérieur
Il existe trois niveaux, du plus simple au plus solide.
1. Réseau local. Faites écouter l'app sur toutes les interfaces :
flask run --host=0.0.0.0
uvicorn main:app --host 0.0.0.0 --port 8000
Depuis un téléphone sur le même Wi-Fi, ouvrez http://192.168.x.x:8000 (remplacez 192.168.x.x par l'IP locale de votre PC). Autorisez le port dans le pare-feu si nécessaire.
2. Démo ponctuelle. Un tunnel temporaire ou une redirection de port sur la box suffit pour montrer un prototype. Les risques sont réels : votre PC est exposé, il n'y a pas de HTTPS, et debug=True est dangereux. La documentation de Werkzeug précise que le débogueur permet d'exécuter du code arbitraire et représente un risque de sécurité majeur. Le PIN affiché dans le terminal est une protection, pas une garantie. Désactivez le mode debug dès que la machine est visible de l'extérieur.
3. Production. Flask le dit clairement : son serveur de développement n'est conçu que pour le développement local, et pas pour être efficace, stable ou sûr. Il vous faut une machine dédiée, avec IP publique.
Déployer une application Python sur un VPS en production
Voici la checklist, étape par étape. Elle suppose un VPS sous Ubuntu ou Debian, avec une app dans /opt/monapp.
- Commander un VPS Linux et vous y connecter en SSH. Le VPS Python - Bots, APIs et scripts en ligne 24/7 fournit une IP dédiée IPv4+IPv6 et un accès root. Si vous travaillez sous Windows, ce guide pour gérer un VPS depuis un poste Windows vous mettra le pied à l'étrier avec SSH. Ensuite :
ssh root@IP_DU_VPS. - Installer Python et créer un venv.
apt update && apt install -y python3-venv nginx cd /opt/monapp && python3 -m venv venv venv/bin/pip install -r requirements.txt gunicorn - Lancer l'app avec un serveur d'application, sur 127.0.0.1:8000. Gunicorn pour Flask (WSGI), Uvicorn pour FastAPI (ASGI). Le paradoxe vaut qu'on s'y arrête : en production, on écoute de nouveau sur localhost, et c'est Nginx qui expose l'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 - Créer un service systemd dans
/etc/systemd/system/monapp.service, pour que l'app redémarre seule et se lance au boot :
Puis[Unit] Description=Mon application Python After=network.target [Service] User=www-data WorkingDirectory=/opt/monapp ExecStart=/opt/monapp/venv/bin/gunicorn -w 3 -b 127.0.0.1:8000 app:app Restart=always [Install] WantedBy=multi-user.targetsystemctl enable --now monapp. - Configurer Nginx en reverse proxy (ports 80 et 443) dans
/etc/nginx/sites-available/monapp:server { listen 80; server_name exemple.fr; location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; } } - Ajouter un enregistrement DNS : A vers l'IPv4 du VPS, AAAA vers son IPv6.
- Activer HTTPS avec Let's Encrypt :
apt install -y certbot python3-certbot-nginx && certbot --nginx -d exemple.fr. Les certificats sont aujourd'hui valables 90 jours. Let's Encrypt annonce une réduction à 45 jours d'ici 2028 : lancezcertbot renew --dry-runpour vérifier que le renouvellement automatique fonctionne. - Fermer le pare-feu sauf 22, 80 et 443. Autorisez SSH avant d'activer UFW, sinon vous vous retrouvez enfermé dehors :
Pour durcir davantage la machine, lisez comment sécuriser son VPS Linux avec UFW, fail2ban et durcissement SSH.ufw allow OpenSSH && ufw allow 80/tcp && ufw allow 443/tcp && ufw enable
La logique est identique avec un autre langage. Pour une API JavaScript, voyez comment déployer une application Node.js sur VPS.
Questions fréquentes
Pourquoi est-ce que je ne peux pas accéder à localhost ?
Le plus souvent, l'application n'est pas lancée, ou elle écoute sur un autre port ou sur une autre famille d'adresses (IPv4 contre IPv6). Vérifiez avec ss -tlnp ou netstat -ano, puis essayez 127.0.0.1 à la place de localhost.
Que signifie « localhost » ?
C'est le nom réservé de votre propre machine. Il renvoie vers l'interface de bouclage, 127.0.0.1 en IPv4 et ::1 en IPv6, et le trafic ne sort jamais vers le réseau.
Quelle différence entre localhost et 127.0.0.1 ?
127.0.0.1 est une adresse IPv4. Localhost est un nom, qui peut se résoudre en 127.0.0.1 comme en ::1 selon le système. Si l'app n'écoute qu'en IPv4, l'un peut fonctionner et pas l'autre.
Est-ce dangereux de lancer Flask avec host 0.0.0.0 ?
Sur un réseau domestique de confiance, le risque reste limité. Sur un Wi-Fi public, ou avec debug=True, n'importe qui sur le réseau peut atteindre votre app, voire exécuter du code via le débogueur. Dès que l'app sort de votre machine, lancez-la sans debug.
Comment donner accès à mon application locale à quelqu'un d'autre ?
Sur le même réseau, utilisez --host 0.0.0.0 et partagez votre IP locale. Pour quelqu'un à distance, un tunnel temporaire convient à une démo courte. Au-delà, déployez sur un serveur avec IP publique.
Faut-il un nom de domaine pour mettre une API Python en ligne ?
Non, l'API répond sur l'IP publique du serveur. Le domaine devient nécessaire pour obtenir un certificat HTTPS Let's Encrypt, et il sert aussi aux webhooks qui exigent une URL sécurisée.
Passer du localhost à une IP publique
Faites un premier essai en suivant les huit étapes ci-dessus sur un serveur neuf, avec une app minimale de quelques lignes. Dès que la requête passe de Nginx à Gunicorn, le reste n'est que de la configuration.
Le VPS Python - Bots, APIs et scripts en ligne 24/7 d'OmegaCloud couvre ce besoin : IP dédiée IPv4+IPv6, machine allumée sans votre PC, accès root pour Gunicorn, Uvicorn, systemd et Nginx, Anti-DDoS 1 Tbit/s inclus et hébergement en France conforme au RGPD. Les offres sont actuellement en pré-commande, avec une disponibilité annoncée au 1er novembre 2026.