Maîtriser Nginx comme reverse proxy, load balancer et serveur web. Configuration production pour sécuriser vos APIs et applications.
Introduction à Nginx et ses use cases
Nginx est un serveur web haute performance et léger, écrit en C. Contrairement à Apache, Nginx utilise une architecture asynchrone et non-bloquante, ce qui le rend idéal pour gérer des milliers de connexions simultanées.
Use cases principaux :
- Reverse proxy : afficher vos APIs Node.js, Go, Python derrière Nginx
- Load balancing : distribuer le trafic entre plusieurs serveurs applicatifs
- Serveur web statique : servir HTML/CSS/JS ultra-rapide
- SSL/TLS termination : gérer HTTPS centralement
- Compression : gzip les réponses pour réduire la bande passante
- Caching : mettre en cache les réponses pour réduire la charge backend
Installation et configuration de base
Installation (Ubuntu/Debian) :
sudo apt update
sudo apt install nginx
# Démarrer le service
sudo systemctl start nginx
sudo systemctl enable nginx # Auto start on boot
# Vérifier le statut
sudo systemctl status nginx
Configuration de base :
Le fichier principal est : /etc/nginx/nginx.conf
worker_processes auto; # Nombre de workers = nombre de CPU
worker_connections 1024; # Connexions max par worker
keepalive_timeout 65; # Délai avant fermeture connexion inactif
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Format des logs
log_format main '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log warn;
# Include tous les configs de sites
include /etc/nginx/conf.d/*.conf;
}
/etc/nginx/: fichiers config/var/www/html: fichiers statiques par défaut/var/log/nginx/: access/error logs
Recharger la config sans redémarrer :
sudo nginx -t # Tester la syntaxe
sudo systemctl reload nginx # Appliquer les changements
Virtual hosts et Server blocks
Les server blocks dans Nginx (équivalent aux vhosts Apache) permettent d'héberger plusieurs sites sur un même serveur.
Structure recommandée :
/etc/nginx/
├── nginx.conf (config principale)
├── conf.d/
│ ├── app1.conf
│ ├── app2.conf
│ └── ...
└── sites-available/
├── app1
└── app2
Exemple simple - app1.conf :
server {
listen 80;
server_name app1.example.com;
root /var/www/app1;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
# Logs spécifiques au site
access_log /var/log/nginx/app1_access.log;
error_log /var/log/nginx/app1_error.log;
}
Exemple avec plusieurs domaines :
server {
listen 80;
server_name app.example.com www.app.example.com;
# Servir sur plusieurs domaines
location / {
proxy_pass http://localhost:3000;
}
}
Reverse proxy et load balancing
Reverse proxy simple (1 backend) :
server {
listen 80;
server_name api.example.com;
location / {
# Rediriger les requêtes vers le backend
proxy_pass http://localhost:3000;
# Headers importants pour le backend
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;
# Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
}
Load balancing avec plusieurs backends :
upstream app_backend {
server 192.168.1.10:3000 weight=5; # 50% du trafic
server 192.168.1.11:3000 weight=3; # 30% du trafic
server 192.168.1.12:3000 weight=2; # 20% du trafic
}
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://app_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Stratégies de load balancing :
round_robin(défaut) : distribue équitablementleast_conn: envoie au serveur avec moins de connexionsip_hash: même IP = même serveur (session persistance)random: aléatoire
upstream app_backend {
least_conn; # Utiliser cette stratégie
server 192.168.1.10:3000;
server 192.168.1.11:3000;
}
Gestion des certificats SSL/TLS
Installer Let's Encrypt avec Certbot :
sudo apt install certbot python3-certbot-nginx
# Générer un certificat
sudo certbot certonly --nginx -d api.example.com -d www.api.example.com
# Le certificat se trouve à :
# /etc/letsencrypt/live/api.example.com/
Configuration HTTPS :
server {
listen 443 ssl http2;
server_name api.example.com;
# Certificat SSL
ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;
# Configuration SSL sécurisée
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# HSTS : force HTTPS pendant 1 an
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
location / {
proxy_pass http://localhost:3000;
}
}
# Redirection HTTP vers HTTPS
server {
listen 80;
server_name api.example.com;
return 301 https://$server_name$request_uri;
}
Renouveler automatiquement les certificats :
sudo certbot renew --dry-run # Test
sudo certbot renew # Renouveler
Headers de sécurité avec Nginx
Headers essentiels pour les APIs :
server {
listen 443 ssl http2;
server_name api.example.com;
# Security Headers
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 "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), microphone=()" always;
# Content Security Policy (stricte pour les APIs)
add_header Content-Security-Policy "default-src 'none'; frame-ancestors 'none'" always;
location / {
proxy_pass http://app_backend;
# Headers pour le backend
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Blocage des requêtes dangereuses :
server {
listen 80;
server_name api.example.com;
# Bloquer les robots malveillants
if ($http_user_agent ~* (bot|crawler|spider|scraper)) {
return 403;
}
# Bloquer les requêtes sans User-Agent
if ($http_user_agent = "") {
return 403;
}
location / {
proxy_pass http://app_backend;
}
}
Cache et compression
Compression Gzip :
http {
# Activer la compression
gzip on;
gzip_vary on;
gzip_min_length 1000;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
gzip_comp_level 6;
gzip_disable "msie6";
}
Cache pour fichiers statiques :
server {
listen 80;
server_name cdn.example.com;
root /var/www/cdn;
# Cache les fichiers statiques 30 jours
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
# Ne pas cacher l'HTML
location ~* \.html$ {
expires -1;
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
}
Cache proxy (microservices) :
http {
# Zone de cache partagée
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=api_cache:10m max_size=1g inactive=60m;
}
server {
location /api/ {
proxy_pass http://app_backend;
# Mettre en cache les GET pendant 10 min
proxy_cache api_cache;
proxy_cache_methods GET HEAD;
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
# Afficher d'où vient la réponse
add_header X-Cache-Status $upstream_cache_status;
}
}
Cache-Control: public, max-age=3600: cacheable 1hCache-Control: no-cache, must-revalidate: valider à chaque foisExpires: Wed, 21 Apr 2026 12:00:00 GMT: date d'expiration
Monitoring et logs
Format des logs personnalisé :
http {
log_format api_log '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'$request_time '
'"$http_referer" "$http_user_agent" '
'upstream: $upstream_addr '
'cache: $upstream_cache_status';
access_log /var/log/nginx/api_access.log api_log;
}
Analyser les logs en temps réel :
# Requêtes les plus lentes
tail -f /var/log/nginx/access.log | sort -k10 -nr | head
# Codes HTTP les plus fréquents
awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -nr
# IPs avec le plus de requêtes
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head
# Erreurs (5xx)
grep " 5[0-9][0-9] " /var/log/nginx/access.log | tail -20
Stats Nginx en direct :
server {
listen 8080;
server_name localhost;
location /nginx_status {
stub_status on;
access_log off;
allow 127.0.0.1;
deny all;
}
}
# Accéder aux stats
curl http://localhost:8080/nginx_status
gzip /var/log/nginx/*.log
Bonnes pratiques production
1. Limite le nombre de workers et connexions :
worker_processes auto; # = nombre CPU
worker_rlimit_nofile 65535; # Fichiers ouverts max
events {
worker_connections 4096; # 2000-4000 en prod
}
2. Désactiver les infos de version :
http {
server_tokens off; # Cache le numéro de version Nginx
}
3. Timeouts appropriés :
http {
client_body_timeout 10s;
client_header_timeout 10s;
keepalive_timeout 5s 5s;
send_timeout 10s;
}
4. Limiter la taille des uploads :
http {
client_max_body_size 10m; # Max 10 MB par requête
}
5. Monitoring actif et alertes :
# Vérifier que Nginx tourne
ps aux | grep nginx
# Utilité CPU/Mémoire
top -p $(pgrep -d',' nginx)
# Nombre de connexions actives
netstat -tupan | grep ESTABLISHED | wc -l
6. Configuration par environnement :
/etc/nginx/
├── nginx.conf
├── conf.d/
│ ├── common.conf # Commun (dev + prod)
│ ├── dev.conf # Dev (permissif)
│ └── prod.conf # Prod (strict)
└── snippets/
├── security.conf
├── cache.conf
└── ssl.conf
7. Tester avant de redéployer :
sudo nginx -t # Valider la config
sudo nginx -s reload # Recharger proprement
sudo tail -f /var/log/nginx/error.log # Vérifier pas d'erreur