TL;DRNginx est rapide dès son installation, mais plusieurs de ses meilleures options sont désactivées par défaut. Voici les réglages qui comptent vraiment pour un site performant, chacun dans un bloc de configuration commenté, avec les valeurs par défaut de la documentation officielle et les pièges à éviter, notamment avec le cache des pages PHP.

Avant de toucher à la configuration#

Nginx est un serveur web et un reverse proxy réputé pour sa sobriété : un petit nombre de processus workers traite des milliers de connexions grâce à une boucle d'événements. Cette efficacité ne dispense pas de le régler. Plusieurs options utiles au quotidien, comme sendfile, la compression gzip, open_file_cache ou le cache FastCGI, sont désactivées par défaut dans le code de Nginx ; les paquets des distributions en activent certaines dans leur fichier nginx.conf, mais pas toutes.

Un point de transparence : chez Websource, nos serveurs d'hébergement tournent sous Apache 2.4 avec PHP-FPM, pas sous Nginx. Les blocs ci-dessous ne sont donc pas copiés d'une production Websource : ils sont construits à partir de la documentation officielle de nginx.org, dont nous citons les valeurs par défaut, et commentés ligne par ligne pour que vous puissiez les adapter. La seule mesure présentée, sur la compression, a été faite sur notre serveur le 17 octobre 2026, avec l'outil gzip et un vrai fichier CSS de notre site.

Deux réflexes avant tout changement :

# 1. Vérifier la syntaxe ET l'ouverture des fichiers référencés (certificats, includes…)
sudo nginx -t

# Variante : même test, puis affichage de la configuration complète réellement chargée
sudo nginx -T | less

# 2. Seulement si le test est OK : rechargement « à chaud »
sudo systemctl reload nginx

Le rechargement est gracieux : le processus maître relit la configuration, démarre de nouveaux workers et laisse les anciens terminer les requêtes en cours avant de s'arrêter. Si la nouvelle configuration est invalide, il conserve l'ancienne. Ce filet de sécurité ne remplace pas nginx -t : un restart lancé sur une configuration cassée, lui, laisse le site hors ligne.

Vérifiez aussi votre version avec nginx -v. Le 17 octobre 2026, la version proposée par Debian 13 est la 1.26.3 (relevé apt-cache policy nginx sur notre serveur) : certaines directives citées plus bas n'existent qu'à partir de versions plus récentes, nous le signalons à chaque fois.

Schéma du parcours d'une requête dans Nginx : connexion TLS et HTTP/2, limitation de débit, fichier statique ou page dynamique avec fastcgi_cache puis PHP-FPM, compression gzip ou Brotli, retour au navigateur
Où intervient chaque réglage présenté dans cet article. Schéma simplifié.

Processus et connexions : worker_processes, worker_connections, worker_rlimit_nofile#

Par défaut, Nginx ne lance qu'un seul worker (worker_processes 1). La documentation conseille de partir du nombre de cœurs du processeur, ce que fait la valeur auto. Chaque worker accepte au plus worker_connections connexions simultanées (512 par défaut), et ce nombre inclut les connexions vers les serveurs en amont (PHP-FPM, application proxifiée) : une requête relayée en consomme donc deux.

Ce plafond est lui-même borné par le nombre de fichiers qu'un processus peut ouvrir. worker_rlimit_nofile relève cette limite pour les workers sans redémarrer le processus maître.

# /etc/nginx/nginx.conf — contexte principal (hors de tout bloc)
worker_processes auto;          # un worker par cœur détecté (défaut : 1)
worker_rlimit_nofile 65535;     # descripteurs de fichiers autorisés par worker

events {
    worker_connections 4096;    # connexions simultanées par worker (défaut : 512)
                                # inclut les connexions vers PHP-FPM / le backend
    # multi_accept on;          # accepter toutes les nouvelles connexions d'un coup (défaut : off)
}

