🎯 Compétences acquises
Héberger ses propres services web
🛠️ Le Tutoriel Complet
Nginx, SSL & Reverse Proxy sur Ubuntu
La métaphore d'OWL : Le Standard Téléphonique
Imagine un grand immeuble de bureaux. Ton serveur Linux, c'est l'immeuble. Chaque service (Nextcloud, Jellyfin, Portainer) est un bureau différent, à des étages différents. Nginx en reverse proxy, c'est la standardiste à l'accueil : elle reçoit tous les appels entrants (les requêtes HTTP/HTTPS sur le port 80/443) et les redirige vers le bon bureau selon le nom demandé.
Quelqu'un appelle pour nextcloud.mondomaine.fr → La standardiste redirige vers le bureau Nextcloud (port 8080). Pour jellyfin.mondomaine.fr → Bureau Jellyfin (port 8096). De l'extérieur, tout passe par le même numéro (port 443/HTTPS), mais chacun arrive dans le bon bureau.
Let's Encrypt, c'est l'organisme officiel qui offre gratuitement une carte d'identité vérifiée (le certificat SSL) à ton immeuble, prouvant que tu es bien le propriétaire du domaine.
Vue d'ensemble — Architecture Reverse Proxy
Internet
é
— :443 (HTTPS)
?
+----------------------------------------------------+
é NGINX Reverse Proxy (Let's Encrypt SSL) é
é é
é nextcloud.mondomaine.fr ------é :8080 Nextcloud é
é jellyfin.mondomaine.fr ------é :8096 Jellyfin é
é portainer.mondomaine.fr ------é :9000 Portainer é
é monsite.fr ------é /var/www/html é
+----------------------------------------------------+
— — — é
Docker Docker Docker Fichiers
Nextcloud Jellyfin Portainer statiques
1. Installer et Configurer Nginx
sudo apt update && sudo apt install -y nginx
# Démarrer et activer au boot
sudo systemctl enable --now nginx
# Vérifier le statut
sudo systemctl status nginx
é nginx.service - A high performance web server
Active: active (running)
# Autoriser dans UFW
sudo ufw allow 'Nginx Full' # ports 80 + 443
sudo ufw status
# Structure des fichiers Nginx
ls /etc/nginx/
nginx.conf — config principale
sites-available/ — configs disponibles
sites-enabled/ — configs actives (liens symboliques)
conf.d/ — configs additionnelles
snippets/ — fragments réutilisables
# Tester la configuration avant reload
sudo nginx -t
nginx: configuration file /etc/nginx/nginx.conf test is OK
# Recharger après chaque modification
sudo systemctl reload nginx
2. Héberger un Site Web Statique
sudo mkdir -p /var/www/monsite.fr/html
sudo chown -R $USER:$USER /var/www/monsite.fr
# Créer une page de test
echo "<h1>Bienvenue sur MonSite ! ??</h1>" > /var/www/monsite.fr/html/index.html
# Créer la config Nginx pour ce site
sudo nano /etc/nginx/sites-available/monsite.fr
Contenu de /etc/nginx/sites-available/monsite.fr :
listen 80;
listen [::]:80;
server_name monsite.fr www.monsite.fr;
root /var/www/monsite.fr/html;
index index.html index.htm;
location / {
try_files $uri $uri/ =404;
}
# Logs
access_log /var/log/nginx/monsite.fr.access.log;
error_log /var/log/nginx/monsite.fr.error.log;
}
# Activer le site (lien symbolique)
sudo ln -s /etc/nginx/sites-available/monsite.fr /etc/nginx/sites-enabled/
# Désactiver le site par défaut
sudo rm /etc/nginx/sites-enabled/default
# Tester et recharger
sudo nginx -t && sudo systemctl reload nginx
3. HTTPS Gratuit avec Let's Encrypt & Certbot
Certbot est le client officiel Let's Encrypt. Il génére ton certificat SSL, modifie automatiquement ta config Nginx pour activer HTTPS, et met en place le renouvellement automatique.
Prérequis : ton domaine doit pointer vers l'IP de ton serveur via un enregistrement DNS A. Let's Encrypt va vérifier que tu contrôles bien le domaine en accédant à ton serveur via HTTP.
sudo apt install -y certbot python3-certbot-nginx
# Obtenir le certificat (remplace par ton domaine et email)
sudo certbot --nginx -d monsite.fr -d www.monsite.fr \
--email ton@email.fr \
--agree-tos \
--no-eff-email
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Account registered.
Requesting a certificate for monsite.fr...
Successfully deployed certificate for monsite.fr
Congratulations! You have successfully enabled HTTPS
IMPORTANT NOTES:
- Certificate is saved at: /etc/letsencrypt/live/monsite.fr/fullchain.pem
- Key is saved at: /etc/letsencrypt/live/monsite.fr/privkey.pem
- This certificate expires on 2026-07-11.
- These files will be updated when the certificate renews.
# Certbot met à jour automatiquement la config Nginx !
# Il ajoute les lignes SSL et une redirection HTTP?HTTPS
# Tester le renouvellement automatique
sudo certbot renew --dry-run
Simulating renewal of an existing certificate for monsite.fr
Congratulations, all simulated renewals succeeded!
# Le service systemd de renouvellement est actif automatiquement
sudo systemctl status certbot.timer
é certbot.timer - Run certbot twice daily
Active: active (waiting)
4. Config Nginx HTTPS Complète & Sécurisée
Config Nginx optimisée après obtention du certificat SSL, avec les headers de sécurité recommandés :
server {
listen 80;
listen [::]:80;
server_name monsite.fr www.monsite.fr;
return 301 https://$host$request_uri;
}
# Serveur HTTPS principal
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name monsite.fr www.monsite.fr;
# Certificat Let's Encrypt
ssl_certificate /etc/letsencrypt/live/monsite.fr/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/monsite.fr/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
# Headers de sécurité
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# Gzip compression
gzip on;
gzip_types text/plain text/css application/json application/javascript;
root /var/www/monsite.fr/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
access_log /var/log/nginx/monsite.fr.access.log;
error_log /var/log/nginx/monsite.fr.error.log;
}
5. Reverse Proxy — Un Domaine, Plusieurs Services
Le reverse proxy te permet de diriger plusieurs sous-domaines vers différents services qui tournent en local (ou dans Docker). Un seul port 443 ouvert, des dizaines de services accessibles.
Snippet proxy réutilisable (é créer une seule fois)
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400s;
proxy_buffering off;
sudo nano /etc/nginx/snippets/proxy-params.conf
# Copie le contenu ci-dessus, puis tu l'incluras dans chaque vhost
Exemple : portainer.mondomaine.fr — localhost:9000
listen 80;
server_name portainer.mondomaine.fr;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name portainer.mondomaine.fr;
ssl_certificate /etc/letsencrypt/live/mondomaine.fr/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mondomaine.fr/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
location / {
proxy_pass http://localhost:9000;
include /etc/nginx/snippets/proxy-params.conf;
}
}
Exemple : jellyfin.mondomaine.fr — localhost:8096
listen 80;
server_name jellyfin.mondomaine.fr;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name jellyfin.mondomaine.fr;
ssl_certificate /etc/letsencrypt/live/mondomaine.fr/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mondomaine.fr/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
# Taille max upload (utile pour Nextcloud aussi)
client_max_body_size 0;
location / {
proxy_pass http://localhost:8096;
include /etc/nginx/snippets/proxy-params.conf;
}
}
# Activer les deux vhosts et recharger Nginx
sudo ln -s /etc/nginx/sites-available/portainer.mondomaine.fr /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/jellyfin.mondomaine.fr /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
Certificat Wildcard — Un SSL pour tous vos sous-domaines
Plutôt que d'obtenir un certificat par sous-domaine, tu peux obtenir un certificat wildcard (*.mondomaine.fr) qui couvre tous vos sous-domaines. Cela nécessite une validation DNS (pas HTTP) et est compatible avec Cloudflare DNS.
6. Alternative : Nginx Proxy Manager
Si tu préféres une interface graphique pour gérer tes reverse proxies, Nginx Proxy Manager (NPM) est la solution idéale — gestion des vhosts, SSL automatique, redirections, tout depuis une UI web.
npm:
image: jc21/nginx-proxy-manager:latest
container_name: nginx-proxy-manager
restart: unless-stopped
ports:
- "80:80" # HTTP
- "443:443" # HTTPS
- "81:81" # Interface d'administration NPM
volumes:
- npm_data:/data
- letsencrypt:/etc/letsencrypt
volumes:
npm_data:
letsencrypt:
# Déployer
mkdir -p /opt/nginx-proxy-manager && cd /opt/nginx-proxy-manager
docker compose up -d
# Accès admin — http://IP:81
# Login par défaut :
# Email : admin@example.com
# Password : changeme
💡 IMPORTANT : Si Nginx est déjé installé sur le serveur,
# arrête-le avant de démarrer NPM (conflit sur les ports 80/443)
sudo systemctl stop nginx
sudo systemctl disable nginx
Nginx manuel vs Nginx Proxy Manager — Quand choisir
⚙️ Nginx Manuel
Plus de contrôle, configuré en fichiers texte, idéal si tu veux comprendre comment ça fonctionne. Recommandé pour apprendre.
✅ Nginx Proxy Manager
Interface graphique, SSL automatique en 1 clic, gestion rapide. Idéal pour un homelab avec beaucoup de services.
7. Commandes Nginx Essentielles — Antiséche
sudo systemctl start nginx # démarrer
sudo systemctl stop nginx # arrêter
sudo systemctl reload nginx # recharger la config (sans interruption)
sudo systemctl restart nginx # redémarrer complètement
## CONFIGURATION ##
sudo nginx -t # tester la config (TOUJOURS avant reload !)
sudo nginx -T # afficher la config complète dépliée
## SITES ##
sudo ln -s /etc/nginx/sites-available/monsite /etc/nginx/sites-enabled/
# activer un site
sudo rm /etc/nginx/sites-enabled/monsite
# désactiver un site (ne supprime pas l'original)
## LOGS ##
sudo tail -f /var/log/nginx/access.log # accès en temps réel
sudo tail -f /var/log/nginx/error.log # erreurs en temps réel
sudo tail -f /var/log/nginx/monsite.fr.error.log # log d'un site
## SSL (CERTBOT) ##
sudo certbot certificates # lister les certificats
sudo certbot renew --dry-run # tester le renouvellement
sudo certbot renew # renouveler maintenant
sudo certbot delete --cert-name monsite.fr # supprimer un certificat
✅ Comment savoir si ton serveur web est bien configuré
- ✅Nginx est actif et
sudo nginx -tretourne "configuration file test is OK" - ✅Ton site est accessible en HTTPS avec le cadenas vert dans le navigateur (certificat Let's Encrypt valide)
- ✅La redirection HTTP — HTTPS fonctionne automatiquement (accèder en http:// redirige vers https://)
- ✅Un reverse proxy pointe vers au moins un service Docker (ex: portainer.tondomaine.fr accessible en HTTPS)
- ✅
sudo certbot renew --dry-runréussit sans erreur, confirmant le renouvellement automatique SSL
⚠️ Problèmes Courants & Solutions
é Certbot échoue avec "Could not bind to IPv4 or IPv6"
💡 Le port 80 est déjé utilisé par Nginx ou un autre service. Certbot a besoin du port 80 libre pour sa validation HTTP. Arrête temporairement Nginx : sudo systemctl stop nginx, obtiens le certificat, puis redémarre. Avec le plugin --nginx, Certbot gére ça automatiquement.
"502 Bad Gateway" en reverse proxy
💡 Le service vers lequel tu proxies ne répond pas. Vérifie : 1) le conteneur Docker est-il démarré — (docker ps) 2) le port dans proxy_pass est-il correct — 3) si Docker, utilise l'IP du conteneur ou le nom du service rôleau plutôt que localhost qui peut ne pas fonctionner depuis Nginx système.
Mon domaine pointe bien vers le serveur mais Certbot dit que le challenge échoue
💡 Le pare-feu UFW bloque le port 80 entrant. Vérifie : sudo ufw status. Ajoute la règle si nécessaire : sudo ufw allow 'Nginx Full'. Aussi, certains FAI bloquent les ports 80/443 en entrée — dans ce cas, utilise la validation DNS.
"413 Request Entity Too Large" lors d'upload vers Nextcloud ou Jellyfin
💡 Nginx limite la taille des fichiers uploadés. Ajoute dans le bloc server ou location : client_max_body_size 0; (illimité) ou une valeur spécifique comme client_max_body_size 10G;. Puis recharge Nginx : sudo systemctl reload nginx.