
Nginx reverse proxy : configuration avancée et hardening
Configuration complète de Nginx en reverse proxy : load balancing, headers de sécurité, cache, rate limiting et hardening. Guide technique avec fichiers complets.
Nginx reverse proxy : configuration avancée et hardening
Ce guide couvre la configuration production-ready de Nginx comme reverse proxy. On part du principe que vous avez un serveur Ubuntu 22.04/24.04 ou Debian 12, un accès root, et une ou plusieurs applications à exposer derrière Nginx.
On ne va pas se contenter d'un proxy_pass basique. On couvre le hardening, le load balancing, le cache, le rate limiting et les headers de sécurité.
Installation depuis le dépôt officiel Nginx
Les dépôts Ubuntu fournissent souvent une version datée. Pour avoir les dernières fonctionnalités et correctifs de sécurité, ajoutez le dépôt officiel Nginx :
## Installer les prérequis
sudo apt install curl gnupg2 ca-certificates lsb-release ubuntu-keyring -y
## Importer la clé GPG officielle Nginx
curl https://nginx.org/keys/nginx_signing.key | gpg —dearmor | sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
## Ajouter le dépôt stable
echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] http://nginx.org/packages/ubuntu $(lsb_release -cs) nginx" | sudo tee /etc/nginx/sources.list.d/nginx.list
## Prioriser le dépôt officiel sur celui d'Ubuntu
echo -e "Package: *\nPin: origin nginx.org\nPin-Priority: 900" | sudo tee /etc/apt/preferences.d/99nginx
## Installer
sudo apt update
sudo apt install nginx -y
## Vérifier la version
nginx -vSur Ubuntu 24.04, le paquet des dépôts standard fournit Nginx 1.24.x. Le dépôt officiel vous donne accès aux versions 1.26+ avec les derniers correctifs.
Configuration de base durcie
Avant de configurer les virtual hosts, on durcit la configuration globale.
Éditez /etc/nginx/nginx.conf :
user www-data;
worker_processes auto;
pid /run/nginx.pid;
error_log /var/log/nginx/error.log warn;
events {
worker_connections 1024;
multi_accept on;
}
http {
# —- Performance —-
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
client_max_body_size 50m;
# —- Sécurité : masquer la version —-
server_tokens off;
# —- Types MIME —-
include /etc/nginx/mime.types;
default_type application/octet-stream;
# —- Logging —-
log_format main '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'$request_time $upstream_response_time';
access_log /var/log/nginx/access.log main;
# —- Gzip —-
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 4;
gzip_min_length 1000;
gzip_types text/plain text/css text/xml application/json
application/javascript application/xml+rss
application/atom+xml image/svg+xml;
# —- Rate limiting (zones globales) —-
limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=api:10m rate=30r/s;
limit_conn_zone $binary_remote_addr zone=addr:10m;
# —- Headers de sécurité globaux —-
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# —- Includes —-
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}Reverse proxy : configuration complète
Créez le fichier pour votre application :
sudo nano /etc/nginx/sites-available/app.mondomaine.frContenu complet :
## Upstream : définit le(s) backend(s)
upstream app_backend {
server 127.0.0.1:3000;
# Pour du load balancing, ajoutez d'autres serveurs :
# server 127.0.0.1:3001;
# server 127.0.0.1:3002;
# Garder les connexions ouvertes vers le backend
keepalive 32;
}
## Redirection HTTP → HTTPS
server {
listen 80;
listen [::]:80;
server_name app.mondomaine.fr;
return 301 https://$host$request_uri;
}
## Configuration HTTPS principale
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name app.mondomaine.fr;
# —- SSL (sera rempli par Certbot ou manuellement) —-
ssl_certificate /etc/letsencrypt/live/app.mondomaine.fr/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.mondomaine.fr/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# —- HSTS —-
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# —- Headers de sécurité —-
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "0" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
# —- Rate limiting —-
limit_req zone=general burst=20 nodelay;
limit_conn addr 50;
# —- Logs dédiés —-
access_log /var/log/nginx/app.mondomaine.fr.access.log main;
error_log /var/log/nginx/app.mondomaine.fr.error.log warn;
# —- Proxy vers le backend —-
location / {
proxy_pass http://app_backend;
proxy_http_version 1.1;
# Headers standards
proxy_set_header Host $host;
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_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
# WebSocket support
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Keepalive vers l'upstream
proxy_set_header Connection "";
# Timeouts
proxy_connect_timeout 10s;
proxy_send_timeout 30s;
proxy_read_timeout 60s;
# Buffering
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 16k;
}
# —- Assets statiques (si servis par Nginx) —-
location /static/ {
alias /var/www/app/static/;
expires 30d;
add_header Cache-Control "public, immutable";
access_log off;
}
# —- Bloquer les fichiers sensibles —-
location ~ /\.(git|env|htaccess|htpasswd) {
deny all;
return 404;
}
# —- Page d'erreur custom —-
error_page 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
internal;
}
}Note sur WebSocket et keepalive : les deux directives Connection dans l'exemple ci-dessus sont mutuellement exclusives. Si votre application utilise des WebSockets, gardez le bloc WebSocket (Upgrade + Connection "upgrade"). Sinon, utilisez Connection "" pour le keepalive HTTP. Pour supporter les deux cas, utilisez une map :
## Dans le bloc http {} de nginx.conf
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
## Puis dans le location :
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;Activation et test
## Activer le site
sudo ln -s /etc/nginx/sites-available/app.mondomaine.fr /etc/nginx/sites-enabled/
## Supprimer la config par défaut si elle existe encore
sudo rm -f /etc/nginx/sites-enabled/default
## Tester la syntaxe
sudo nginx -t
## Recharger sans interruption
sudo systemctl reload nginxObtenir le certificat SSL avec Certbot
Si vous n'avez pas encore le certificat :
## Installer Certbot
sudo apt install certbot python3-certbot-nginx -y
## Obtenir le certificat
sudo certbot —nginx -d app.mondomaine.fr
## Vérifier le timer de renouvellement
sudo systemctl list-timers | grep certbot
## Test de renouvellement
sudo certbot renew —dry-runSi vous préférez configurer le SSL manuellement (sans laisser Certbot modifier votre config) :
## Obtenir le certificat sans modifier Nginx
sudo certbot certonly —webroot -w /var/www/html -d app.mondomaine.frLoad balancing multi-backends
Pour répartir la charge entre plusieurs instances :
upstream app_backend {
# Round-robin par défaut
server 127.0.0.1:3000 weight=3;
server 127.0.0.1:3001 weight=2;
server 127.0.0.1:3002 backup;
# Alternatives au round-robin :
# least_conn; # envoie vers le backend le moins chargé
# ip_hash; # session sticky basée sur l'IP client
keepalive 64;
}Pour du health checking passif (inclus dans la version open source) :
upstream app_backend {
server 127.0.0.1:3000 max_fails=3 fail_timeout=30s;
server 127.0.0.1:3001 max_fails=3 fail_timeout=30s;
keepalive 64;
}Cache reverse proxy
Accélérer les réponses en cachant les résultats côté Nginx :
## Dans http {} de nginx.conf
proxy_cache_path /var/cache/nginx/app levels=1:2
keys_zone=app_cache:10m max_size=1g inactive=60m
use_temp_path=off;
## Dans le server {} ou location {}
location / {
proxy_pass http://app_backend;
proxy_cache app_cache;
proxy_cache_valid 200 10m;
proxy_cache_valid 404 1m;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503;
proxy_cache_lock on;
add_header X-Cache-Status $upstream_cache_status;
}Créez le dossier de cache :
sudo mkdir -p /var/cache/nginx/app
sudo chown www-data:www-data /var/cache/nginx/appBloquer les bots et user-agents indésirables
## Dans http {} ou dans un fichier inclus
map $http_user_agent $bad_bot {
default 0;
~*semrush 1;
~*ahrefs 1;
~*mj12bot 1;
~*dotbot 1;
~*petalbot 1;
~*bytespider 1;
}
## Dans le server {}
if ($bad_bot) {
return 403;
}Script de déploiement automatisé
Pour automatiser la création d'un nouveau reverse proxy :
À lire aussi
Intel sort de nouveaux processeurs pour les PC portables gamin...
Intel lance les Core Ultra 9 290HX Plus et Core Ultra 7 270HX Plus, ses processeurs les plus rapides pour les PC portables gaming.
Kagi Small Web : 30 000 sites indexés, apps natives et filtra...
Kagi déploie ses apps Small Web iOS et Android avec index curé de 30 000 sites indépendants, filtrage par catégorie et mode lecture sans distraction.
Kagi Small Web : redécouvrir un internet fait par des humains
Kagi lance ses apps mobiles Small Web sur iOS et Android pour explorer 30 000 sites indépendants créés par de vraies personnes, loin du contenu généré par IA.