Gardez worker_rlimit_nofile nettement au-dessus de worker_connections : chaque connexion relayée, chaque fichier servi et chaque fichier de log occupent un descripteur. Inutile en revanche de régler la directive use : Nginx choisit seul la méthode la plus efficace (epoll sous Linux).

Envoi des fichiers : sendfile, tcp_nopush et tcp_nodelay#

sendfile laisse le noyau copier un fichier directement du disque vers la connexion réseau, sans passer par la mémoire de Nginx. tcp_nopush, qui n'agit que si sendfile est actif, permet d'envoyer l'en-tête de la réponse et le début du fichier dans un même paquet, puis le fichier en paquets pleins. tcp_nodelay, actif par défaut, évite au contraire d'attendre pour expédier les petits paquets sur les connexions keepalive.

http {
    sendfile           on;   # copie disque → réseau par le noyau (défaut : off)
    tcp_nopush         on;   # en-tête + début du fichier dans le même paquet (exige sendfile)
    tcp_nodelay        on;   # déjà la valeur par défaut, rappelé pour la lisibilité
    sendfile_max_chunk 2m;   # une connexion rapide ne monopolise pas un worker (défaut depuis 1.21.4)
}

Keepalive : réutiliser les connexions#

Ouvrir une connexion TCP puis TLS coûte plusieurs allers-retours réseau. Le keepalive permet au navigateur de réutiliser la même connexion pour plusieurs requêtes. Nginx la garde ouverte 75 secondes par défaut et y accepte jusqu'à 1 000 requêtes (100 avant la version 1.19.10). Le même principe vaut côté serveur, entre Nginx et PHP-FPM ou votre application.

http {
    keepalive_timeout  30s;    # durée d'inactivité avant fermeture (défaut : 75s)
    keepalive_requests 1000;   # requêtes max par connexion (défaut depuis 1.19.10)

    # Connexions persistantes vers PHP-FPM
    upstream php {
        server unix:/run/php/php8.4-fpm.sock;   # adaptez au socket de votre version de PHP
        keepalive 8;                            # connexions inactives conservées par worker
    }
}

# Dans le server {} : sans fastcgi_keep_conn, PHP-FPM ferme la connexion après chaque réponse
location ~ \.php$ {
    fastcgi_pass      php;
    fastcgi_keep_conn on;
    # ... (voir la section FastCGI plus bas)
}

Raccourcir keepalive_timeout libère plus vite les connexions inactives sur un serveur très sollicité ; l'allonger profite aux visiteurs qui enchaînent les pages. Il n'y a pas de valeur universelle : observez le nombre de connexions ouvertes (module stub_status) avant et après.

HTTP/2 et HTTP/3 (QUIC)#

HTTP/2 multiplexe toutes les requêtes d'une page sur une seule connexion et compresse les en-têtes. Depuis Nginx 1.25.1, on l'active avec la directive http2 on; ; l'ancien paramètre listen 443 ssl http2 est déclaré obsolète par la documentation. Sur Debian 13 (1.26.3), la nouvelle syntaxe est disponible.

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;                   # remplace « listen ... http2 » depuis 1.25.1

    server_name exemple.fr;
    ssl_certificate     /etc/ssl/exemple.fr/fullchain.pem;
    ssl_certificate_key /etc/ssl/exemple.fr/privkey.pem;
}

N'utilisez plus le server push HTTP/2 : les directives http2_push sont obsolètes depuis la 1.25.1. La documentation oriente vers early_hints, qui envoie une réponse 103 pour que le navigateur commence à télécharger les ressources utiles.

