Déploiement10 min de lecture

Cloudflare Tunnel : exposer une app VPS sans ouvrir de port

Vous venez de déployer une application sur votre VPS et vous vous retrouvez bloqué : IP dynamique, pare-feu d'entreprise qui filtre le port 443 entrant, ou refus d'exposer directement votre serveur à Internet. Un Cloudflare Tunnel résout ce problème en renversant la connexion : c'est `cloudflared` qui appelle Cloudflare, pas l'inverse. Résultat : votre application est accessible en HTTPS sur votre domaine, sans ouvrir le moindre port entrant et sans toucher à votre configuration DNS.

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 cloudflared traverse 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

01

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 cloudflared

Vérifiez que l'installation s'est déroulée correctement :

cloudflared --version

La 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.

02

Authentifier cloudflared auprès de Cloudflare

Sur le VPS (ou localement si vous avez un accès graphique), lancez :

cloudflared tunnel login

Un 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.

03

Créer un tunnel nommé

Créez un tunnel avec un nom descriptif :

cloudflared tunnel create mon-tunnel

Cloudflare 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.

04

Écrire le fichier de configuration config.yml

Créez /etc/cloudflared/config.yml :

sudo mkdir -p /etc/cloudflared

Contenu 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:404

La 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).

05

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.com

Puis testez le tunnel en mode foreground pour valider la configuration :

cloudflared tunnel run mon-tunnel

Ouvrez https://app.votre-domaine.com dans un navigateur. Si l'application répond, arrêtez le process (Ctrl+C) et passez à l'étape suivante.

06

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 cloudflared

Le 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.target

Avec ce service actif, le tunnel est opérationnel dès le démarrage du VPS, sans intervention manuelle.

07

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 enable

Votre 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èreCloudflare TunnelReverse proxy local (Nginx/Traefik)
Port entrant requisNon — connexion sortante uniquementOui — 80/443 doivent être accessibles
TLS publicGéré par Cloudflare, automatiqueLet's Encrypt via ACME (certbot, Traefik…)
IP dynamique / NAT strictTransparent — pas de DNS à mettre à jourProblématique — nécessite un DynDNS ou une IP fixe
Load balancingPlan payant (Cloudflare Load Balancing)Disponible nativement (Traefik, Nginx upstream)
LatenceLégèrement supérieure (routage via POP Cloudflare)Minimale — trafic direct vers le serveur
Dépendance externeOui — Cloudflare doit être joignableNon — 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 locations
cloudflared 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.

Un VPS avec accès root pour installer cloudflared

Le démon `cloudflared` et son service systemd nécessitent l'accès root — ce qu'un hébergement mutualisé ne permet pas. Un VPS ServOrbit vous donne le contrôle total : choix d'OS, accès root, IPv4 dédiée, et les templates du Marketplace pour démarrer rapidement.

Besoin d'aide ?

Parcourez notre centre d'aide et notre FAQ, ou écrivez à notre équipe — support en français, anglais et arabe.