Cloudflare Tunnel sans ports ouverts : configuration VPS
Configurez un tunnel nommé, le fichier de credentials, les règles ingress et le service systemd, puis fermez 80 et 443 et liez l’application à localhost.
Ce que fait Cloudflare Tunnel et ce que signifie réellement l’absence de ports ouverts
Cloudflare Tunnel installe sur votre VPS un petit daemon appelé cloudflared. Ce daemon ouvre une connexion sortante vers Cloudflare et la maintient ouverte. Les requêtes destinées à votre hostname arrivent sur l’edge de Cloudflare, puis sont transmises via cette connexion existante. Aucun hôte n’a donc besoin d’établir une connexion entrante vers votre serveur.
L’étape que la plupart des guides n’abordent jamais : installer le tunnel ne ferme aucun port. Si 80 et 443 sont toujours ouverts dans votre firewall et si votre application écoute toujours sur 0.0.0.0, vous avez ajouté un second point d’entrée au lieu de remplacer le premier. Votre IP d’origine reste accessible, et toute personne qui la découvre contourne directement Cloudflare. La fermeture de ces ports est une étape manuelle. C’est elle qui donne son intérêt à tout le reste.
cloudflared doit pouvoir atteindre region1.v2.argotunnel.com et region2.v2.argotunnel.com en sortie sur le port 7844. Il utilise UDP pour le protocole QUIC et bascule sur TCP pour HTTP/2. Si le réseau filtre les connexions sortantes, autorisez les deux protocoles ou forcez l’utilisation de TCP avec --protocol http2.
Avant de commencer
- Un domaine déjà présent dans un compte Cloudflare, avec les serveurs de noms de Cloudflare qui servent la zone.
cloudflared tunnel route dnsécrit les enregistrements dans cette zone, qui doit donc déjà exister. - Un accès sortant sur le port 7844 depuis le VPS, en UDP et en TCP.
- Une application qui écoute déjà en local, même s’il ne s’agit que de
python3 -m http.server 8080pour le premier test. sudosur le serveur, ainsi qu’une deuxième session SSH ouverte avant de modifier le firewall.
Installer cloudflared sur Ubuntu ou Debian
Cloudflare associe un paquet .deb à chaque version cloudflared, donc l’installation se limite à un téléchargement et à une commande dpkg.
curl -fsSL -o /tmp/cloudflared.deb \
https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i /tmp/cloudflared.deb
cloudflared --versionExécutez d’abord dpkg --print-architecture si vous n’êtes pas certain de l’architecture du serveur. Sur ARM 64 bits, le nom de fichier se termine par arm64 et non par amd64 ; rien d’autre ne change. L’affichage d’une chaîne de version par cloudflared --version est la seule confirmation nécessaire avant de continuer.
Un paquet installé de cette manière ne passe pas par le circuit de mise à jour d’apt. apt-get upgrade ne le mettra donc jamais à jour, et vous devrez gérer vous-même ses mises à jour. sudo cloudflared update récupère la dernière version et remplace le binaire sur place. Une fois le service créé, exécutez sudo systemctl restart cloudflared afin que le processus en cours utilise le nouveau binaire. Intégrez cette opération au même calendrier que le reste de vos mises à jour, car un daemon de tunnel est un logiciel exposé à Internet, même s’il n’écoute sur aucun port.
Se connecter et créer un tunnel nommé
cloudflared tunnel loginSur un VPS sans interface graphique, aucun navigateur ne s’ouvre. Copiez donc l’URL affichée dans le navigateur de votre ordinateur portable, puis sélectionnez la zone. Une fois l’opération terminée, ~/.cloudflared/cert.pem existe.
cert.pem est l’identifiant de votre compte. Il autorise la création de tunnels, l’écriture d’enregistrements DNS dans cette zone et la suppression de tunnels. Le tunnel en fonctionnement ne l’utilise jamais. Traitez-le comme un mot de passe, car une simple copie de ce fichier suffit pour permettre à quelqu’un de publier de nouveaux noms d’hôte sur votre domaine.
cloudflared tunnel create homelabUne exécution réussie affiche les 2 lignes ci-dessous. L’UUID qu’elles contiennent est la valeur à copier dans le fichier de configuration.
Tunnel credentials written to /home/you/.cloudflared/6ff42ae2-765d-4adf-8112-31c55c1551ef.json. cloudflared chose this file based on where your origin certificate was found. Keep this file secret. To revoke these credentials, delete the tunnel.
Created tunnel homelab with id 6ff42ae2-765d-4adf-8112-31c55c1551efCe fichier JSON est l’identité du tunnel. C’est le seul identifiant dont le service en fonctionnement a besoin. Toute personne qui le détient peut s’enregistrer comme votre tunnel et recevoir votre trafic. Il n’est pas possible de le renouveler seul : pour le révoquer, il faut cloudflared tunnel delete homelab et créer un nouveau tunnel.
Conservez le fichier d’identifiants dans un emplacement approprié
Le service s’exécute avec les privilèges de root. Placez donc le fichier dans un répertoire appartenant à root, au lieu de le laisser dans un répertoire personnel auquel une tâche de sauvegarde ou un compte partagé pourrait accéder.
sudo install -d -m 700 -o root -g root /etc/cloudflared
sudo install -m 600 -o root -g root \
~/.cloudflared/6ff42ae2-765d-4adf-8112-31c55c1551ef.json \
/etc/cloudflared/6ff42ae2-765d-4adf-8112-31c55c1551ef.json
rm ~/.cloudflared/6ff42ae2-765d-4adf-8112-31c55c1551ef.json
sudo ls -l /etc/cloudflared
cloudflared tunnel listls -l doit afficher -rw------- root root sur le fichier JSON. cloudflared tunnel list lit cert.pem, donc cela continue de fonctionner. La commande doit afficher le nom du tunnel, son UUID et le nombre de connexions qu’il gère actuellement.
Écrivez config.yml avec les vraies règles d’ingress
Écrivez la configuration dans /etc/cloudflared/config.yml, pas dans votre répertoire personnel. Voici pourquoi. cloudflared service install copie toute configuration trouvée vers /etc/cloudflared/config.yml, puis inscrit en dur --config /etc/cloudflared/config.yml dans l’unité systemd. Si vous créez le fichier dans ~/.cloudflared/config.yml, cette copie est un instantané unique. Les modifications ultérieures de la copie située dans le répertoire personnel ne changent rien : le service continue de servir les anciennes règles et aucun avertissement n’est affiché. Écrire le fichier directement à son emplacement définitif élimine entièrement ce problème.
tunnel: 6ff42ae2-765d-4adf-8112-31c55c1551ef
credentials-file: /etc/cloudflared/6ff42ae2-765d-4adf-8112-31c55c1551ef.json
loglevel: info
ingress:
- hostname: app.example.com
service: http://127.0.0.1:8080
- hostname: files.example.com
service: http://127.0.0.1:8081
- hostname: grafana.example.com
path: ^/api/
service: http://127.0.0.1:3000
- service: http_status:404Les règles sont lues de haut en bas et la première correspondance est retenue. Une règle sans hostname correspond à tous les noms d’hôte ; c’est pourquoi la règle fourre-tout doit être placée en dernier. Si vous l’omettez, la configuration est rejetée avec The last ingress rule must match all URLs (i.e. it should not have a hostname or path filter). http_status:404 est un service intégré qui renvoie 404 et ne fait rien d’autre. Il est indispensable : sans lui, une requête destinée à un nom d’hôte que vous n’avez jamais prévu de publier tombe sur la dernière règle réelle présente dans la configuration.
Utilisez 127.0.0.1 dans l’URL service: plutôt que localhost. Sur Ubuntu, localhost est d’abord résolu vers ::1, et une application liée uniquement à la loopback IPv4 refuse cette connexion. La ligne de journal est dial tcp [::1]:8080: connect: connection refused et le visiteur reçoit une erreur 502.
http:// simple est correct ici, car le trafic ne quitte jamais la machine. Utilisez https:// uniquement lorsque l’application locale exige TLS (transport layer security), et attendez-vous à x509: certificate is valid for example.com, not localhost si son certificat ne correspond pas au nom que vous avez appelé. Corrigez cela avec originServerName sous originRequest, ou acceptez le risque avec noTLSVerify: true.
Vérifiez les règles avant de démarrer quoi que ce soit :
sudo cloudflared --config /etc/cloudflared/config.yml tunnel ingress validate
sudo cloudflared --config /etc/cloudflared/config.yml tunnel ingress rule https://app.example.com/loginingress validate indique si la configuration est valide ou nomme la règle qui l’a rendue invalide. ingress rule prend une URL et affiche la première règle qui lui correspond. C’est le moyen le plus rapide de vérifier qu’une expression régulière path ne correspond pas à ce que vous pensiez.
Pointer le DNS vers le tunnel
cloudflared tunnel route dns homelab app.example.com
cloudflared tunnel route dns homelab files.example.com
cloudflared tunnel route dns homelab grafana.example.comChaque commande crée un enregistrement CNAME proxifié qui pointe vers 6ff42ae2-765d-4adf-8112-31c55c1551ef.cfargotunnel.com. Cette cible n’est résolue qu’au sein du réseau Cloudflare. La réponse DNS publique pour votre nom d’hôte contient donc une adresse Cloudflare, et l’adresse IP de votre VPS n’y apparaît jamais. Un joker hostname dans config.yml nécessite malgré tout un enregistrement DNS correspondant pour chaque nom que vous utilisez réellement.
Lorsqu’un enregistrement existe déjà, la commande échoue avec le message suivant :
Failed to add route: code: 1003, reason: Failed to create record app.example.com with err An A, AAAA, or CNAME record with that host already exists.Cet enregistrement existant est presque toujours l’ancien enregistrement A qui pointe vers l’adresse IP publique de votre VPS. C’est précisément celui que vous devez supprimer. Supprimez-le dans le tableau de bord Cloudflare, puis exécutez à nouveau la commande. Si vous le laissez en place, le DNS continue de publier l’adresse IP de votre serveur d’origine. Le tunnel ne masque donc rien.
Installer le tunnel comme service pour qu’il survive à un redémarrage
Lancez-le d’abord une fois au premier plan. Une erreur est beaucoup plus facile à lire dans votre terminal que dans le journal.
sudo cloudflared --config /etc/cloudflared/config.yml tunnel run homelabUn démarrage correct journalise plusieurs lignes Registered tunnel connection, une par emplacement edge, chacune avec son propre connIndex. Ouvrez l’un de vos noms d’hôte dans un navigateur et vérifiez que les règles d’ingress vous redirigent au bon endroit, puis arrêtez le processus avec Ctrl-C.
sudo cloudflared --config /etc/cloudflared/config.yml service install
systemctl status cloudflaredCette commande écrit /etc/systemd/system/cloudflared.service avec cloudflared-update.service et cloudflared-update.timer, puis exécute systemctl enable cloudflared.service et systemctl start cloudflared.service pour vous. enable est la partie importante ici, car c’est elle qui relance le tunnel après un redémarrage. Le ExecStart de l’unité vaut cloudflared --no-autoupdate --config /etc/cloudflared/config.yml tunnel run, ce qui rend ce chemin de configuration obligatoire.
Trois erreurs peuvent se produire à cette étape, avec des messages précis. possible conflicting configuration in /home/you/.cloudflared/config.yml and /etc/cloudflared/config.yml signifie que les deux fichiers existent et que cloudflared refuse de choisir à votre place : supprimez celui dont vous ne voulez pas. configuration file ... must contain entries for the tunnel to run and its associated credentials (tunnel: TUNNEL-UUID, credentials-file: CREDENTIALS-FILE) signifie que votre configuration utilise l’abréviation quick url: au lieu des clés du tunnel nommé. Cette abréviation ne peut pas être utilisée comme service. cloudflared service is already installed signifie qu’une ancienne unité est encore présente. Exécutez donc d’abord sudo cloudflared service uninstall.
Il n’y a pas de reload. Après avoir modifié /etc/cloudflared/config.yml, exécutez sudo systemctl restart cloudflared. Vérifiez ensuite le comportement après redémarrage au lieu de le supposer :
sudo reboot
# once it is back
systemctl is-enabled cloudflared
systemctl is-active cloudflared
journalctl -u cloudflared -n 50 --no-pager
curl -sI https://app.example.comLe fait que is-enabled affiche enabled et que is-active affiche active est l’objectif de cette section. Une fois les routes créées et le service démarré, cert.pem n’a plus rien à faire sur le serveur : rm ~/.cloudflared/cert.pem. Pour ajouter un nom d’hôte ultérieurement, il suffit d’exécuter à nouveau cloudflared tunnel login.
Fermez 80 et 443, sinon le tunnel n’est qu’un chemin supplémentaire
Deux modifications sont nécessaires, et vous devez appliquer les deux. Si vous n’en faites qu’une, l’origine reste accessible.
Commencez par lier l’application à l’adresse loopback. Dans nginx, cela signifie utiliser listen 127.0.0.1:8080; à la place de listen 80;, comme dans cette explication de configuration d’un reverse proxy nginx. Dans Docker Compose, cela signifie utiliser ports: - "127.0.0.1:8080:80". La forme simple "8080:80" publie le port sur toutes les interfaces. Docker crée alors ses propres règles de NAT (network address translation), que les paquets traversent avant qu’ufw ne les voie. Une règle de refus ufw ne les bloque donc pas. Ce piège est expliqué dans cet article sur les ports publiés par Docker qui ignorent ufw.
sudo ss -lntpChaque service que vous avez déplacé doit maintenant afficher 127.0.0.1:8080 dans la colonne Local Address. Une ligne contenant 0.0.0.0:8080 ou *:8080 reste à l’écoute sur toutes les interfaces.
Ensuite, fermez les ports.
sudo ufw status numbered
sudo ufw delete allow 'Nginx Full'
sudo ufw statusSupprimez les règles d’autorisation pour 80 et 443 au lieu d’ajouter des règles de refus par-dessus. ufw s’arrête à la première règle correspondante. Une ancienne règle d’autorisation située plus haut dans la liste reste donc prioritaire. Conservez votre règle SSH. Le guide des bases du pare-feu ufw explique le reste de cette configuration. La plupart des fournisseurs de VPS proposent également un pare-feu réseau distinct dans leur panneau de contrôle. Ce pare-feu n’est pas ufw. Fermez donc aussi 80 et 443 à cet endroit.
Vérifiez maintenant depuis une autre machine. La valeur de curl http://127.0.0.1:8080 sur le serveur lui-même ne prouve rien concernant l’accès depuis l’extérieur.
# run these from a different machine
nc -vz 203.0.113.10 443
curl -sI https://app.example.comUn nc refusé ou expiré vers l’adresse IP brute, ainsi qu’un 200 via le nom d’hôte, correspondent au résultat attendu. Vérifier qu’un port est réellement ouvert présente d’autres méthodes de test.
Le tunnel assure le transport, pas l’authentification. Tout ce que vous publiez par son intermédiaire est accessible publiquement, sauf si vous placez une authentification devant, avec Cloudflare Access à la périphérie ou un proxy OAuth2 placé devant l’application sur le serveur. SSH nécessite également une configuration distincte, car le tunnel ne le couvre pas : laissez le port 22 ouvert, mais limitez-le à vos propres adresses source.
Ce que Cloudflare Tunnel vous apporte, et ce qu’il vous coûte
Les avantages sont réels. L’adresse IP de votre serveur d’origine n’est plus publiée, aucun port entrant n’est exposé, la configuration fonctionne même depuis une machine qui n’a aucune adresse IP publique, et Cloudflare gère le certificat public. Aucun client ACME (automatic certificate management environment) ne s’exécute donc sur votre machine. Les attaques volumétriques sont absorbées en périphérie du réseau, au lieu de consommer votre bande passante.
Les coûts sont tout aussi réels. Cloudflare termine TLS sur son réseau périphérique : la requête du visiteur y est déchiffrée, puis chiffrée de nouveau dans le tunnel. Cloudflare peut donc lire le trafic. C’est ce qui permet d’utiliser son firewall, sa mise en cache et ses règles Access. Aucun réglage ne permet de désactiver ce comportement tout en continuant à utiliser son proxy. Si le fait qu’un tiers détienne vos données en clair est inacceptable, arrêtez-vous ici et choisissez une autre solution.
Cloudflare devient également une dépendance critique pour l’accessibilité. Lorsque cloudflared n’est pas connecté, les visiteurs obtiennent la page d’erreur 1033 de Cloudflare au lieu de votre application. Vous avez en outre supprimé la route directe sur laquelle ils auraient pu se rabattre.
Depuis un navigateur classique, seuls HTTP, HTTPS et WebSocket peuvent atteindre un hostname public. Pour tout autre protocole TCP, comme SSH, RDP (remote desktop protocol) ou un serveur de jeu, un logiciel doit également être installé côté client : cloudflared access tcp pour transférer un port local, ou le client WARP. Il n’existe pas de solution sans client pour ces protocoles.
La taille des corps de requête est limitée sur le réseau périphérique. Un upload qui dépasse cette limite est rejeté avec le code HTTP 413 avant même d’atteindre votre application. En août 2026, cette limite est de 100 MB avec les offres Free et Pro, et elle est plus élevée avec les offres payantes. Consultez donc la page actuelle des limites Cloudflare avant de concevoir votre architecture autour d’une valeur précise. Les conditions d’utilisation en libre-service de Cloudflare limitent également l’utilisation principale du proxy à la diffusion de vidéos et d’autres fichiers volumineux non HTML. Lisez-les avant de connecter une médiathèque à un tunnel gratuit.
Cloudflare Tunnel, un tunnel SSH inversé ou Tailscale Funnel
Les trois connexions sont uniquement sortantes. Elles fonctionnent donc depuis une machine qui n’accepte aucune connexion entrante et ne possède pas d’adresse IP publique. Elles diffèrent par l’entité qui peut lire vos données en clair et par le nom d’hôte visible publiquement.
Un tunnel SSH inversé nécessite une deuxième machine disposant d’une adresse IP publique. Cette machine devient le point d’entrée : vous gérez vous-même son certificat, son reverse proxy et son pare-feu. Aucun tiers ne déchiffre les données. La configuration comporte davantage d’éléments et nécessite autossh ou une unité systemd avec Restart=always pour résister à une brève interruption réseau. Le guide du tunnel SSH inversé pour le CGNAT détaille cette configuration.
Tailscale Funnel est la comparaison la plus proche. La connexion est également uniquement sortante et TLS se termine sur votre propre machine. Les relais Tailscale ne voient donc jamais les données en clair. En contrepartie, le nom d’hôte et les ports sont limités : Funnel ne sert que les noms situés sous le domaine ts.net de votre tailnet, et uniquement sur 443, 8443 et 10000. La différence entre Tailscale Serve et Funnel présente les deux solutions.
Choisissez donc la solution correspondant à votre contrainte réelle. Choisissez Cloudflare Tunnel lorsque le public doit accéder à votre propre domaine et que vous acceptez que Cloudflare lise le trafic. Choisissez Tailscale Funnel lorsqu’un nom d’hôte ts.net vous convient et que vous ne voulez pas confier les données en clair à un proxy. Choisissez un tunnel SSH inversé lorsque vous possédez déjà une machine publique et ne voulez aucun tiers sur le chemin.
FAQ
Ai-je encore besoin d’ouvrir le port 443 avec Cloudflare Tunnel ?
Non. cloudflared établit une connexion sortante vers Cloudflare sur le port 7844, et chaque requête revient par cette connexion. Aucun port entrant n’est donc utilisé. L’installation du tunnel ne ferme toutefois rien à votre place. Supprimez les règles d’autorisation des ports 80 et 443 dans ufw, fermez-les dans le firewall réseau distinct de votre fournisseur, liez l’application à 127.0.0.1 et supprimez tout enregistrement A restant qui publie encore l’adresse IP de votre VPS. Vérifiez avec sudo ss -lntp sur le serveur, puis avec nc -vz <your-ip> 443 depuis une autre machine.
Pourquoi mon nom d’hôte affiche-t-il l’erreur Cloudflare 1033 ?
L’erreur 1033 signifie que Cloudflare possède l’enregistrement DNS de ce nom d’hôte, mais ne trouve aucun cloudflared sain et connecté pour recevoir la requête. Soit le processus est arrêté, soit il fonctionne mais ne peut pas joindre Cloudflare. Vérifiez systemctl status cloudflared et journalctl -u cloudflared -n 50, puis confirmez que le port sortant 7844 est autorisé en UDP et en TCP. Un firewall qui bloque UDP et QUIC sans autoriser le repli sur TCP produit exactement ce résultat. cloudflared tunnel info homelab affiche les connexions actuellement vues par Cloudflare. Une liste vide indique que le problème se trouve de votre côté.
Pourquoi le tunnel renvoie-t-il une erreur 502 Bad Gateway ?
Une erreur 502 signifie que cloudflared a été atteint, mais n’a pas pu joindre votre service local. Le problème se situe donc entre ces deux éléments, et non chez Cloudflare. Consultez le journal. dial tcp [::1]:8080: connect: connection refused signifie que rien n’écoute à l’adresse indiquée. Le [::1] présent dans ce message signifie généralement que vous avez écrit localhost dans l’URL service: alors que l’application écoute uniquement en IPv4. Écrivez donc http://127.0.0.1:8080 à la place. HTTP/1.x transport connection broken: malformed HTTP response correspond au cas inverse : vous avez écrit https:// alors que l’origine utilise HTTP en clair.
Puis-je utiliser SSH, RDP ou un serveur de jeu avec Cloudflare Tunnel ?
Pas avec un client standard. Un nom d’hôte public via un tunnel transporte HTTP, HTTPS et WebSocket, c’est-à-dire les protocoles utilisés par un navigateur. Tout autre protocole TCP nécessite également un logiciel sur la machine cliente : soit cloudflared access tcp, qui transfère un port local, soit le client WARP. Si vous voulez utiliser SSH depuis n’importe quelle machine sans rien installer, le tunnel n’est pas l’outil adapté. Laissez le port 22 ouvert et limitez-le selon l’adresse source.
Cloudflare voit-il mon trafic via le tunnel ?
Oui. Cloudflare termine TLS sur son edge, déchiffre la requête à cet endroit, puis la chiffre de nouveau dans le tunnel vers votre serveur. Ce déchiffrement permet à leur firewall, à leur cache et à leurs règles Access de fonctionner. Il signifie également que vos données en clair existent sur leurs machines. Aucune configuration ne permet de l’éviter tant que vous utilisez leur proxy. Si cela est inacceptable, utilisez Tailscale Funnel ou exécutez votre propre reverse proxy sur un serveur public.