HTTP/3, qui transporte HTTP sur QUIC (au-dessus d'UDP), est disponible depuis Nginx 1.25.0 via le module ngx_http_v3_module. La documentation le qualifie elle-même d'expérimental et il n'est pas compilé par défaut. Si votre build l'inclut (nginx -V doit afficher --with-http_v3_module), la configuration ressemble à ceci :

server {
    listen 443 quic reuseport;  # QUIC sur UDP 443 ; reuseport une seule fois par port/adresse
    listen 443 ssl;             # TCP 443 conservé : HTTP/2 et HTTP/1.1 restent le repli
    http2 on;

    # Annonce HTTP/3 aux navigateurs (ils l'essaieront à la visite suivante)
    add_header Alt-Svc 'h3=":443"; ma=86400' always;
}

Prudence : ouvrez le port UDP 443 dans le pare-feu (le TCP 443 ne suffit pas), gardez toujours le TCP en repli et mesurez avant de généraliser. Le bénéfice attendu concerne surtout les connexions instables, typiquement en mobilité : mesurez-le sur votre propre trafic avant de conclure.

Compression : gzip, et Brotli en module tiers#

Le texte (HTML, CSS, JavaScript, JSON, SVG) se compresse très bien. Nginx intègre gzip, mais il est désactivé par défaut, et même activé il ne compresse que le text/html tant qu'on ne déclare pas d'autres types dans gzip_types.

http {
    gzip            on;          # défaut : off
    gzip_comp_level 5;           # 1 (rapide) à 9 (plus serré) ; défaut : 1
    gzip_min_length 1024;        # ne pas compresser les toutes petites réponses (défaut : 20 octets)
    gzip_vary       on;          # ajoute « Vary: Accept-Encoding » pour les caches intermédiaires
    gzip_proxied    any;         # compresser aussi les requêtes arrivées via un proxy/CDN
    # text/html est toujours compressé : inutile de l'ajouter
    gzip_types text/css text/plain text/xml application/javascript application/json
               application/xml application/rss+xml image/svg+xml font/ttf font/otf;
    # Pas d'images JPEG/PNG/WebP/AVIF ni de WOFF2 : déjà compressés, gain nul et CPU gaspillé
}

Quel niveau choisir ? Nous avons mesuré l'effet sur la feuille de style principale de websource.fr (439 460 octets non compressée), le 17 octobre 2026, avec l'outil gzip de notre serveur. Le temps est une moyenne sur 200 compressions et inclut le lancement du programme : il donne un ordre de grandeur, pas la performance exacte de Nginx, qui compresse en mémoire avec la même bibliothèque zlib.

Niveau gzipTaille compresséeRéductionTemps par compression
Aucun439 460 octets——
1 (défaut de Nginx)89 707 octets79,6 %environ 5,9 ms
669 290 octets84,2 %environ 8,3 ms
968 250 octets84,5 %environ 21,4 ms

Sur ce fichier, passer du niveau 1 au niveau 6 retire encore environ 20 Ko, alors que le niveau 9 ne gagne qu'un kilo-octet de plus pour un temps de calcul plus de deux fois supérieur à celui du niveau 6. Pour une compression faite à chaque requête, un niveau intermédiaire (4 à 6) est le bon compromis ; le niveau maximal se réserve aux fichiers précompressés une fois pour toutes au déploiement, servis par gzip_static on; si votre build inclut ce module (vérifiez avec nginx -V).

Brotli produit en général des fichiers un peu plus légers que gzip, mais il ne fait pas partie de Nginx : c'est un module tiers, ngx_brotli, maintenu dans un dépôt Google. Pas besoin de le compiler sur Debian 13 : les paquets libnginx-mod-http-brotli-filter et libnginx-mod-http-brotli-static figurent dans les dépôts (relevé apt-cache search du 17 octobre 2026). Nous n'avons pas mesuré Brotli nous-mêmes, l'outil n'étant pas installé sur notre serveur.

# Après : sudo apt install libnginx-mod-http-brotli-filter libnginx-mod-http-brotli-static
# (le module dynamique doit être chargé : vérifiez avec  sudo nginx -T | grep brotli)
http {
    brotli            on;        # compression à la volée (défaut : off)
    brotli_comp_level 5;         # 0 à 11 ; défaut : 6
    brotli_static     on;        # sert fichier.css.br s'il existe à côté de fichier.css
    brotli_types text/css text/plain text/xml application/javascript application/json
                 application/xml image/svg+xml;
    # gzip reste actif : il sert les clients qui n'annoncent pas « br »
}

Si un CDN comme Cloudflare se trouve devant votre serveur, il peut déjà compresser vers les visiteurs : vérifiez l'en-tête Content-Encoding reçu par le navigateur avant d'ajouter une couche. Enfin, la documentation rappelle qu'une réponse compressée en HTTPS peut être exposée à l'attaque BREACH lorsqu'elle mêle un secret (jeton CSRF) et des données contrôlées par le visiteur : c'est rare, mais à garder en tête pour les pages sensibles.

Cache navigateur : expires et immutable#

Le meilleur octet est celui qui n'est pas renvoyé. Pour les fichiers dont le nom change à chaque version (empreinte du type app-3f9a1c.css, générée par Webpack, Vite ou l'AssetMapper de Symfony), on peut demander au navigateur de les garder un an sans jamais les revérifier.

