Nginx comme reverse proxy : exposer ses services sur internet
Qu'est-ce qu'un reverse proxy ?
Un reverse proxy est un serveur qui se place entre internet et tes applications. Il reçoit toutes les requêtes entrantes et les redirige vers le bon service en fonction de l'URL.
Sans reverse proxy, chaque service doit être exposé directement sur internet avec son propre port :
mondomaine.com:3000pour le frontendmondomaine.com:8000pour le backendmondomaine.com:5432pour la base de données
C'est non seulement peu élégant mais surtout dangereux. Avec un reverse proxy, un seul point d'entrée sur les ports 80 et 443 gère tout le trafic.
Pourquoi Nginx ?
Nginx est l'un des serveurs web les plus utilisés au monde. Il est léger, performant et peut jouer plusieurs rôles :
- Serveur web statique
- Reverse proxy
- Load balancer
- Terminaison SSL
Dans notre cas, on l'utilise comme reverse proxy pour exposer nos containers Docker sur internet.
Installation
sudo apt update && sudo apt install -y nginx
sudo systemctl start nginx
sudo systemctl enable nginx
Vérifier que Nginx tourne :
sudo systemctl status nginx
Configuration de base
Les fichiers de configuration Nginx sont dans /etc/nginx/. La structure est la suivante :
/etc/nginx/nginx.conf: la configuration globale/etc/nginx/sites-available/: les configurations des sites disponibles/etc/nginx/sites-enabled/: les liens symboliques vers les sites actifs
Pour créer une nouvelle configuration :
sudo nano /etc/nginx/sites-available/monapp
Configurer le reverse proxy
server {
listen 80;
server_name mondomaine.com www.mondomaine.com;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location /api/ {
proxy_pass http://localhost:8000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Explications :
listen 80: Nginx écoute sur le port 80server_name: le domaine pour lequel cette configuration s'appliquelocation /: toute requête vers la racine est redirigée vers le frontend sur le port 3000location /api/: toute requête vers/api/est redirigée vers le backend sur le port 8000proxy_set_header: transmet les headers HTTP originaux au service cible
Activer la configuration :
sudo ln -s /etc/nginx/sites-available/monapp /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
nginx -t vérifie la syntaxe de la configuration avant de l'appliquer. Ne jamais recharger Nginx sans cette vérification.
Bloquer l'accès par IP directe
Sans cette mesure, n'importe qui peut accéder à ton application en tapant directement l'IP du serveur dans son navigateur, en contournant ton nom de domaine et potentiellement ton SSL.
server {
listen 80 default_server;
server_name _;
return 444;
}
Le code 444 est spécifique à Nginx. Il ferme la connexion sans envoyer de réponse. Ce bloc doit être placé avant la configuration principale.
Masquer les informations techniques
Par défaut, Nginx expose sa version dans les headers HTTP. Un attaquant peut utiliser cette information pour cibler des vulnérabilités connues de cette version.
Dans /etc/nginx/nginx.conf, dans le bloc http {} :
server_tokens off;
Ajouter les headers de sécurité
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_hide_header X-Powered-By;
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options DENY;
add_header X-XSS-Protection "1; mode=block";
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self' https://mondomaine.com;" always;
}
proxy_hide_header X-Powered-By: masque le framework utilisé par le backendX-Content-Type-Options nosniff: empêche le navigateur de deviner le type MIME d'une réponseX-Frame-Options DENY: empêche l'intégration du site dans une iframe, protège contre le clickjackingX-XSS-Protection: active la protection XSS du navigateurStrict-Transport-Security: force le navigateur à utiliser HTTPS pendant 1 an, même si l'utilisateur tape HTTP.includeSubDomainsapplique la règle aussi aux sous-domainesContent-Security-Policy: définit les sources de contenu autorisées.default-src 'self'n'autorise que les ressources du même domaine. Les exceptions pourunsafe-inlineetunsafe-evalsont nécessaires pour Next.js
Limiter le nombre de requêtes
Le rate limiting protège contre les attaques DDoS et le bruteforce en limitant le nombre de requêtes par IP.
Dans /etc/nginx/nginx.conf :
http {
limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=api:10m rate=5r/s;
}
Dans la configuration du site :
location / {
limit_req zone=general burst=20 nodelay;
proxy_pass http://localhost:3000;
}
location /api/ {
limit_req zone=api burst=10 nodelay;
proxy_pass http://localhost:8000/;
}
zone=general:10m: zone de 10MB en mémoire pour stocker les compteurs par IPrate=10r/s: maximum 10 requêtes par secondeburst=20: autorise un burst de 20 requêtes avant d'appliquer le rate limitingnodelay: rejette immédiatement les requêtes qui dépassent la limite
Configurer le SSL avec Certbot
Certbot est un outil qui automatise l'obtention et le renouvellement de certificats SSL gratuits via Let's Encrypt.
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d mondomaine.com -d www.mondomaine.com
Certbot modifie automatiquement la configuration Nginx pour activer HTTPS et configure le renouvellement automatique du certificat.
Vérifier le renouvellement automatique :
sudo certbot renew --dry-run
La configuration finale
Après toutes ces étapes, voici la configuration complète :
server {
listen 80 default_server;
server_name _;
return 444;
}
server {
listen 80;
server_name mondomaine.com www.mondomaine.com;
location / {
limit_req zone=general burst=20 nodelay;
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_hide_header X-Powered-By;
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options DENY;
add_header X-XSS-Protection "1; mode=block";
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self' https://mondomaine.com;" always;
}
location /api/ {
limit_req zone=api burst=10 nodelay;
proxy_pass http://localhost:8000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_hide_header X-Powered-By;
}
}
Vérifier les headers en place
Une fois la configuration rechargée, tu peux vérifier que tous les headers sont bien présents :
curl -I https://mondomaine.com
Tu dois voir dans la réponse :
X-Content-Type-Options: nosniff X-Frame-Options: DENY X-XSS-Protection: 1; mode=block Strict-Transport-Security: max-age=31536000; includeSubDomains Content-Security-Policy: default-src 'self'; ...
Conclusion
Nginx comme reverse proxy centralise tout le trafic entrant, sécurise l'accès aux services et simplifie la gestion du SSL. C'est un composant indispensable dans toute infrastructure de production moderne.
Dans l'article suivant, on sécurise MongoDB pour protéger les données de l'application.