📑 Table des matières

03 - Cloudflare Tunnel : exposer ses services sans ouvrir de ports

03 - Cloudflare Tunnel : exposer ses services sans ouvrir de ports

Self-Hosting 🟡 Intermédiaire ⏱️ 14 min de lecture 📅 2026-02-24

🤔 Le problème : exposer un service sans se mettre en danger

Quand vous lancez une application sur un VPS (une API FastAPI sur le port 8000, un dashboard admin sur le port 3000, un webhook sur le port 5000…), elle n'est accessible que localement.

Pour la rendre accessible depuis Internet, la méthode classique consiste à :

  1. Ouvrir le port dans le firewall (UFW, iptables…)
  2. Configurer un reverse proxy (Nginx, Caddy…)
  3. Obtenir un certificat SSL (Let's Encrypt…)
  4. Gérer le renouvellement du certificat
  5. Se protéger contre les attaques DDoS

Chaque étape ajoute de la complexité et des risques :

Risque Description
Ports ouverts Chaque port ouvert est une surface d'attaque potentielle
Mauvaise config Nginx Headers manquants, TLS mal configuré, fuite d'infos serveur
Certificat expiré Service inaccessible, erreurs navigateur
DDoS Un VPS standard ne résiste pas à une attaque volumétrique
Scan de ports Les bots scannent en permanence les IP publiques

Et si vous pouviez tout éviter ?

🚀 La solution : Cloudflare Tunnel (ex-Argo Tunnel)

Cloudflare Tunnel crée une connexion sortante depuis votre serveur vers le réseau Cloudflare. Aucun port entrant n'est nécessaire.

⚙️ Comment ça marche

[Utilisateur]  [Cloudflare Edge]  tunnel chiffré  [cloudflared sur votre VPS]  [localhost:8000]

Le flux est inversé par rapport à un serveur classique :

  1. cloudflared (le client tunnel) tourne sur votre VPS
  2. Il établit une connexion sortante vers Cloudflare
  3. Cloudflare route le trafic entrant vers votre tunnel
  4. Votre service reste sur localhostaucun port ouvert

✅ Avantages

Feature Classique (Nginx + Let's Encrypt) Cloudflare Tunnel
Ports à ouvrir 80, 443 + ports services Aucun (0)
SSL Let's Encrypt (config manuelle) Automatique
Protection DDoS Aucune (ou payante) Incluse gratuitement
Reverse proxy Nginx/Caddy à configurer Intégré
Difficulté Intermédiaire à avancé Débutant à intermédiaire
Coût Gratuit Gratuit
Zero Trust Non Oui (Access policies)

🛠️ Prérequis

Avant de commencer, vous aurez besoin de :

  • Un VPS avec Linux (Ubuntu/Debian recommandé) — Hostinger propose d'excellents VPS à partir de 3,99€/mois avec 20% de remise
  • Un nom de domaine pointé vers Cloudflare (nameservers Cloudflare)
  • Un compte Cloudflare (gratuit)
  • Un service local qui tourne (API, site, dashboard…)

💡 Astuce VPS : Pour un tunnel Cloudflare, même un petit VPS suffit. cloudflared consomme très peu de ressources (~20 Mo de RAM). Hostinger est idéal pour débuter avec un excellent rapport qualité/prix.

📥 Étape 1 : Installer cloudflared

🐧 Sur Ubuntu/Debian

L'installation sur Ubuntu/Debian se fait via le dépôt officiel Cloudflare : on ajoute la clé GPG, puis le dépôt, et on installe le paquet cloudflared avec apt. Cette méthode garantit des mises à jour automatiques.

✔️ Vérifier l'installation

cloudflared --version

📦 Alternative : binaire direct

Si le dépôt pose problème, vous pouvez télécharger le binaire officiel depuis les releases GitHub et le placer dans /usr/local/bin/ avec les droits d'exécution.

🔐 Étape 2 : Authentification

Connectez-vous à votre compte Cloudflare en exécutant la commande cloudflared tunnel login. Cette commande ouvre un lien dans le terminal : copiez-le dans votre navigateur, sélectionnez le domaine à utiliser, et autorisez. Un certificat sera sauvegardé dans ~/.cloudflared/cert.pem. Vous pouvez vérifier sa présence avec la commande ls -la ~/.cloudflared/cert.pem.

🏗️ Étape 3 : Créer le tunnel

Exécutez la commande cloudflared tunnel create mon-tunnel. Elle génère un UUID unique pour le tunnel et sauvegarde les credentials dans un fichier JSON. Notez cet UUID, vous en aurez besoin pour la configuration. Listez ensuite vos tunnels avec cloudflared tunnel list pour vérifier que tout est en ordre.

⚙️ Étape 4 : Configurer le tunnel

Créez le fichier de configuration avec la commande nano ~/.cloudflared/config.yml.

📝 Configuration simple (un seul service)

tunnel: a1b2c3d4-e5f6-7890-abcd-ef1234567890
credentials-file: /root/.cloudflared/a1b2c3d4-e5f6-7890-abcd-ef1234567890.json

ingress:
  - hostname: api.mondomaine.com
    service: http://localhost:8000
  - service: http_status:404

🔀 Configuration multi-services

Pour plusieurs services, on ajoute une entrée ingress par hostname, chacune pointant vers un port local différent. On peut aussi ajouter des options originRequest comme connectTimeout ou noTLSVerify selon les besoins de chaque service. La dernière entrée doit toujours être un catch-all http_status:404 sans hostname — c'est obligatoire.

✅ Valider la configuration

cloudflared tunnel ingress validate

🌐 Étape 5 : Configurer le DNS

Pour chaque hostname dans votre config, créez un enregistrement DNS CNAME automatiquement avec la commande cloudflared tunnel route dns mon-tunnel api.mondomaine.com (à répéter pour chaque sous-domaine). Cela crée automatiquement des enregistrements CNAME dans votre zone Cloudflare, tous pointant vers l'adresse cfargotunnel.com de votre tunnel avec le proxy activé.

▶️ Étape 6 : Lancer le tunnel

🧪 Test manuel

Lancez le tunnel avec la commande cloudflared tunnel run mon-tunnel. Si le tunnel est opérationnel, vous verrez 4 connexions enregistrées dans les logs. Testez ensuite dans votre navigateur : https://api.mondomaine.com

🏭 En tant que service systemd (production)

Pour que le tunnel démarre automatiquement au boot, installez-le en tant que service systemd via sudo cloudflared service install. Le service utilise la config située dans /etc/cloudflared/config.yml — pensez à y copier votre fichier de configuration et vos credentials JSON. Activez-le ensuite avec sudo systemctl enable cloudflared.

🐍 Cas d'usage 1 : Exposer une API FastAPI

Imaginons que vous développez une API IA avec FastAPI. L'application expose un endpoint /health pour le monitoring et un endpoint /generate pour les requêtes IA, acceptant un prompt et un paramètre max_tokens.

Lancez l'API sur localhost avec la commande uvicorn api:app --host 127.0.0.1 --port 8000.

Configuration tunnel adaptée aux services IA :

ingress:
  - hostname: api.mondomaine.com
    service: http://localhost:8000
    originRequest:
      connectTimeout: 30s  # Plus long pour les requêtes IA
  - service: http_status:404

Votre API est maintenant accessible sur https://api.mondomaine.com avec SSL automatique ! Testez avec un curl vers /health ou /generate depuis n'importe où.

📊 Cas d'usage 2 : Dashboard admin protégé

Pour un dashboard admin (Streamlit, Grafana, ou custom), vous voulez une couche d'authentification supplémentaire.

Cloudflare Access (Zero Trust)

Cloudflare offre gratuitement (jusqu'à 50 utilisateurs en 2025) un système d'authentification. Depuis le dashboard Zero Trust → Access → Applications, créez une application de type Self-hosted, indiquez le subdomain admin.mondomaine.com, et définissez une policy autorisant uniquement votre email.

Maintenant, quand quelqu'un accède à admin.mondomaine.com, il doit d'abord s'authentifier via Cloudflare Access. Même si votre dashboard n'a pas de login, il est protégé.

# config.yml - le dashboard est sur localhost:8501 (Streamlit)
ingress:
  - hostname: admin.mondomaine.com
    service: http://localhost:8501
    originRequest:
      noTLSVerify: true
  - service: http_status:404

Vérifier le header JWT dans votre app

Cloudflare Access ajoute un header Cf-Access-Jwt-Assertion à chaque requête. Vous pouvez le vérifier côté serveur en récupérant le token, en téléchargeant les clés publiques depuis l'endpoint /cdn-cgi/access/certs de votre team Cloudflare, puis en décodant le JWT avec l'algorithme RS256 en vérifiant l'audience correspondant à votre URL.

🔗 Cas d'usage 3 : Webhook receiver

Les webhooks (GitHub, Stripe, Telegram…) nécessitent une URL publique HTTPS. Cloudflare Tunnel est parfait pour ça. Côté application, il suffit de récupérer le body de la requête, de vérifier la signature (par exemple X-Hub-Signature-256 pour GitHub avec HMAC-SHA256), puis de traiter l'événement.

ingress:
  - hostname: webhook.mondomaine.com
    service: http://localhost:5000
    originRequest:
      connectTimeout: 10s
  - service: http_status:404

🔒 Sécurité avancée : verrouiller votre VPS

Maintenant que tout passe par Cloudflare Tunnel, vous pouvez fermer tous les ports sauf SSH en utilisant UFW : refusez tout le trafic entrant par défaut, autorisez le trafic sortant, ouvrez uniquement le port 22 en TCP, puis activez le firewall.

Votre VPS n'a plus qu'un seul port ouvert : SSH. Tout le reste passe par le tunnel.

Aller plus loin : restreindre SSH aussi

Vous pouvez restreindre SSH à votre IP only avec sudo ufw allow from VOTRE_IP to any port 22 proto tcp, ou même faire passer SSH via le tunnel Cloudflare en ajoutant une règle ssh://localhost:22 dans votre config ingress. Côté client, configurez votre ~/.ssh/config avec ProxyCommand cloudflared access ssh --hostname %h pour vous connecter via le tunnel.

🔍 Monitoring et debugging

Vérifier l'état du tunnel

Utilisez sudo systemctl status cloudflared pour le statut du service, sudo journalctl -u cloudflared -f pour les logs en temps réel, et cloudflared tunnel info mon-tunnel pour les métriques détaillées.

Logs détaillés

Pour lancer le tunnel en mode debug, ajoutez l'option --loglevel debug ou ajoutez loglevel: debug dans votre config.yml.

🔧 Configuration avancée

Timeouts et performances

Ajustez les timeouts selon le type de service dans la section originRequest de chaque règle ingress. Par exemple : 10 secondes pour une API classique, 120 secondes pour un service IA avec réponses lentes ou streaming, 5 secondes pour des fichiers statiques. Les WebSockets sont supportés nativement sans configuration supplémentaire.

Headers personnalisés

Cloudflare ajoute automatiquement des headers utiles à chaque requête : Cf-Connecting-Ip (IP réelle du visiteur), X-Forwarded-For, et X-Forwarded-Proto. Côté application, vous pouvez récupérer l'IP réelle et le pays du visiteur directement depuis ces headers.

Métriques et observabilité

Activez les métriques Prometheus en ajoutant metrics: localhost:2000 au niveau racine de votre config. Vous obtiendrez alors des métriques comme le nombre total de requêtes, les erreurs, les requêtes concurrentes, les réponses par code HTTP et la latence.

🐛 Troubleshooting avancé

Diagnostic étape par étape

Quand quelque chose ne marche pas, suivez cet arbre de décision :

  • 502 Bad Gateway → Vérifiez que le service local tourne (curl http://localhost:PORT), que le port est correct dans config.yml, et ajoutez noTLSVerify: true si le service est en HTTPS local.
  • ERR_NAME_NOT_RESOLVED → Le DNS n'est pas configuré. Vérifiez avec dig et relancez cloudflared tunnel route dns.
  • ERR_CONNECTION_TIMED_OUT → Le tunnel ne tourne pas, les credentials sont invalides, ou le firewall bloque le trafic sortant vers *.cloudflare.com.
  • 1033: Argo Tunnel error → Le tunnel n'existe plus. Vérifiez avec cloudflared tunnel list et recréez-le si nécessaire.
  • Access denied (avec Cloudflare Access) → Vérifiez que votre email est dans la policy et que le cookie n'est pas expiré.

Commandes de diagnostic utiles

Les commandes essentielles pour diagnostiquer : cloudflared tunnel run --loglevel debug pour les logs de connexion, dig +short pour vérifier le DNS, curl -v pour tester le service local, ss -tlnp pour les connexions actives, journalctl pour les logs système, et cloudflared tunnel ingress validate pour valider la config.

Problèmes fréquents et solutions

Le tunnel se déconnecte régulièrement

Vérifiez la stabilité réseau avec un ping prolongé. Le service systemd redémarre automatiquement le tunnel en cas de crash. Pour plus de sécurité, vous pouvez ajouter un script de monitoring en cron (toutes les 5 minutes) qui vérifie l'état du service et le redémarre si nécessaire, avec une alerte optionnelle via Telegram.

Latence élevée sur les requêtes

Vérifiez le datacenter Cloudflare utilisé avec curl -s https://api.mondomaine.com/cdn-cgi/trace | grep colo. Si le datacenter est éloigné géographiquement, c'est un problème de routage Cloudflare.

Migration vers un nouveau serveur

Pour migrer, copiez simplement le cert.pem, les fichiers JSON de credentials et le config.yml vers le nouveau serveur dans ~/.cloudflared/. Installez le service avec sudo cloudflared service install et démarrez-le. Aucune modification DNS n'est nécessaire — le tunnel reprend automatiquement sur le nouveau serveur.

📋 Sommaire

📋 Récapitulatif : la checklist complète

  • ✅ 1. VPS avec Linux (Hostinger recommandé)
  • ✅ 2. Domaine pointé vers Cloudflare (nameservers)
  • ✅ 3. cloudflared installé
  • ✅ 4. Authentification (cloudflared tunnel login)
  • ✅ 5. Tunnel créé (cloudflared tunnel create)
  • ✅ 6. config.yml rédigé
  • ✅ 7. DNS configuré (cloudflared tunnel route dns)
  • ✅ 8. Test manuel réussi
  • ✅ 9. Service systemd installé et activé
  • ✅ 10. Firewall verrouillé (UFW)
  • ✅ 11. Cloudflare Access configuré (optionnel)

💡 Astuces et bonnes pratiques

1. Nommez vos tunnels intelligemment

Préférez des noms explicites comme prod-api-fastapi ou staging-dashboard plutôt que tunnel1.

2. Un tunnel par environnement

Séparez production et staging avec des tunnels distincts pour isoler les environnements.

3. Utilisez les variables d'environnement

Évitez de hardcoder les UUID dans vos scripts — utilisez des variables d'environnement pour le tunnel ID et le chemin des credentials.

4. Surveillez vos tunnels

Dans le dashboard Cloudflare → Zero TrustNetworksTunnels, vous pouvez voir l'état de chaque tunnel (Healthy/Degraded/Down), les connexions actives et le trafic en temps réel.

5. Backup de vos credentials

Les fichiers cert.pem et les JSON de credentials sont critiques. Sans eux, vous devrez recréer le tunnel. Faites des sauvegardes régulières.

🆚 Cloudflare Tunnel vs alternatives

Solution Ports ouverts SSL auto DDoS protection Coût Difficulté
Cloudflare Tunnel ❌ 0 Gratuit ⭐⭐
Nginx + Let's Encrypt ✅ 80, 443 Gratuit ⭐⭐⭐
Caddy ✅ 80, 443 Gratuit ⭐⭐
ngrok ❌ 0 Freemium
Tailscale Funnel ❌ 0 Freemium ⭐⭐

Cloudflare Tunnel gagne sur presque tous les critères pour un usage production.

🎯 L'essentiel

  • Cloudflare Tunnel crée une connexion sortante chiffrée entre votre VPS et Cloudflare — aucun port entrant à ouvrir
  • SSL automatique et protection DDoS sont inclus gratuitement
  • La configuration tient en un seul fichier YAML avec des règles ingress par hostname
  • En production, installez cloudflared comme service systemd pour un redémarrage automatique
  • Une fois le tunnel actif, fermez tous les ports du firewall sauf SSH
  • Cloudflare Access (Zero Trust) ajoute une couche d'authentification gratuite pour vos dashboards sensibles

❌ Erreurs courantes

  • Oublier le catch-all : la dernière règle ingress doit toujours être service: http_status:404 sans hostname, sinon la configuration est rejetée
  • Mauvais port dans le service : un 502 Bad Gateway signifie presque toujours que l'application n'écoute pas sur le port indiqué dans config.yml
  • Ne pas copier les credentials : après sudo cloudflared service install, le service lit la config dans /etc/cloudflared/ — pensez à y copier votre config.yml et le fichier JSON
  • HTTPS local sans noTLSVerify : si votre service local utilise HTTPS, ajoutez noTLSVerify: true dans originRequest sinon cloudflared refusera la connexion
  • DNS non routé : si vous obtenez ERR_NAME_NOT_RESOLVED, c'est que la commande cloudflared tunnel route dns n'a pas été exécutée pour le hostname concerné

🔧 Outils recommandés

  • cloudflared : le client officiel Cloudflare Tunnel (gratuit, ~20 Mo de RAM)
  • UFW : firewall simplifié pour verrouiller votre VPS après setup du tunnel
  • systemd : gestionnaire de service pour assurer le redémarrage automatique de cloudflared
  • Cloudflare Zero Trust Dashboard : interface web pour configurer les policies Access, surveiller les tunnels et gérer les utilisateurs
  • Hostinger : hébergeur VPS recommandé pour ce setup, avec un excellent rapport qualité/prix à partir de 3,99€/mois

❓ FAQ

Cloudflare Tunnel est-il vraiment gratuit ?
Oui, le plan gratuit inclut les tunnels illimités, le SSL automatique et la protection DDoS. La seule limite est sur Cloudflare Access (50 utilisateurs gratuits).

Puis-je utiliser Cloudflare Tunnel sans nom de domaine ?
Non, un domaine configuré avec les nameservers Cloudflare est obligatoire pour créer les enregistrements CNAME vers le tunnel.

Le tunnel ajoute-t-il de la latence ?
Très peu. La latence supplémentaire est généralement inférieure à 10 ms si le datacenter Cloudflare le plus proche est bien routé. Vérifiez avec l'endpoint /cdn-cgi/trace.

Que se passe-t-il si cloudflared crash ?
Le service systemd redémarre automatiquement le processus. Pour une surveillance supplémentaire, ajoutez un script de healthcheck en cron.

Puis-je exposer SSH via le tunnel ?
Oui, avec une règle ssh://localhost:22 dans l'ingress et ProxyCommand cloudflared access ssh --hostname %h côté client. Cela permet de fermer le port 22 dans le firewall.

🎯 Conclusion

Cloudflare Tunnel transforme radicalement la façon d'exposer des services :

  • Zéro port ouvert = surface d'attaque minimale
  • SSL automatique = plus jamais de certificats expirés
  • DDoS protection gratuite = tranquillité d'esprit
  • Configuration simple = un fichier YAML et c'est parti
  • Zero Trust = authentification moderne incluse

Combiné avec un VPS Hostinger (performant et abordable), vous avez une infrastructure de production solide pour quelques euros par mois. Si vous souhaitez aller plus loin dans l'auto-hébergement de services IA, consultez notre guide VPS + IA : le setup complet pour tout auto-héberger. Pour optimiser vos appels d'API depuis vos services exposés, notre comparatif APIs IA : OpenRouter vs appels directs vous aidera à choisir la bonne approche. Et si vous conteneurisez vos services, Docker + IA : conteneuriser ses services intelligents complète parfaitement ce setup.
```