# Fichiers versionnés (nom avec empreinte) : un an, sans revalidation
location ~* \.(?:css|js|woff2|avif|webp|png|jpe?g|gif|svg|ico)$ {
    expires 1y;                                  # Expires + Cache-Control: max-age=31536000
    add_header Cache-Control "public, immutable";
    access_log off;                              # inutile de journaliser chaque image
    log_not_found off;                           # ni les favicon.ico absents
}

# HTML : toujours revalidé, pour que les mises à jour soient visibles
location / {
    expires -1;        # Cache-Control: no-cache
    try_files $uri $uri/ /index.php$is_args$args;
}

N'utilisez immutable que pour des fichiers réellement versionnés : un style.css au nom fixe resterait figé chez vos visiteurs pendant un an après une modification.

Bon à savoir : une directive add_header placée dans un locationannule toutes celles héritées du server ou du http. La documentation est explicite : elles ne sont héritées que s'il n'y a aucun add_header au niveau courant. Le bloc ci-dessus, qui ajoute Cache-Control, fait donc disparaître vos en-têtes de sécurité (HSTS, X-Content-Type-Options…) sur les images et les scripts. Répétez-les dans le location, ou regroupez-les dans un fichier inclus avec include. Depuis Nginx 1.29.3, la directive add_header_inherit merge; permet de cumuler les niveaux, mais elle n'existe pas dans la version 1.26 de Debian 13. Autre détail : sans le paramètre always, add_header n'agit que sur les codes de succès et de redirection, pas sur les pages d'erreur.

open_file_cache : garder les fichiers sous la main#

À chaque fichier servi, Nginx ouvre le fichier, lit sa taille et sa date de modification. open_file_cache, désactivé par défaut, garde en mémoire ces descripteurs et ces informations pour les fichiers souvent demandés.

http {
    open_file_cache          max=10000 inactive=60s;  # 10 000 entrées, retirées après 60 s sans accès
    open_file_cache_valid    60s;    # revérifier les infos d'un fichier toutes les 60 s (défaut)
    open_file_cache_min_uses 2;      # garder seulement les fichiers demandés au moins 2 fois (défaut : 1)
    open_file_cache_errors   on;     # mémoriser aussi les « fichier introuvable » (défaut : off)
}

Contrepartie : un fichier remplacé sur le disque peut être servi dans son ancienne version jusqu'à la revalidation (60 secondes ici). Avec des fichiers versionnés, ce n'est pas un problème ; sinon, réduisez open_file_cache_valid.

PHP-FPM, FastCGI et microcache#

Pour un site en PHP (WordPress, PrestaShop, Symfony…), Nginx transmet les pages dynamiques à PHP-FPM par le protocole FastCGI. C'est souvent là que se joue le TTFB. Le module FastCGI dispose de son propre cache : une page générée par PHP est stockée, puis resservie sans solliciter PHP ni la base de données. Avec une durée de vie de quelques secondes, on parle de microcache : le contenu reste quasiment frais, mais un pic de trafic sur une même page ne déclenche plus qu'un calcul par intervalle.

