Pourquoi un Cloudflare Tunnel plutôt qu'ouvrir un port
La configuration classique — ouvrir les ports 80 et 443 sur le pare-feu, pointer un enregistrement DNS vers l'IP du serveur, installer un reverse proxy — fonctionne bien quand vous contrôlez le réseau. Mais trois situations la mettent en échec : un FAI ou un réseau d'entreprise qui bloque les connexions entrantes sur 443, une IP dynamique qui invalide vos enregistrements DNS toutes les 24 heures, ou un VPS placé derrière un NAT strict qui ne permet aucun port forwarding.
Cloudflare Tunnel contourne ces trois cas d'un même mécanisme : le démon cloudflared établit une connexion sortante persistante vers les points de présence Cloudflare. Votre serveur n'accepte rien, Cloudflare reçoit les requêtes HTTPS et les transmet via ce tunnel chiffré. Le trafic entrant ne touche jamais votre serveur directement.
Ce que Cloudflare Tunnel apporte concrètement
- Zéro port ouvert — le pare-feu du VPS peut bloquer tout le trafic entrant (80, 443 compris) sans impacter l'accessibilité de l'application.
- HTTPS automatique — Cloudflare gère le certificat TLS côté client : pas de Let's Encrypt à configurer, pas de renouvellement à surveiller.
- NAT et IP dynamique transparents — la connexion sortante de
cloudflaredtraverse n'importe quel NAT ; l'IP du serveur peut changer sans reconfigurer le DNS. - Réseau d'entreprise ou FAI restrictif — si le port 443 entrant est bloqué chez vous, le tunnel reste fonctionnel car il repose sur des connexions sortantes HTTP/2 ou QUIC.
- Intégration Zero Trust optionnelle — les tunnels s'associent à Cloudflare Access pour restreindre l'accès à des utilisateurs authentifiés, sans VPN.
- Protection Cloudflare incluse — le trafic passe par le réseau Cloudflare : DDoS mitigation, WAF et rate limiting s'appliquent sans configuration supplémentaire.
- Plan gratuit disponible — un tunnel simple, sans load-balancing, est utilisable sans abonnement payant Cloudflare.
Prérequis
Pour suivre ce guide, il vous faut :
VPS avec accès root. L'installation de cloudflared comme service systemd — la seule façon de garantir le redémarrage automatique — nécessite les droits root. Un hébergement mutualisé ou une instance sans accès root ne permet pas cette configuration.
Ressources minimales. cloudflared consomme moins de 50 Mo de RAM et négligeable en CPU. Comptez 1 vCPU et 512 Mo de RAM comme strict minimum pour le démon seul ; la contrainte réelle vient de l'application que vous exposez.
Un domaine géré par Cloudflare. Le domaine doit être enregistré ou transféré vers Cloudflare (ou délégation NS vers Cloudflare). Sans zone Cloudflare active, un tunnel named ne peut pas créer d'enregistrement DNS automatique.
Docker Engine (si vous utilisez la variante Docker Compose de ce guide). Disponible sur Ubuntu 22.04/24.04, Debian 12 et les distributions RHEL-compatibles.
Un compte Cloudflare gratuit. Aucun abonnement payant n'est requis pour un tunnel unique sans load-balancing.
Installer et configurer cloudflared sur le VPS
Installer cloudflared via le dépôt Cloudflare
Cloudflare publie cloudflared sous forme de paquet .deb / .rpm et de binaire statique. Pour une installation via APT sur Debian/Ubuntu :
curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg > /dev/null
echo "deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/cloudflared.list
sudo apt update && sudo apt install cloudflaredVérifiez que l'installation s'est déroulée correctement :
cloudflared --versionLa commande doit retourner une ligne du type cloudflared version 2025.x.x (built ...). La version exacte dépend du moment de l'installation ; référez-vous au dépôt officiel cloudflare/cloudflared pour le numéro courant.
Authentifier cloudflared auprès de Cloudflare
Sur le VPS (ou localement si vous avez un accès graphique), lancez :
cloudflared tunnel loginUn lien s'affiche dans le terminal. Ouvrez-le dans un navigateur, sélectionnez la zone Cloudflare à autoriser, puis validez. Un certificat ~/.cloudflared/cert.pem est créé sur la machine.
Créer un tunnel nommé
Créez un tunnel avec un nom descriptif :
cloudflared tunnel create mon-tunnelCloudflare génère un identifiant UUID et un fichier de credentials ~/.cloudflared/<UUID>.json. Notez l'UUID ; vous en aurez besoin dans les étapes suivantes.
Écrire le fichier de configuration config.yml
Créez /etc/cloudflared/config.yml :
sudo mkdir -p /etc/cloudflaredContenu du fichier (adaptez <UUID>, votre-domaine.com et le port de votre application) :
tunnel: <UUID>
credentials-file: /home/<user>/.cloudflared/<UUID>.json
ingress:
- hostname: app.votre-domaine.com
service: http://localhost:3000
- service: http_status:404La dernière règle — service: http_status:404 sans hostname — est obligatoire : elle sert de règle catch-all. Sans elle, cloudflared refuse de démarrer et retourne l'erreur "You must specify an ingress rule that matches all incoming requests". Si votre application tourne sur un autre port ou protocole, remplacez http://localhost:3000 en conséquence (par exemple http://localhost:8080 ou tcp://localhost:22 pour SSH).
Créer l'enregistrement DNS et lancer le tunnel
Enregistrez automatiquement le sous-domaine dans votre zone Cloudflare :
cloudflared tunnel route dns mon-tunnel app.votre-domaine.comPuis testez le tunnel en mode foreground pour valider la configuration :
cloudflared tunnel run mon-tunnelOuvrez https://app.votre-domaine.com dans un navigateur. Si l'application répond, arrêtez le process (Ctrl+C) et passez à l'étape suivante.
Installer cloudflared comme service systemd
Pour que le tunnel redémarre automatiquement au reboot, installez-le comme démon système :
sudo cloudflared service install
sudo systemctl enable cloudflared
sudo systemctl start cloudflared
sudo systemctl status cloudflaredLe fichier unit systemd créé par Cloudflare se trouve dans /etc/systemd/system/cloudflared.service. Son contenu ressemble à :
[Unit]
Description=cloudflared
After=network.target
[Service]
TimeoutStartSec=0
Type=notify
ExecStart=/usr/bin/cloudflared --no-autoupdate tunnel run
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.targetAvec ce service actif, le tunnel est opérationnel dès le démarrage du VPS, sans intervention manuelle.
Intégrer cloudflared dans un Docker Compose existant
Si votre application tourne déjà dans une stack Docker Compose, ajoutez un service cloudflared dans le même fichier. L'approche par token (sans fichier de credentials) est la plus simple pour un conteneur :
services:
app:
image: mon-image
networks:
- internal
cloudflared:
image: cloudflare/cloudflared:latest
command: tunnel --no-autoupdate run
environment:
- TUNNEL_TOKEN=${TUNNEL_TOKEN}
networks:
- internal
restart: unless-stopped
networks:
internal:Définissez TUNNEL_TOKEN dans un fichier .env au même niveau. Le token se récupère depuis le tableau de bord Cloudflare → Zero Trust → Networks → Tunnels → votre tunnel → Configure → Connecteurs Docker. Le service cloudflared et votre application partagent le réseau internal ; dans config.yml ou via le token, pointez l'application par son nom de service Docker (http://app:3000 au lieu de http://localhost:3000).
Configuration post-installation
Une fois le tunnel opérationnel, quelques réglages complémentaires améliorent la robustesse de la configuration.
Récupérer l'IP réelle du client. Par défaut, votre application reçoit les requêtes depuis 127.0.0.1 ou depuis l'IP interne du tunnel. Pour obtenir l'IP réelle du visiteur, lisez l'en-tête CF-Connecting-IP que Cloudflare injecte automatiquement. Configurez votre application ou votre reverse proxy local pour faire confiance à cet en-tête.
Chiffrement de bout en bout. Le tunnel chiffre la connexion entre cloudflared et Cloudflare. La connexion entre cloudflared et votre application locale est en HTTP par défaut (loopback ou réseau Docker interne). Si votre application expose du HTTPS en local, ajoutez originServerName: app.votre-domaine.com dans la règle d'ingress correspondante pour que cloudflared valide le certificat.
Plusieurs services, un seul tunnel. Un tunnel peut exposer plusieurs services sur des sous-domaines distincts : ajoutez simplement des entrées supplémentaires dans le bloc ingress de config.yml, avant la règle catch-all.
Durcissement : fermez le port 80 et 443 entrant
Le principal avantage de cette architecture est de pouvoir fermer tous les ports entrants sur le VPS. Une fois le tunnel validé, appliquez ces règles UFW :
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw enableVotre application reste accessible via le tunnel Cloudflare (qui repose sur des connexions sortantes), et le port SSH reste ouvert pour l'administration. Aucune connexion directe sur 80 ou 443 n'atteint plus le serveur. Consultez le guide <a href="/blog/pare-feu-ufw-vps">Configurer UFW sur un VPS</a> pour la configuration complète.
Cloudflare Tunnel versus Nginx / Traefik : des approches complémentaires
Une objection fréquente : « J'ai déjà Nginx et Traefik qui font ce travail, pourquoi ajouter une couche Cloudflare ? » La réponse est que les deux approches ne résolvent pas le même problème.
Un reverse proxy local (Nginx, Traefik, Caddy) gère le routage entre services sur le même réseau et le renouvellement SSL — mais il suppose que le trafic entrant atteigne le serveur. Si le port 443 est bloqué par le réseau en amont, le reverse proxy ne sert à rien.
Cloudflare Tunnel résout précisément ce que le reverse proxy local ne peut pas : le trafic arrive à Cloudflare quelle que soit la connectivité réseau du serveur. Les deux sont complémentaires : vous pouvez très bien placer Traefik derrière le tunnel pour gérer le routage interne, et laisser Cloudflare gérer le TLS public.
Cloudflare Tunnel vs reverse proxy local
| Critère | Cloudflare Tunnel | Reverse proxy local (Nginx/Traefik) |
|---|---|---|
| Port entrant requis | Non — connexion sortante uniquement | Oui — 80/443 doivent être accessibles |
| TLS public | Géré par Cloudflare, automatique | Let's Encrypt via ACME (certbot, Traefik…) |
| IP dynamique / NAT strict | Transparent — pas de DNS à mettre à jour | Problématique — nécessite un DynDNS ou une IP fixe |
| Load balancing | Plan payant (Cloudflare Load Balancing) | Disponible nativement (Traefik, Nginx upstream) |
| Latence | Légèrement supérieure (routage via POP Cloudflare) | Minimale — trafic direct vers le serveur |
| Dépendance externe | Oui — Cloudflare doit être joignable | Non — fonctionne sans tiers |
Dépannage : messages d'erreur courants
You must specify an ingress rule that matches all incoming requests
La règle catch-all est absente ou mal placée dans config.yml. Elle doit être la dernière entrée du bloc ingress, sans hostname, avec service: http_status:404.
Unable to locate config file in default locationscloudflared cherche sa configuration dans ~/.cloudflared/config.yml ou /etc/cloudflared/config.yml. Précisez le chemin explicitement avec cloudflared tunnel --config /etc/cloudflared/config.yml run mon-tunnel.
ERR connection to origin timed out dans les logs
L'application cible n'est pas joignable depuis cloudflared. Vérifiez que le service local tourne (curl http://localhost:3000) et que le port dans config.yml correspond. Dans un contexte Docker Compose, utilisez le nom du service (http://app:3000) plutôt que localhost.
Token expiré : tunnel credentials file not found ou token is expired
Les tokens générés via l'interface Cloudflare ont une durée de vie limitée si le connecteur n'a jamais été enregistré. Régénérez le token depuis Zero Trust → Networks → Tunnels → Configure → Connecteurs, puis mettez à jour la variable TUNNEL_TOKEN dans votre .env.
Limitation Plan Free : load-balancing et SSH par tunnel
Le plan gratuit ne prend pas en charge le load-balancing entre plusieurs origines. L'accès SSH via tunnel (cloudflared access ssh) sur le plan gratuit nécessite une configuration spécifique de Cloudflare Access et n'est pas activé par défaut. Le nombre de connecteurs par tunnel est limité à quelques instances sur le plan gratuit.
Cloudflare Tunnel comme architecture de référence sur VPS
Cloudflare Tunnel illustre bien ce que l'accès root sur un VPS rend possible : installer cloudflared comme démon système, modifier les règles du pare-feu, piloter des services systemd. Sur un hébergement mutualisé sans accès root, aucune de ces étapes n'est réalisable — le démon ne peut pas s'installer, le pare-feu n'est pas sous votre contrôle, et le service ne peut pas être configuré pour démarrer au boot.
Cette architecture convient particulièrement aux situations où la connectivité réseau est contrainte ou incertaine : labs de développement, bureaux avec pare-feu d'entreprise strict, serveurs edge, ou simplement refus d'exposer une IP publique. Elle s'associe naturellement aux reverse proxies locaux comme Traefik ou Nginx Proxy Manager pour le routage interne, et au durcissement système pour fermer les surfaces d'attaque directes.