Fait partie de notre série Performance & Scalability
Lire le guide completConfiguration de production Nginx : SSL, mise en cache et sécurité
Nginx est le serveur Web de production qui gère pratiquement tous les déploiements Web à fort trafic. Lorsqu'il est bien configuré, il fournit une terminaison SSL, un service de fichiers statiques efficace, un proxy WebSocket, une limitation de débit et des en-têtes de sécurité, le tout avant qu'une seule requête n'atteigne votre application Node.js. Lorsqu'il est mal configuré, il devient un goulot d'étranglement, un problème de sécurité ou la cause de mystérieuses boucles de redirection Cloudflare.
Ce guide couvre une configuration Nginx de niveau production pour un déploiement Node.js multi-applications : interface Next.js, API NestJS et documents Docusaurus, tous exécutés sur le même serveur derrière Cloudflare.
Points clés à retenir
- Ne divisez jamais www et non-www en blocs de serveur distincts derrière Cloudflare - cela provoque des boucles de redirection
- Utilisez un seul fichier de configuration Nginx dans
/etc/nginx/conf.d/— ne créez pas également de lien symbolique danssites-enabled/X-XSS-Protectionest obsolète — utilisez plutôt CSP ;X-Frame-Options: DENYest toujours valideproxy_passdoit inclure la barre oblique finale lors de l'utilisation d'un préfixe de chemin- La compression Gzip pour les types texte/* MIME vaut toujours la peine d'être activée
- La limitation de débit nécessite
limit_req_zonedéfini au niveauhttp{}, et non au niveauserver{}- Les certificats Let's Encrypt se renouvellent automatiquement avec Certbot ; ajouter une tâche cron pour recharger Nginx après le renouvellement
- Le proxy WebSocket nécessite des en-têtes spécifiques :
UpgradeetConnection
Structure du répertoire
Gardez la configuration Nginx organisée :
/etc/nginx/
nginx.conf — Main config (rarely touch this)
conf.d/
ecosire-production.conf — All your server blocks in ONE file
snippets/
ssl-params.snippet — SSL hardening (included by server blocks)
proxy-params.snippet — Common proxy headers
security-headers.snippet — Security headers
Critique : mettez toute votre configuration dans conf.d/ecosire-production.conf. Ne l'ajoutez PAS également à sites-enabled/ - cela oblige Nginx à traiter la configuration deux fois, ce qui entraîne des erreurs limit_req_zone en double et un comportement inattendu.
Zones de limitation de débit
Définissez des zones de limitation de débit au niveau http{} dans votre configuration. Ils ne peuvent pas être définis à l’intérieur des blocs server{} :
# /etc/nginx/conf.d/ecosire-production.conf
# Must be at http{} level — these are in the main conf or conf.d root
limit_req_zone $binary_remote_addr zone=api_general:10m rate=60r/m;
limit_req_zone $binary_remote_addr zone=api_auth:10m rate=10r/m;
limit_req_zone $binary_remote_addr zone=api_public:10m rate=30r/m;
limit_req_zone $binary_remote_addr zone=static_files:10m rate=200r/m;
Bloc du serveur d'application principal
# Main web application — Next.js on port 3000
server {
listen 80;
listen [::]:80;
server_name ecosire.com www.ecosire.com;
# Cloudflare handles SSL termination — Nginx only sees HTTP
# (If direct SSL, add listen 443 ssl and certificate paths)
# Security headers
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
# CSP — adjust based on your needs
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://js.stripe.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; frame-src https://js.stripe.com; connect-src 'self' https://api.ecosire.com;" always;
# Remove server version from responses
server_tokens off;
# Gzip compression
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types
text/plain
text/css
text/javascript
application/javascript
application/json
application/xml
image/svg+xml
font/woff2;
gzip_min_length 1024;
# ─── Static Files: Next.js build output ─────────────────────────
location /_next/static/ {
proxy_pass http://127.0.0.1:3000;
add_header Cache-Control "public, max-age=31536000, immutable";
# Files include content hash in filename — safe to cache forever
}
# ─── Public static assets ────────────────────────────────────────
location /assets/ {
proxy_pass http://127.0.0.1:3000;
add_header Cache-Control "public, max-age=86400"; # 1 day
}
# ─── Well-known files (no locale prefix) ─────────────────────────
location /.well-known/ {
proxy_pass http://127.0.0.1:3000;
add_header Cache-Control "public, max-age=86400";
}
# ─── App routes (rate limited) ───────────────────────────────────
location / {
limit_req zone=api_general burst=20 nodelay;
limit_req_status 429;
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
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_cache_bypass $http_upgrade;
# Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
}
Blocage du serveur API
# NestJS API — port 3001
server {
listen 80;
listen [::]:80;
server_name api.ecosire.com;
server_tokens off;
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# CORS — handled by NestJS, but Nginx can add preflight response
# for performance (avoids reaching Node.js for OPTIONS)
location / {
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin "https://ecosire.com";
add_header Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS";
add_header Access-Control-Allow-Headers "Content-Type, Authorization";
add_header Access-Control-Allow-Credentials "true";
add_header Access-Control-Max-Age 1728000;
add_header Content-Length 0;
return 204;
}
limit_req zone=api_general burst=30 nodelay;
proxy_pass http://127.0.0.1:3001;
proxy_http_version 1.1;
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_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# Stricter rate limits for auth endpoints
location ~ ^/api/auth/(login|exchange|callback) {
limit_req zone=api_auth burst=5 nodelay;
limit_req_status 429;
proxy_pass http://127.0.0.1:3001;
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;
}
# Health check — no rate limiting
location /api/health {
proxy_pass http://127.0.0.1:3001;
proxy_set_header Host $host;
access_log off; # Don't log health check spam
}
# Stripe webhook — needs raw body
location /api/billing/webhook {
proxy_pass http://127.0.0.1:3001;
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;
# No rate limiting on webhooks — Stripe IPs are trusted
# NestJS validates the Stripe signature
}
}
Proxy WebSocket
Si votre application utilise des WebSockets (Socket.IO, passerelle NestJS WebSocket), la configuration du proxy nécessite des en-têtes spécifiques :
# WebSocket upgrade handling
location /ws/ {
proxy_pass http://127.0.0.1:3001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade"; # Capital U required
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# WebSocket connections can be long-lived
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
Sans les en-têtes Upgrade et Connection, la négociation WebSocket du navigateur échoue avec une erreur 426 Upgrade Required ou 101 Switching Protocols.
SSL direct (sans Cloudflare)
Si vous n'utilisez pas Cloudflare, gérez la résiliation SSL directement dans Nginx :
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name ecosire.com;
ssl_certificate /etc/letsencrypt/live/ecosire.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ecosire.com/privkey.pem;
# SSL hardening
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# ... rest of server block
}
# HTTP to HTTPS redirect
server {
listen 80;
listen [::]:80;
server_name ecosire.com www.ecosire.com;
return 301 https://ecosire.com$request_uri;
}
Configuration spécifique à Cloudflare
Derrière Cloudflare, Nginx ne voit que les IP de Cloudflare. Pour conserver les adresses IP réelles des clients :
# Real IP from Cloudflare — add this in http{} or at top of server{}
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 104.16.0.0/13;
set_real_ip_from 104.24.0.0/14;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 131.0.72.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 2400:cb00::/32;
set_real_ip_from 2606:4700::/32;
set_real_ip_from 2803:f800::/32;
set_real_ip_from 2405:b500::/32;
set_real_ip_from 2405:8100::/32;
set_real_ip_from 2a06:98c0::/29;
set_real_ip_from 2c0f:f248::/32;
real_ip_header CF-Connecting-IP;
De plus : ne divisez jamais www.ecosire.com et ecosire.com en blocs de serveur Nginx distincts lorsque vous utilisez Cloudflare. Cloudflare gère la redirection www à sa périphérie. Si vous ajoutez un bloc de serveur www qui redirige vers un site non-www et que Cloudflare redirige également www, vous obtenez une boucle de redirection (ERR_TOO_MANY_REDIRECTS).
Configuration du journal
# Custom log format with useful fields
log_format json_combined escape=json
'{'
'"time":"$time_iso8601",'
'"remote_addr":"$remote_addr",'
'"cf_ip":"$http_cf_connecting_ip",'
'"method":"$request_method",'
'"uri":"$request_uri",'
'"status":$status,'
'"body_bytes":$body_bytes_sent,'
'"response_time":$request_time,'
'"referrer":"$http_referer",'
'"user_agent":"$http_user_agent"'
'}';
access_log /var/log/nginx/ecosire-access.log json_combined;
error_log /var/log/nginx/ecosire-error.log warn;
Test de votre configuration
# Test syntax before applying
nginx -t
# Reload without downtime
nginx -s reload
# Check which config file is active
nginx -T | grep "configuration file"
# Test a specific server block
curl -I https://ecosire.com
curl -I https://api.ecosire.com/api/health
# Check security headers
curl -I https://ecosire.com | grep -E "X-Frame|X-Content|Referrer|Content-Security"
Questions fréquemment posées
Dois-je utiliser Nginx ou Caddy pour un nouveau projet ?
Caddy est nettement plus simple à configurer : il gère automatiquement Let's Encrypt SSL et possède des paramètres par défaut raisonnables. Nginx est plus puissant et dispose d’un plus grand écosystème de modules et de documentation. Pour la plupart des nouveaux projets, Caddy est le meilleur point de départ ; passez à Nginx si vous avez besoin d'un contrôle précis sur SSL, d'un routage en amont complexe ou de modules spécifiques à Nginx. Pour les serveurs Linux existants sur lesquels Nginx est déjà installé, restez fidèle à Nginx.
Comment éviter l'erreur limit_req_zone en double ?
La directive limit_req_zone doit apparaître au niveau du contexte http{}, et non à l'intérieur des blocs server{}. Dans un déploiement monorepo Turborepo dans lequel vous incluez plusieurs fichiers de configuration, assurez-vous que la directive n'apparaît qu'une seule fois dans tous les fichiers inclus. Si vous voyez l'erreur, vérifiez si vous avez à la fois un lien symbolique conf.d/app.conf et un lien symbolique sites-enabled/ pointant vers le même fichier.
Comment configurer Nginx pour Next.js ISR (régénération statique incrémentielle) ?
Next.js gère ISR en interne – Nginx a simplement besoin de transmettre par proxy toutes les requêtes à Next.js sans mettre en cache les réponses. N'ajoutez pas d'en-têtes Cache-Control qui interfèrent avec les en-têtes de cache ISR de Next.js. Pour les ressources statiques dans /_next/static/, ajoutez les en-têtes de cache immutable car ces fichiers ont des noms hachés au contenu. Pour toutes les autres routes, laissez Next.js définir les en-têtes de cache.
Pourquoi mon score SSL diminue-t-il lorsque Cloudflare est activé ?
Lorsque Cloudflare est en mode proxy, SSL Labs teste le SSL de Cloudflare, pas le vôtre. Définissez le mode SSL de Cloudflare sur « Complet (Strict) » pour garantir que Cloudflare valide votre certificat d'origine. Installez un certificat Cloudflare Origin sur votre serveur : c'est gratuit et valable 15 ans. Votre score SSL Labs devient le score de Cloudflare (généralement A+), ce qui constitue en fait une amélioration par rapport à une configuration SSL Nginx auto-configurée typique.
Comment gérer plusieurs applications Node.js sur le même serveur ?
Exécutez chaque application sur un port différent (Next.js sur 3000, NestJS sur 3001, Docusaurus sur 3002, etc.) et ajoutez un bloc serveur pour chaque sous-domaine dans votre configuration Nginx. Utilisez PM2 pour gérer tous les processus Node.js. Nginx achemine par sous-domaine (ou préfixe de chemin) vers le bon port. Chaque bloc de serveur peut avoir sa propre configuration de limitation de débit, de mise en cache et d'en-tête de sécurité.
Prochaines étapes
Une configuration de production Nginx est un document évolutif : elle évolue à mesure que les exigences de votre application changent, que vos modèles de trafic évoluent et que de nouvelles bonnes pratiques de sécurité émergent. ECOSIRE exécute Nginx en production au service de plusieurs applications, gérant la terminaison SSL, la limitation de débit et l'intégration Cloudflare dans tous les domaines.
Que vous ayez besoin de conseils DevOps, de configuration d'une infrastructure de production ou d'une conception d'architecture de déploiement complète, explorez nos services pour voir comment nous pouvons vous aider.
Rédigé par
ECOSIRE Research and Development Team
Création de produits numériques de niveau entreprise chez ECOSIRE. Partage d'analyses sur les intégrations Odoo, l'automatisation e-commerce et les solutions d'entreprise propulsées par l'IA.
Articles connexes
API Rate Limiting: Patterns and Best Practices
Master API rate limiting with token bucket, sliding window, and fixed counter patterns. Protect your backend with NestJS throttler, Redis, and real-world configuration examples.
Cybersecurity Trends 2026-2027: Zero Trust, AI Threats, and Defense
The definitive guide to cybersecurity trends for 2026-2027—AI-powered attacks, zero trust implementation, supply chain security, and building resilient security programs.
Financial Services ERP Implementation: Regulatory and Security Requirements
A practitioner's guide to implementing ERP in regulated financial services firms, covering security controls, compliance validation, data governance, and phased rollout.
Plus de Performance & Scalability
k6 Load Testing: Stress-Test Your APIs Before Launch
Master k6 load testing for Node.js APIs. Covers virtual user ramp-ups, thresholds, scenarios, HTTP/2, WebSocket testing, Grafana dashboards, and CI integration patterns.
Odoo Performance Tuning: PostgreSQL and Server Optimization
Expert guide to Odoo 19 performance tuning. Covers PostgreSQL configuration, indexing, query optimization, Nginx caching, and server sizing for enterprise deployments.
Odoo vs Acumatica: Cloud ERP for Growing Businesses
Odoo vs Acumatica compared for 2026: unique pricing models, scalability, manufacturing depth, and which cloud ERP fits your growth trajectory.
Testing and Monitoring AI Agents in Production
A complete guide to testing and monitoring AI agents in production environments. Covers evaluation frameworks, observability, drift detection, and incident response for OpenClaw deployments.
Compliance Monitoring Agents with OpenClaw
Deploy OpenClaw AI agents for continuous compliance monitoring. Automate regulatory checks, policy enforcement, audit trail generation, and compliance reporting.
Optimizing AI Agent Costs: Token Usage and Caching
Practical strategies for reducing AI agent operational costs through token optimization, caching, model routing, and usage monitoring. Real savings from production OpenClaw deployments.