# --- Contexte http : zone de cache ---
# 10 Mo de clés ≈ 80 000 entrées (environ 8 000 clés par Mo selon la documentation)
fastcgi_cache_path /var/cache/nginx/fastcgi levels=1:2 keys_zone=MICRO:10m
                   max_size=1g inactive=10m use_temp_path=off;
fastcgi_cache_key "$scheme$request_method$host$request_uri";

# --- Contexte server : quand NE PAS utiliser le cache ---
set $skip_cache 0;
if ($request_method = POST)  { set $skip_cache 1; }   # formulaires, paniers
if ($query_string != "")     { set $skip_cache 1; }   # recherches, filtres (à affiner)
if ($http_cookie ~* "PHPSESSID|wordpress_logged_in_|wp-postpass_|comment_author_|woocommerce_items_in_cart|PrestaShop-") {
    set $skip_cache 1;                                # visiteur connecté ou panier rempli
}
if ($request_uri ~* "^/(wp-admin|admin|panier|cart|commande|checkout|mon-compte|login)") {
    set $skip_cache 1;                                # pages privées
}

# --- Contexte location PHP ---
location ~ \.php$ {
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
    fastcgi_pass  php;                          # l'upstream défini plus haut
    fastcgi_keep_conn on;

    fastcgi_cache        MICRO;
    fastcgi_cache_valid  200 301 302 5s;        # microcache : 5 secondes
    fastcgi_cache_bypass $skip_cache;           # ne pas LIRE le cache dans ces cas
    fastcgi_no_cache     $skip_cache;           # ne pas ÉCRIRE dans le cache non plus
    fastcgi_cache_lock   on;                    # une seule requête régénère une page expirée
    fastcgi_cache_use_stale updating error timeout http_500 http_503;
    fastcgi_cache_background_update on;         # resservir l'ancienne version pendant la mise à jour
    add_header X-Cache $upstream_cache_status;  # HIT, MISS, BYPASS, EXPIRED, STALE…
}

Les noms de cookies et d'URL sont des exemples à adapter à votre CMS. Les pièges qui transforment un cache en fuite de données sont toujours les mêmes :

  • Les cookies de session. Si la page d'un client connecté entre dans le cache, le visiteur suivant voit son nom, son panier, voire ses commandes. D'où les deux directives complémentaires : fastcgi_cache_bypass empêche de lire le cache, fastcgi_no_cache empêche d'y écrire. Il faut les deux.
  • Le piège de fastcgi_ignore_headers. Par défaut, Nginx ne met pas en cache une réponse qui contient un en-tête Set-Cookie, et il tient compte des en-têtes Cache-Control et Expires envoyés par l'application. Beaucoup de tutoriels ajoutent fastcgi_ignore_headers Cache-Control Expires Set-Cookie; pour « faire marcher » le cache : vos règles de contournement deviennent alors la seule protection. Si vous le faites, testez chaque page privée.
  • Les pages privées par URL. Compte client, panier, tunnel de commande, administration : excluez-les explicitement, même si un cookie les protège déjà.
  • La clé de cache. Sans $scheme et $host, la version HTTP d'une page ou un autre domaine du même serveur peuvent se mélanger.
  • La vérification. Après mise en place, appelez la même page deux fois avec curl -I : l'en-tête X-Cache doit passer de MISS à HIT en navigation anonyme, et rester BYPASS avec un cookie de session.

Si le cache ne suffit pas, regardez du côté de l'application : un cache objet Redis ou Memcache réduit le coût des pages qui ne peuvent pas être mises en cache entières.

Reverse proxy : proxy_cache#

Quand Nginx se place devant une application Node.js, Python, Java ou devant un Apache, le module proxy offre le même cache que FastCGI, avec les mêmes règles de prudence : une réponse avec Set-Cookie n'est pas mise en cache par défaut.

http {
    proxy_cache_path /var/cache/nginx/proxy levels=1:2 keys_zone=APP:10m
                     max_size=2g inactive=60m use_temp_path=off;

    upstream app {
        server 127.0.0.1:3000;
        keepalive 16;                       # connexions persistantes vers l'application
    }

    server {
        # ... listen, ssl, server_name ...
        location / {
            proxy_pass http://app;
            proxy_http_version 1.1;         # obligatoire pour le keepalive avant 1.29.7 (défaut 1.0)
            proxy_set_header Connection ""; # idem : ne pas transmettre « Connection: close »
            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            APP;
            proxy_cache_valid      200 301 10m;
            proxy_cache_valid      404 1m;
            proxy_cache_use_stale  error timeout updating http_500 http_502 http_503 http_504;
            proxy_cache_lock       on;
            proxy_cache_bypass     $cookie_session;   # adaptez au nom de votre cookie de session
            proxy_no_cache         $cookie_session;
            add_header X-Cache $upstream_cache_status;
        }
    }
}

proxy_cache_use_stale a un bénéfice souvent sous-estimé : si l'application tombe, Nginx continue à servir la dernière version connue des pages au lieu d'afficher une erreur 502 ou une erreur 504.

Buffers : ni trop petits, ni démesurés#

Les buffers déterminent ce que Nginx garde en mémoire avant d'écrire sur disque. Trop petits, ils provoquent des écritures de fichiers temporaires ou des erreurs ; trop grands, ils consomment de la mémoire pour chaque connexion.

http {
    client_max_body_size    20m;   # taille max d'un envoi (défaut : 1m → erreur 413 au-delà)
    client_body_buffer_size 16k;   # au-delà, le corps de la requête part en fichier temporaire

    # Réponses de PHP-FPM
    fastcgi_buffer_size 16k;       # 1re partie de la réponse (en-têtes) ; défaut : 4k ou 8k
    fastcgi_buffers     16 16k;    # reste de la réponse ; défaut : 8 × 4k ou 8k

    # Réponses d'une application proxifiée
    proxy_buffer_size   16k;
    proxy_buffers       16 16k;
}

Le symptôme classique d'un fastcgi_buffer_size trop petit est le message upstream sent too big header while reading response header from upstream dans le journal d'erreurs, suivi d'une erreur 502 : il arrive avec les applications qui posent beaucoup de cookies ou de gros en-têtes. Augmentez ces valeurs quand le journal le réclame, pas par principe.

Limitation de débit contre les abus : limit_req#

Les robots qui testent des mots de passe ou aspirent un catalogue peuvent saturer PHP. limit_req applique un « seau qui fuit » (leaky bucket) : les requêtes au-delà du débit autorisé sont mises en attente dans la limite de burst, puis refusées.

http {
    # Une entrée par adresse IP ; 10 Mo ≈ 80 000 adresses suivies (environ 8 000 états de 128 octets par Mo en 64 bits)
    limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
    limit_req_zone $binary_remote_addr zone=login:10m   rate=10r/m;
    limit_req_status 429;          # « Too Many Requests » au lieu du 503 par défaut

    server {
        location / {
            limit_req zone=general burst=20 nodelay;   # tolère des rafales de 20 sans attente
            # ...
        }
        location = /wp-login.php {                     # ou /login, /admin… selon votre site
            limit_req zone=login burst=5 nodelay;
            # ... (même traitement PHP que plus haut)
        }
    }
}

Le paramètre nodelay sert immédiatement les requêtes de la rafale autorisée au lieu de les étaler, ce qui évite de ralentir un visiteur légitime qui charge une page et ses ressources. Attention si votre serveur est derrière un CDN ou un équilibreur de charge : $binary_remote_addr contiendrait alors l'adresse du proxy, et tout le monde partagerait la même limite. Configurez d'abord le module ngx_http_realip_module (set_real_ip_from, real_ip_header) pour retrouver l'adresse réelle du visiteur. Pour les causes et le diagnostic côté client, voir notre article sur l'erreur 429 Too Many Requests.

TLS : reprise de session et OCSP stapling#

La poignée de main TLS est coûteuse. Le cache de session permet à un visiteur qui revient de reprendre sa session sans tout renégocier. La documentation conseille un cache partagé entre workers plutôt que le cache intégré à chaque processus : un mégaoctet contient environ 4 000 sessions.

server {
    listen 443 ssl;
    http2 on;

    ssl_protocols       TLSv1.2 TLSv1.3;  # valeur par défaut des versions récentes, explicitée
    ssl_session_cache   shared:SSL:10m;   # ≈ 40 000 sessions, partagé entre workers
    ssl_session_timeout 1h;               # défaut : 5m ; la doc suggère de l'allonger
    # ssl_session_tickets on (défaut) : depuis 1.23.2, avec un cache partagé,
    # Nginx génère et fait tourner lui-même les clés des tickets

    # OCSP stapling : uniquement si votre certificat contient une URL OCSP
    # ssl_stapling        on;
    # ssl_stapling_verify on;
    # ssl_trusted_certificate /etc/ssl/exemple.fr/chain.pem;  # chaîne de l'autorité
    # resolver 127.0.0.1;                                     # résolveur DNS pour joindre le répondeur OCSP
}

L'OCSP stapling consiste pour le serveur à joindre lui-même la preuve de validité du certificat, ce qui évite au navigateur d'interroger l'autorité de certification. C'est utile avec un certificat qui annonce un répondeur OCSP. Mais Let's Encrypt a retiré les URL OCSP de ses certificats le 7 mai 2025 et a éteint ses répondeurs le 6 août 2025 : avec un certificat Let's Encrypt, il n'y a plus rien à agrafer, et ces lignes ne servent à rien. Vérifiez votre certificat avant de les ajouter :

# Affiche l'URL du répondeur OCSP, ou rien s'il n'y en a pas
openssl x509 -in /etc/ssl/exemple.fr/fullchain.pem -noout -ocsp_uri

Pour aller plus loin sur la partie réseau, voir nos articles sur le port 443 et le passage d'un site en HTTPS.

Logs : écrire moins souvent, et seulement l'utile#

Par défaut, chaque requête produit une écriture dans le journal d'accès. Le paramètre buffer regroupe les lignes en mémoire ; elles sont écrites quand le tampon est plein, quand les données dépassent l'âge fixé par flush, ou quand un worker rouvre ses journaux ou s'arrête.

http {
    # Écritures groupées par blocs de 64 Ko, au plus toutes les 5 secondes
    access_log /var/log/nginx/access.log combined buffer=64k flush=5s;

    # Option : ne pas journaliser les réponses 2xx/3xx des fichiers statiques
    # (préférez « access_log off; » dans le location des assets, voir plus haut)
}

Gardez les journaux des pages et des erreurs : ce sont eux qui permettent de comprendre une attaque, une erreur 502 ou un pic de trafic. Couper le journal des images et des scripts, en revanche, réduit les écritures disque sans perte d'information utile.

Récapitulatif des réglages#

DirectiveDéfaut NginxValeur de départ proposéeEffet
worker_processes1autoUn worker par cœur
worker_connections5122 048 à 4 096Plus de connexions simultanées
sendfile / tcp_nopushoff / offon / onFichiers envoyés par le noyau, paquets pleins
keepalive_timeout75s15 à 75 s selon la chargeRéutilisation des connexions
http2offon (Nginx 1.25.1 et plus)Requêtes multiplexées
gzip / gzip_comp_leveloff / 1on / 4 à 6Notre CSS : 5 à 6 fois plus léger
expiresoff1y sur les fichiers versionnésAucun retéléchargement
open_file_cacheoffmax=10000 inactive=60sMoins d'accès au système de fichiers
fastcgi_cacheoffMicrocache de quelques secondesPHP sollicité une fois par intervalle
limit_req_status503429Code de refus explicite
ssl_session_cachenoneshared:SSL:10mReprise de session TLS

La colonne « Défaut Nginx » reprend les valeurs de la documentation, c'est-à-dire du code source. Le fichier nginx.conf livré par votre distribution peut déjà en modifier certaines : nginx -T montre ce qui est réellement appliqué. Le ratio de compression indiqué correspond à notre mesure sur un fichier CSS ; il varie selon le contenu.

Dans quel ordre procéder ?#

  1. Mesurez l'état de départ : temps de réponse serveur (TTFB), poids des pages, en-têtes renvoyés (curl -I). Notre diagnostic en 5 étapes aide à savoir si la lenteur vient bien du serveur.
  2. Appliquez les réglages sans risque : workers, sendfile, keepalive, HTTP/2, compression, cache navigateur, open_file_cache, journaux.
  3. Mettez en place la limitation de débit sur les URL sensibles, en vérifiant l'adresse IP réelle derrière un éventuel CDN.
  4. Ajoutez le microcache en dernier, page par page, et testez les parcours connectés : c'est le réglage le plus rentable et le plus risqué.
  5. Après chaque étape : nginx -t, puis systemctl reload nginx, puis une nouvelle mesure.

Nginx ou Apache ?#

Les deux serveurs atteignent d'excellentes performances lorsqu'ils sont bien réglés. Nginx sert les fichiers statiques et encaisse les connexions simultanées avec très peu de mémoire, mais ne lit pas les fichiers .htaccess : toute règle passe par la configuration du serveur. Apache, que nous exploitons au quotidien chez Websource, garde l'avantage de cette configuration par dossier, très pratique en hébergement mutualisé. Si votre site tourne sous Apache, lisez notre article complémentaire sur les optimisations d'Apache2.

Websource héberge et administre les sites de ses clients sur une infrastructure Apache et PHP-FPM réglée pour la vitesse, et les accompagne dès la commande : la migration est offerte avec les offres Premium et Advanced, sur devis avec l'offre Standard. Les offres d'hébergement web démarrent dès 4,99 € HT/mois. Vous gérez votre propre serveur Nginx et souhaitez un regard extérieur sur sa configuration ? Contactez-nous.

Questions fréquentes#

Quelle commande pour vérifier la configuration de Nginx ?#

nginx -t teste la syntaxe et tente d'ouvrir les fichiers référencés (certificats, includes). nginx -T fait le même test et affiche en plus toute la configuration chargée. Ne rechargez avec systemctl reload nginx que si le test est réussi.

Faut-il activer gzip et Brotli en même temps ?#

Oui, si vous installez Brotli : le navigateur annonce les formats qu'il accepte, Nginx sert Brotli à ceux qui le gèrent et gzip aux autres. Brotli est un module tiers ; sur Debian 13, il est disponible en paquet.

Le cache FastCGI est-il compatible avec WordPress ou PrestaShop ?#

Oui, à condition d'exclure les visiteurs connectés, les paniers, le tunnel de commande, l'administration et les requêtes POST, via fastcgi_cache_bypass et fastcgi_no_cache. Sans ces exclusions, des pages personnelles peuvent être servies à d'autres visiteurs.

HTTP/3 est-il prêt pour la production avec Nginx ?#

Le module HTTP/3 existe depuis Nginx 1.25.0, mais la documentation officielle le qualifie toujours d'expérimental. Il faut ouvrir le port UDP 443 et conserver HTTP/2 en repli ; testez-le sur un site non critique avant de le généraliser.

Pourquoi limit_req bloque-t-il tous mes visiteurs derrière Cloudflare ?#

Parce que Nginx voit alors l'adresse IP du proxy, pas celle du visiteur : tout le monde partage la même limite. Le module realip (set_real_ip_from, real_ip_header) permet de retrouver l'adresse réelle avant d'appliquer la limitation.

Article publié le 22/10/2026 sous la signature de Jonathan Gutierrez, rédigé à partir des sources citées ci-dessus, puis relu avant publication. Une information vous semble inexacte ou datée ? Signalez-le nous, nous corrigeons.