Vous ne ressentez pas le choix du serveur web un mardi calme. Vous le ressentez à 02:13 un samedi quand le paiement bloque,
les tableaux de bord mentent et votre cerveau en astreinte commence à calculer avec des pertes de paquets. « Nginx vs Apache »
n’est pas une question d’idéologie. C’est une question des modes de défaillance que vous pouvez accepter, des réglages
opérationnels que votre équipe utilisera réellement et des choix par défaut qui vont vous coûter en silence pendant des mois.
En 2026, la bonne réponse est souvent : « les deux, mais volontairement. » La décision importante concerne la frontière : où
se termine le TLS, où le buffering des requêtes a lieu, où le code dynamique s’exécute, où les remplacements par locataire
sont autorisés et où vous voulez que le rayon d’impact d’un incident s’arrête.
Ce qui compte vraiment en 2026 (et ce qui n’en vaut pas la peine)
En 2010, la question était « lequel est le plus rapide ? » En 2026, la vitesse brute des benchmarks est rarement votre
goulot d’étranglement. Votre goulot est généralement l’un des suivants : saturation en amont (app ou BD), mauvais
choix de buffering, trop de clients lents, comportement TLS ou HTTP/2 défectueux, pics de latence du système de fichiers,
ou un modèle de configuration qui permet à des équipes au hasard de modifier le comportement de production sans revue.
La décision qui compte réellement
Vous ne choisissez pas un simple serveur web. Vous choisissez un plan de contrôle pour le trafic et la configuration :
- Où terminez-vous le TLS ? C’est là que résident certificats, suites de chiffrement, ALPN, OCSP et les bizarreries clients.
- Où tamponnez-vous les requêtes et réponses ? Cela décide si des clients lents noieront votre application.
- Autorisez-vous des remplacements par répertoire ? C’est une question de gouvernance déguisée en fonctionnalité.
- Comment gérez-vous les rechargements ? Si les rechargements font peur, les gens retardent les corrections. Les corrections retardées deviennent des incidents.
- Que journalisez-vous par défaut ? Parce que vous déboguerez avec ce que vous avez journalisé, pas avec ce que vous auriez voulu journaliser.
Six à dix faits historiques qui vous affectent encore
- L’Apache HTTP Server a commencé en 1995 comme un ensemble de patchs (« a patchy server »). Son écosystème de modules et la prolifération de la config en héritent directement.
- Nginx a été créé au début des années 2000 pour adresser le problème C10k : gérer efficacement 10 000 connexions simultanées avec un modèle orienté événement.
- La séparation des MPM d’Apache (prefork/worker/event) existe parce qu’il a fallu concilier threads, processus et modules non thread-safe au fil du temps.
- .htaccess est un artefact culturel de l’hébergement mutualisé : décentraliser le contrôle jusqu’aux répertoires, accepter le surcoût. Pratique mais chaotique opérationnellement.
- L’identité « reverse proxy d’abord » de Nginx a façonné des architectures modernes où le « serveur web » est souvent un routeur de trafic, pas un exécuteur d’apps.
- HTTP/2 a changé le chargement des ressources (multiplexage) mais a aussi introduit du head-of-line blocking au niveau TCP, rendant la latence et la perte plus visibles.
- QUIC/HTTP/3 a déplacé le transport vers UDP ce qui modifie le comportement des middleboxes, des pare-feux et de l’observabilité, notamment dans les réseaux d’entreprise.
- Let’s Encrypt a normalisé l’automatisation des certificats courts rendant « TLS difficile » moins vrai, mais « le renouvellement TLS casse la prod à 3h du matin » reste très vrai.
- Les améliorations du noyau (epoll, sendfile, réglages TCP) ont rendu les deux serveurs plus rapides ; désormais le différenciateur est le modèle de configuration et le comportement en cas de défaillance.
La conclusion : les deux serveurs sont « assez rapides » pour la plupart des sites. La question est lequel rendra le prochain
incident plus court, moins coûteux et moins humiliant.
Recommandations franches (prenez ceci, évitez cela)
Si vous exploitez une pile web Linux moderne typique
- Par défaut, placez Nginx à la périphérie pour les ressources statiques, la terminaison TLS, le cache et le reverse proxy vers les backends applicatifs.
- Exécutez les applications derrière via PHP-FPM, uWSGI, Gunicorn, Node ou ce que vous aurez choisi de regretter.
- N’utilisez Apache que lorsque vous en avez besoin : usage intensif des règles par répertoire, applications legacy ou modules difficiles à remplacer.
Si plusieurs équipes poussent des changements de configuration
- Évitez .htaccess en production à moins que vous n’aimiez faire des post-mortems avec la phrase « quelqu’un a modifié un fichier dans un répertoire sur un hôte au hasard ».
- Préférez la configuration centralisée et la validation CI (Nginx s’aligne naturellement là-dessus ; Apache le peut aussi, mais .htaccess a tendance à le saboter).
Si vous hébergez du contenu généré par les utilisateurs ou gérez des clients lents
- Le modèle de buffering de Nginx est généralement plus simple à raisonner : il protège les upstreams des uploads/downloads lents.
- Apache peut le faire, mais vous devez être explicite et prudent avec le choix du MPM et les timeouts.
Blague #1 : Choisir un serveur web parce qu’un benchmark dit qu’il est 3 % plus rapide, c’est comme choisir un parachute pour sa couleur. On ne remarque le mauvais choix qu’une seule fois.
Architecture qui survit au trafic réel
L’architecture qui garde votre pager calme paraît ennuyeuse :
Nginx (périphérie) → serveurs d’app (PHP-FPM / conteneurs / runtime) → BD/cache.
Placez la « réflexion coûteuse » (code dynamique) derrière quelque chose qui peut absorber les bizarreries clients.
Responsabilités de la périphérie (ce qui appartient à l’avant)
- Terminaison TLS avec des suites de chiffrement sensées, reprise de session, OCSP stapling où approprié.
- Limitation des requêtes (rate/conn) et contrôles basiques d’abus. Pas du théâtre de sécurité — juste un modelage de charge.
- Ressources statiques servies depuis le disque avec des en-têtes de cache corrects.
- Compression (prudent avec les formats déjà compressés) et conformité du content-type.
- Routage vers les upstreams, health checks (ou au moins timeouts sensibles à l’état), comportement de basculement gracieux.
Responsabilités en amont (ce qui appartient derrière)
- Rendu dynamique et logique métier.
- Authentification/autorisation (la périphérie peut aider, mais n’imbriquez pas votre modèle d’identité dans la config du proxy).
- Accès base de données et logique de cache.
Pourquoi « simplement exécuter Apache avec mod_php » est principalement un mouvement legacy
Ce n’est pas que mod_php ne puisse pas fonctionner. C’est que cela couple votre modèle de worker HTTP au cycle de vie PHP,
et ce couplage est l’endroit où la liberté opérationnelle meurt. Cela a également tendance à encourager « encore une règle de réécriture »
dispersée dans les répertoires.
Réalité moderne : vous voulez une séparation claire entre « accepter le trafic client » et « exécuter le code applicatif ». Cette séparation
est l’endroit où vous placez les timeouts, le buffering, les limites et l’observabilité. Nginx rend cette séparation naturelle. Apache peut le faire,
mais vous combattrez des anciens choix par défaut et des habitudes.
Plongée Apache : MPM, .htaccess et quand il l’emporte
Le choix du MPM n’est pas optionnel
Si vous exécutez Apache en 2026 et que vous ne savez pas quel MPM vous utilisez, vous le faites au feeling. Stop.
Les Multi-Processing Modules d’Apache définissent la concurrence et le comportement des ressources :
- prefork : un processus par connexion. Compatible avec les modules anciens non thread-safe. Empreinte mémoire élevée. Facile à surcharger avec de la concurrence.
- worker : threads par processus. Meilleure utilisation mémoire. La sécurité thread-safe importe.
- event : comme worker mais meilleure gestion des keep-alive. Généralement le meilleur choix par défaut pour un Apache en reverse-proxy moderne.
Les avantages d’Apache sont toujours réels :
- Compatibilité : applications d’entreprise legacy, anciens modules d’auth et configurations qui dépendent des sémantiques Apache.
- Configuration par répertoire : si vous êtes vraiment dans un environnement multi-tenant avec contrôle délégué, .htaccess peut être le moindre mal pour la gouvernance.
- Écosystème de modules : certains modules sont effectivement « uniquement Apache » dans la culture opérationnelle, pas seulement dans le code.
.htaccess : fonctionnalité ou piège ?
.htaccess existe parce que l’hébergement partagé avait besoin d’un contrôle distribué. Il force aussi Apache à vérifier des fichiers d’override
dans les répertoires pendant le traitement des requêtes. Ce coût est souvent mineur par requête, jusqu’à ce que vous montiez en charge, cassiez le cache
ou mettiez votre contenu sur un système de fichiers à latence plus élevée.
Le problème majeur n’est pas le CPU. C’est le contrôle des changements. Avec .htaccess, un développeur peut « réparer la prod » en éditant un fichier
sur un seul hôte, contournant revue, CI et rollback. Vous ne voulez pas que la « dérive de configuration » devienne un trait de personnalité de votre flotte.
Plongée Nginx : boucle d’événements, buffering et quand il l’emporte
Nginx est essentiellement une machine à trafic opinionnée : un petit nombre de workers, une boucle d’événements et l’habitude de bufferiser pour
protéger les upstreams. Il brille quand vous devez gérer beaucoup de connexions simultanées sans attacher un thread/processus par client.
Où Nginx est généralement le bon choix
- Reverse proxy vers plusieurs upstreams, avec un comportement prévisible sous charge.
- Service de fichiers statiques avec sendfile, en-têtes de cache et keep-alives sensés.
- Modelage de charge (rate limiting, limitation de connexions) qui est simple à raisonner.
- Hygiène opérationnelle : configuration centralisée, syntaxe testable, rechargements qui ne coupent pas les connexions actives quand c’est bien fait.
Où Nginx fait parfois des dégâts
- Surprises de buffering : si vous ne comprenez pas le buffering requête/réponse, vous pouvez augmenter les IO disque ou la latence involontairement.
- Décalages de timeout : des timeouts Nginx qui ne correspondent pas aux timeouts upstream provoquent des « 504 mystérieux ».
- Lisibilité de la configuration : la config Nginx est propre jusqu’à ce qu’elle ne le soit plus. Les emplacements imbriqués et les règles regex peuvent devenir une boîte à énigmes.
Blague #2 : Les configurations Nginx ne deviennent pas illisibles progressivement. Elles le deviennent le moment où quelqu’un dit : « Ce n’est qu’un bloc location de plus. »
HTTP/2, HTTP/3, TLS et ce qui casse en silence
HTTP/2 et HTTP/3 ne sont pas des « interrupteurs de vitesse ». Ils changent vos modes de défaillance.
Le multiplexage HTTP/2 réduit le nombre de connexions mais peut amplifier la douleur de la perte de paquets sur une connexion unique.
HTTP/3 (QUIC) peut améliorer les performances sur les réseaux avec perte, mais il interagit aussi avec les pare-feux et la supervision de
manière à poser des questions pointues à votre équipe réseau.
Terminaison TLS : choisissez un endroit et assumez-le
Terminer le TLS à la périphérie est courant car cela centralise certificats et réglages de performance. Mais centraliser signifie aussi centraliser le blâme.
Bien : moins de parties mobiles. Mal : une mauvaise suite de chiffrement peut mettre à genoux toute votre flotte.
Une citation fiabilité à intégrer
L’espoir n’est pas une stratégie.
— idée paraphrasée souvent attribuée aux leaders opérations et fiabilité.
Paraphrasée ou non, le point est précis opérationnellement : ne vous « contentez pas d’espérer » que vos timeouts s’alignent, que vos renouvellements TLS fonctionneront
ou que vos paramètres HTTP/2 sont corrects. Prouvez-le avec des tests et des tableaux de bord.
Apps dynamiques : PHP-FPM, proxies et réalité
La plupart des disputes « Nginx vs Apache » sont en réalité des disputes « comment exécutons-nous le code dynamique ? ». En 2026, PHP-FPM reste un choix courant,
et la différence entre un système stable et un système instable est généralement la taille des pools, les timeouts et la rétro-pression upstream — pas la marque du
serveur frontal.
Ce que vous devriez standardiser
- Chaîne de timeout : client → proxy de périphérie → runtime app → BD. Fixez-les intentionnellement, documentez le temps maximal de requête attendu.
- Rétro-pression : que se passe-t-il quand l’app est saturée ? Queue ? Rejet ? Déchargement de charge ? C’est une décision produit déguisée en costume SRE.
- Taille maximale du corps de requête : si vous acceptez des uploads, imposez des limites en un seul point (périphérie) et validez à nouveau dans l’app.
- Champs d’observabilité : request ID, timings upstream, statut, octets envoyés et détails TLS pour le débogage.
Quand Apache est un bon front-end pour l’app
Si vous exécutez une ancienne application PHP qui dépend d’un comportement spécifique à Apache, ou si vous avez un écosystème interne mature construit autour des modules Apache,
il peut être judicieux de garder Apache proche de l’application. Mais vous voulez toujours une périphérie stable devant — souvent Nginx, parfois un LB matériel ou cloud —
afin que la couche app n’ait pas à absorber les chocs du trafic client.
Observabilité pour éviter le debugging motivé par la recherche de coupables
Si votre métrique unique est « requêtes par seconde », vous n’observez pas — vous comptez. Pour Nginx/Apache, votre visibilité minimale viable inclut :
- Percentiles de latence (p50/p95/p99), séparés en temps upstream vs temps périphérie.
- Taux d’erreur par code de statut et par upstream.
- États de connexion (actives, lecture, écriture, attente/keepalive).
- Pression sur les ressources : CPU steal, mémoire, latence IO disque, pertes/rétransmissions réseau.
- Événements de déploiement/reload corrélés avec les pics d’erreur.
L’astuce : journalisez ce dont vous aurez besoin avant de savoir que vous en avez besoin. Ajoutez des request IDs et des champs de timing upstream maintenant.
Pendant un incident, vous n’aurez pas le temps de négocier avec votre passé.
Tâches pratiques : commandes, sorties et décisions (12+)
Ce sont des tâches que vous pouvez exécuter sur une vraie machine Linux. Chacune inclut : commande, ce que la sortie signifie et la décision à en prendre.
Utilisez-les comme un rituel de production, pas comme un tutoriel une fois pour toutes.
1) Identifier quel serveur répond réellement au trafic
cr0x@server:~$ sudo ss -ltnp | egrep ':(80|443)\s'
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=1294,fd=6))
LISTEN 0 511 0.0.0.0:443 0.0.0.0:* users:(("nginx",pid=1294,fd=7))
Signification : Nginx possède les ports 80/443 sur cet hôte. Si vous attendiez Apache, vous avez un décalage architectural ou un runbook obsolète.
Décision : Orientez vos réglages et journaux vers le processus d’edge réel. Vérifiez aussi si Apache n’est que l’upstream (par ex. sur 127.0.0.1:8080).
2) Confirmer que la config Nginx est syntaxiquement valide avant de recharger
cr0x@server:~$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Signification : Vous ne bloquerez pas le service avec un reload dû à une erreur de syntaxe.
Décision : Procédez au reload ; s’il échoue, traitez-le comme un incident de gestion du changement, pas comme une simple précaution ops.
3) Valider la config Apache et les modules chargés
cr0x@server:~$ sudo apachectl -t -D DUMP_MODULES | head
Loaded Modules:
core_module (static)
so_module (static)
http_module (static)
mpm_event_module (shared)
proxy_module (shared)
proxy_fcgi_module (shared)
Signification : Apache utilise mpm_event et possède des modules proxy. Cela suggère qu’Apache est utilisé comme reverse proxy ou front-end applicatif, pas nécessairement mod_php.
Décision : Si vous voyez mpm_prefork avec un trafic important, enquêtez immédiatement sur la mémoire et les limites de concurrence.
4) Vérifier le MPM Apache à l’exécution (le modèle de concurrence que vous payez)
cr0x@server:~$ sudo apachectl -V | egrep 'Server MPM|MPM_NAME|MPM_DIR'
Server MPM: event
MPM_NAME: event
MPM_DIR: server/mpm/event
Signification : Vous êtes sur le MPM event, généralement le choix moderne pour l’efficacité des keep-alive.
Décision : Ajustez les limites de threads/processus en conséquence ; n’appliquez pas la folklore « MaxClients » de l’ère prefork.
5) Vérifier la pression des connexions en direct (êtes-vous noyé par des clients lents ?)
cr0x@server:~$ sudo ss -s
Total: 2234 (kernel 0)
TCP: 1947 (estab 812, closed 1001, orphaned 0, timewait 1001)
Transport Total IP IPv6
RAW 0 0 0
UDP 12 9 3
TCP 946 781 165
INET 958 790 168
FRAG 0 0 0
Signification : Des comptes TIME-WAIT élevés peuvent être normaux, mais des connexions établies élevées et soutenues peuvent indiquer des clients lents,
des keepalive trop généreux ou une lenteur en aval.
Décision : Si les connexions établies augmentent avec la latence, investiguez les paramètres keepalive et la saturation upstream ; envisagez le buffering de requêtes/réponses et des timeouts plus stricts.
6) Détecter l’épuisement des descripteurs de fichiers avant qu’il ne devienne une panne mystérieuse
cr0x@server:~$ sudo cat /proc/$(pidof nginx | awk '{print $1}')/limits | egrep 'Max open files'
Max open files 1024 1024 files
Signification : 1024 fichiers ouverts est minuscule pour un proxy d’edge chargé. Vous l’atteindrez via sockets et logs sous charge.
Décision : Augmentez les limites via des overrides systemd et worker_rlimit_nofile (Nginx) / limites de service appropriées pour Apache.
7) Vérifier le backlog d’écoute et les symptômes de pression SYN
cr0x@server:~$ sudo netstat -s | egrep -i 'listen|overflow|drop' | head
104 times the listen queue of a socket overflowed
104 SYNs to LISTEN sockets dropped
Signification : Votre file d’acceptation a débordé ; les clients peuvent voir des échecs de connexion ou des retries. C’est souvent charge + backlog trop petit + accept lent dû à la pression CPU.
Décision : Augmentez somaxconn, vérifiez Nginx listen ... backlog= si applicable, et réduisez la latence upstream pour que les workers reviennent plus vite à l’accept.
8) Vérifier le backlog kernel et les limites de fichiers (garde-fous système)
cr0x@server:~$ sysctl net.core.somaxconn fs.file-max
net.core.somaxconn = 128
fs.file-max = 9223372036854775807
Signification : somaxconn=128 est conservateur pour un serveur d’edge ; cela peut contribuer à des SYNs perdus sous un trafic en rafale.
Décision : Augmentez net.core.somaxconn (et alignez-le avec les paramètres de backlog d’écoute de votre serveur). Validez par des tests de charge ; ne copiez pas des valeurs énormes sans besoin.
9) Confirmer quelles versions HTTP sont négociées (et si les clients restent bloqués en 1.1)
cr0x@server:~$ curl -skI --http2 https://localhost/ | head -n 5
HTTP/2 200
server: nginx
date: Tue, 04 Feb 2026 12:03:11 GMT
content-type: text/html
Signification : HTTP/2 fonctionne localement ; cela ne prouve pas que ça marche depuis Internet (il existe des middleboxes), mais c’est un point de référence.
Décision : Si les clients négocient encore HTTP/1.1 à grande échelle, vérifiez ALPN/TLS, les capacités du CDN/LB et si vous avez accidentellement désactivé HTTP/2 sur le vhost concerné.
10) Inspecter rapidement le certificat TLS et sa date d’expiration
cr0x@server:~$ echo | openssl s_client -servername example.com -connect 127.0.0.1:443 2>/dev/null | openssl x509 -noout -dates -subject
notBefore=Jan 10 00:00:00 2026 GMT
notAfter=Apr 10 00:00:00 2026 GMT
subject=CN = example.com
Signification : Le cert est valide maintenant et expire le 10 avril. Vous avez un calendrier, mais la production nécessite automatisation et alerting.
Décision : Si l’expiration est proche, priorisez la pipeline de renouvellement et le processus de déploiement ; n’organisez pas de sauvetage.
11) Déterminer si vous êtes CPU-bound, IO-bound ou bloqué sur autre chose
cr0x@server:~$ sudo pidstat -p $(pidof nginx | tr ' ' ',') 1 3
Linux 6.8.0 (server) 02/04/2026 _x86_64_ (8 CPU)
12:03:40 PM UID PID %usr %system %CPU CPU Command
12:03:41 PM 0 1294 18.00 6.00 24.00 2 nginx
12:03:41 PM 0 1295 21.00 7.00 28.00 3 nginx
12:03:41 PM 0 1296 20.00 6.00 26.00 4 nginx
Signification : Les workers Nginx consomment du CPU mais n’occupent pas entièrement l’hôte. Si la latence est élevée, il peut s’agir d’attentes upstream, réseau ou disque plutôt que d’une saturation CPU pure.
Décision : Si le CPU est saturé, réduisez le travail coûteux à la périphérie (regex, Lua, niveau de compression), déchargez les assets statiques et assurez-vous de ne pas exécuter de travail dynamique dans la couche web.
12) Mesurer la latence disque qui transforme un « serveur web rapide » en « serveur web lent »
cr0x@server:~$ iostat -xz 1 2
Linux 6.8.0 (server) 02/04/2026 _x86_64_ (8 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
22.10 0.00 7.20 9.80 0.00 60.90
Device r/s rkB/s rrqm/s %rrqm r_await rareq-sz w/s wkB/s w_await wareq-sz aqu-sz %util
nvme0n1 210.0 8420.0 10.0 4.5 8.20 40.1 95.0 6120.0 18.40 64.4 2.10 92.0
Signification : Un %util élevé et un w_await augmenté suggèrent une pression disque. Fichiers statiques, buffers temporaires et logs peuvent tous contribuer.
Décision : Déplacez les buffers temporaires vers un stockage plus rapide, réduisez le buffering de réponse sur disque quand approprié, réduisez le volume de logs et investigatez la santé du système de fichiers et du stockage sous-jacent.
13) Vérifier la saturation upstream pour PHP-FPM (la fabrique silencieuse de 504)
cr0x@server:~$ sudo ss -ltnp | grep php-fpm
LISTEN 0 128 127.0.0.1:9000 0.0.0.0:* users:(("php-fpm8.2",pid=2201,fd=9))
Signification : PHP-FPM écoute sur le TCP 9000. Vérifiez maintenant s’il est le goulot via son statut ou les logs.
Décision : Si Nginx montre des timeouts upstream, ajustez le pool FPM (pm.max_children, timeouts) et corrigez les requêtes lentes plutôt qu’augmenter le nombre de workers web.
14) Valider les champs du access log Nginx pour le timing upstream (déboguez avec des faits)
cr0x@server:~$ sudo tail -n 3 /var/log/nginx/access.log
203.0.113.10 - - [04/Feb/2026:12:03:51 +0000] "GET /api/cart HTTP/2.0" 200 512 "-" "Mozilla/5.0" rt=0.245 uct=0.002 uht=0.240 urt=0.241
203.0.113.11 - - [04/Feb/2026:12:03:52 +0000] "POST /upload HTTP/2.0" 413 166 "-" "curl/8.5.0" rt=0.003 uct=- uht=- urt=-
203.0.113.12 - - [04/Feb/2026:12:03:52 +0000] "GET /static/app.js HTTP/2.0" 200 183211 "https://example.com/" "Mozilla/5.0" rt=0.012 uct=- uht=- urt=-
Signification : rt (request time) et les champs de timing upstream montrent où le temps est passé. Pour /api/cart, presque tout le temps est upstream.
Décision : Ne tunez pas Nginx pour un problème de latence upstream. Allez vers l’app/BD. Pour le 413, ajustez client_max_body_size intentionnellement (et appliquez les limites produit).
15) Confirmer qu’Apache ne fait pas secrètement des parcours de système de fichiers par requête via AllowOverride
cr0x@server:~$ sudo apachectl -t -D DUMP_VHOSTS | head -n 12
VirtualHost configuration:
*:80 is a NameVirtualHost
default server example.com (/etc/apache2/sites-enabled/000-default.conf:1)
port 80 namevhost example.com (/etc/apache2/sites-enabled/000-default.conf:1)
Signification : L’emplacement de la config des vhosts est identifié. Vous devez encore inspecter la présence de AllowOverride dans les répertoires concernés.
Décision : Si vous trouvez AllowOverride All sur des chemins à fort trafic, envisagez de déplacer les règles dans la configuration centrale et de définir AllowOverride None pour la performance et la gouvernance.
Playbook de diagnostic rapide
Quand le site est « lent », ne commencez pas par éditer la config. Commencez par localiser le goulot avec le moins de cérémonie possible.
C’est l’ordre qui permet de gagner les incidents.
Première étape : est-ce l’edge ou l’upstream ?
- Vérifiez la latence edge vs latence upstream en utilisant les logs (champs de timing upstream Nginx ou Apache %D/%{…}).
- Vérifiez le mix des codes de statut : des pics de 499/504/502 crient « problème proxy/upstream », pas « serveur web lent ».
- Vérifiez les comptes de connexion : trop de connexions établies + RPS stable signifie souvent des clients lents ou un mauvais réglage des keepalive.
Deuxième étape : l’hôte est-il en manque de ressources ?
- CPU : un %system élevé peut signifier overhead TLS/compression ou pression noyau/réseau.
- Mémoire : le swap sur une couche web est un DoS auto-infligé. Si vous voyez de l’activité de swap, traitez-le comme une mauvaise configuration priorité 1.
- Latence IO disque : des temps await élevés peuvent bloquer la livraison de fichiers statiques et le buffering proxy.
Troisième étape : le réseau vous ment-il ?
- Rétransmissions et pertes : la perte de paquets transforme HTTP/2 en « une connexion qui souffre ».
- Débordement de backlog : des SYNs perdus ressemblent à des défaillances clients aléatoires.
- Problèmes MTU/PMTUD : particulièrement avec VPNs, réseaux d’entreprise et UDP/QUIC.
Quatrième étape : seulement alors touchez la config
Une fois que vous avez identifié où le temps est passé, ajustez le composant qui possède ce temps. Si l’upstream est lent, ne « scalez pas Nginx ».
C’est ainsi que vous obtenez une panne coûteuse avec de plus jolis graphiques.
Erreurs courantes : symptôme → cause racine → correctif
1) Symptôme : pics aléatoires de 502/504 pendant les déploiements
Cause racine : comportement de reload/restart non gracieux plus drainage des connexions upstream non géré ; health checks trop optimistes ; timeouts décalés.
Correctif : implémentez des reloads gracieux ; assurez-vous que les upstreams se vident ; alignez proxy_read_timeout (Nginx) / ProxyTimeout (Apache) sur les timeouts applicatifs ; réduisez le rayon d’impact des déploiements.
2) Symptôme : forte consommation mémoire, OOM kills sur Apache
Cause racine : MPM prefork avec forte concurrence ; mod_php chargé ; chaque processus porte une mémoire lourde.
Correctif : passez à event MPM + PHP-FPM ; limitez workers/threads ; mesurez le RSS par worker ; arrêtez de prétendre que la RAM est infinie.
3) Symptôme : Nginx « fonctionne » mais les uploads échouent ou sont lents
Cause racine : buffering de requête sur disque avec stockage lent ; client_max_body_size trop bas ; timeouts trop agressifs pour de gros uploads.
Correctif : fixez les limites de taille intentionnellement ; placez les chemins temp sur un stockage rapide ; envisagez les uploads directs vers un object store ; ajustez client_body_timeout et les timeouts upstream.
4) Symptôme : augmentation soudaine de TIME_WAIT et problèmes de ports éphémères en amont
Cause racine : keepalive désactivé entre proxy et upstream, provoquant de nouvelles connexions TCP par requête ; ou l’upstream ferme trop agressivement.
Correctif : activez des pools keepalive upstream (Nginx keepalive dans upstream) ; assurez-vous que le serveur app le supporte ; ajustez les timeouts keepalive de manière réfléchie.
5) Symptôme : « rapide en local, lent pour certains utilisateurs » après activation de HTTP/3
Cause racine : UDP bloqué ou limité sur certains réseaux ; comportement de fallback non testé ; angles morts d’observabilité.
Correctif : déployez avec un rollout progressif ; assurez un fallback propre vers HTTP/2 ; surveillez les taux d’erreur et d’échec de handshake par protocole.
6) Symptôme : pics CPU Apache après un « petit changement de config »
Cause racine : activation de règles de rewrite extensives par répertoire ; AllowOverride provoquant des vérifications filesystem ; rewrites regex lourdes exécutées à chaque requête.
Correctif : migrez les règles vers la config centrale ; définissez AllowOverride None quand possible ; simplifiez la logique de rewrite ; cachez les routes statiques à la périphérie.
7) Symptôme : Nginx sert du contenu périmé ou incorrect de façon intermittente
Cause racine : cache proxy configuré sans clé de cache correcte ; gestion manquante de Vary ; cache de réponses authentifiées.
Correctif : définissez explicitement la clé de cache ; bypasser le cache sur auth/cookies ; validez les en-têtes cache-control ; exécutez des contrôles canaris pour les pages authentifiées.
8) Symptôme : les clients voient « connection reset » sous charge, les logs semblent propres
Cause racine : débordement de backlog kernel, pression conntrack, ou resets upstream ; les logs du serveur web ne capturent souvent pas les SYNs jetés par le noyau.
Correctif : vérifiez les statistiques de débordement de file d’écoute ; augmentez les paramètres de backlog ; assurez un headroom CPU ; examinez la capacité du pare-feu/conntrack si pertinent.
Trois mini-récits d’entreprise du terrain
Mini-récit n°1 : L’incident causé par une mauvaise hypothèse
Une entreprise SaaS de taille moyenne a migré depuis une seule machine Apache vers « une pile moderne ». Ils ont mis Nginx devant, laissé la couche app
quasi inchangée, et déclaré victoire après un test de charge qui n’a touché que la page d’accueil. L’hypothèse : « Nginx gérera mieux la
concurrence, donc l’app ira bien. »
Deux semaines plus tard, une campagne marketing a atterri. Le trafic n’était pas fou, mais le comportement utilisateur a changé : plus d’uploads,
des sessions plus longues et beaucoup plus de clients mobiles lents. Nginx a fait son travail — il a gardé des connexions ouvertes, bufferisé poliment
et transféré les requêtes. L’app, cependant, était toujours dimensionnée comme en 2014 : pools PHP-FPM trop petits, requêtes lentes non bornées et des
requêtes BD prenant parfois des secondes.
Le symptôme visible était des 504 à la périphérie. L’équipe a blâmé Nginx parce qu’il était nouveau. Ils ont augmenté les processus workers,
augmenté les limites de fichiers et ajusté les keepalive. Les 504 ont empiré parce que le proxy s’était amélioré à alimenter l’upstream.
La correction a été embarrassante de simplicité : ajouter le timing upstream aux logs, caper le temps de requête, dimensionner les enfants PHP-FPM
selon la mémoire mesurée par worker, et ajouter un comportement de circuit-breaker pour les endpoints lents. Nginx n’était pas le problème ;
l’hypothèse que « la performance de l’edge égale la performance du système » l’était.
Mini-récit n°2 : L’optimisation qui s’est retournée contre eux
Une équipe plateforme interne d’entreprise a décidé de « standardiser » en passant tout en HTTP/2 partout et en activant une compression agressive à la périphérie.
L’objectif était noble : moins de connexions, chargements plus rapides, bande passante réduite. Ils l’ont déployé largement après des tests montrant une
latence médiane plus faible.
Puis la queue lente est arrivée. Un sous-ensemble d’utilisateurs sur des connexions VPN avec perte a commencé à voir des requêtes bloquer. Pas échouer immédiatement — bloquer.
Le p99 est devenu moche. Les tickets support parlaient de « chargement » et « parfois ça se débloque tout seul. »
L’observabilité n’a pas aidé tout de suite parce que les tableaux de bord étaient encore focalisés sur les moyennes et le débit total.
Cause racine : avec le multiplexage HTTP/2, une connexion unique avec perte pouvait dégrader plusieurs requêtes en vol. Combinez cela avec une haute charge CPU due à la compression
agressive et vous obtenez une tempête parfaite : les workers passent plus de temps à compresser et moins à accepter/servir, tandis que les clients font des retries qui amplifient la charge.
Ce n’était pas un bug HTTP/2 ; c’était une interaction entre conditions réseau, headroom CPU et choix de configuration.
Le « correctif » n’a pas été de désactiver HTTP/2 globalement. Ils ont réduit le niveau de compression, exclu les types déjà compressés,
ajouté du headroom et instrumenté la latence par protocole. La leçon est classique SRE : des optimisations qui améliorent la médiane peuvent nuire à la queue,
et c’est la queue qui déclenche les pages.
Mini-récit n°3 : La pratique ennuyeuse mais correcte qui a sauvé la mise
Une équipe de services financiers gérait un parc mixte : Nginx en périphérie, Apache derrière pour une app legacy, plus quelques services plus récents. Rien de glamour.
Mais ils avaient une règle : chaque changement de config doit passer un test de syntaxe, être déployé via la même pipeline et inclure un chemin de rollback automatisé.
Ils envoyaient aussi un format de logs standard incluant request IDs et timings upstream partout.
Un après-midi, un renouvellement de certificat a mal tourné — pas parce que TLS est difficile, mais parce qu’un refactor de config mineur a modifié le chemin du certificat
dans un environnement. L’edge a commencé à servir un ancien cert sur un sous-ensemble d’hôtes. Certains clients ont échoué brutalement, d’autres ont retry, et le support
a commencé à s’allumer.
Voici la partie ennuyeuse qui les a sauvés : ils avaient une alerte sur l’expiration/validité des certificats, et un check canari qui validait le sujet présenté et la date d’expiration
depuis plusieurs réseaux. L’incident a été détecté rapidement. La corrélation des logs par request ID a aussi prouvé que la latence applicative n’était pas impliquée, donc l’incident est resté limité.
Ils ont rollbacké en quelques minutes. Le postmortem a été presque décevant : pas d’héroïsme, pas de mystère. Juste un changement contrôlé, une détection rapide et une pipeline
qui ne demandait pas à un humain de se souvenir de douze étapes sous stress.
Listes de contrôle / plan pas à pas
Checklist A : choisir Nginx, Apache ou les deux
- Définissez le contrat d’edge : point de terminaison TLS, taille maximale du corps, timeouts et quels en-têtes vous faites confiance.
- Décidez si les overrides par répertoire sont autorisés. Si oui, Apache peut être justifié ; si non, préférez la config centralisée (souvent Nginx).
- Choisissez le modèle d’exécution de l’app : PHP-FPM ou serveur app derrière un proxy. Évitez de coupler le modèle de worker web au runtime app sauf raison forte.
- Documentez le comportement en cas de défaillance : que se passe-t-il quand l’upstream est lent ? Queue ? Rejet ? Servir du stale ? Cela doit être explicite.
- Choisissez une base d’observabilité : champs de logs, métriques, dashboards et alertes qui correspondent au contrat ci-dessus.
Checklist B : durcissement production pour l’un ou l’autre serveur
- Fixez les limites de descripteurs de fichiers (service et système). Validez avec
/proc/<pid>/limits. - Validez la configuration dans CI en utilisant
nginx -touapachectl -tavant déploiement. - Standardisez les formats de logs et incluez timing upstream et request IDs.
- Alignez les timeouts entre edge, upstream et application.
- Fixez des keepalive sensés entre clients et edge, et entre edge et upstream.
- Planifiez la stratégie de reload et testez-la en heures ouvrables sur un hôte canari.
- Protégez l’upstream avec buffering/limites pour que des clients lents ne bloquent pas les ressources applicatives.
Checklist C : plan de migration (Apache → Nginx edge) sans drame
- Inventaire des règles de rewrite, mécanismes d’auth et modules Apache-only dont vous dépendez.
- Commencez par un proxy en passthrough (Nginx en front, logique minimale). Prouvez la correction d’abord.
- Déplacez les assets statiques et le caching vers Nginx ensuite. Observez IO et en-têtes de cache.
- Migrez les rewrites prudemment. La parité regex est un piège ; testez avec des échantillons de requêtes réelles.
- Déployez par pourcentage (poids LB) ou par domaine/hôte. Gardez le rollback bon marché.
- Après une opération stable, décidez si Apache reste en upstream ou est retiré.
FAQ
1) Lequel est plus rapide : Nginx ou Apache ?
Pour la plupart des piles réelles, « plus rapide » est dominé par le temps applicatif et base de données. Nginx gère souvent la haute concurrence avec
moins de ressources en périphérie, mais Apache avec event MPM peut aussi être excellent. Choisissez selon le modèle opérationnel et les modes de défaillance.
2) Dois-je exécuter à la fois Nginx et Apache ?
Souvent oui : Nginx en périphérie, Apache derrière pour les apps legacy. Mais ne le faites pas par accident. Soyez explicite sur qui gère le TLS, le buffering et les timeouts.
3) .htaccess est-il toujours mauvais ?
Pas toujours — .htaccess est un outil pragmatique pour le contrôle délégué. Il est « mauvais » quand vous voulez un contrôle centralisé des changements, un comportement cohérent et des déploiements reproductibles.
4) Quel MPM Apache devrais-je utiliser en 2026 ?
Préférez event pour la plupart des configurations modernes. Utilisez prefork seulement si vous êtes contraint par des modules non thread-safe ou des exigences legacy, et dans ce cas prévoyez la mémoire en conséquence.
5) Pourquoi je vois 499 dans les logs Nginx ?
499 signifie que le client a fermé la connexion avant que Nginx ait pu répondre. Causes courantes : timeouts client, upstreams lents, pertes réseau mobile ou timeouts edge trop agressifs.
6) Quelle est la cause la plus courante d’un 504 Gateway Timeout ?
Lenteur ou saturation de l’upstream : threads/workers applicatifs épuisés, requêtes BD lentes, dépendances en aval paresseuses. Corrigez la capacité et la latence upstream d’abord ; puis alignez les timeouts du proxy sur la réalité.
7) Dois-je activer HTTP/3 partout ?
Activez-le quand vous pouvez le mesurer. Déployez progressivement, assurez un fallback propre et surveillez la latence et les taux d’erreur par protocole. Certains réseaux traiteront UDP comme suspect.
8) Nginx est-il meilleur pour Kubernetes et les microservices ?
Nginx est couramment utilisé, mais en environnement cluster votre choix d’ingress/LB peut importer plus que le serveur web par nœud. Les mêmes principes s’appliquent : buffering, timeouts, observabilité et contrôle des changements.
9) Apache peut-il faire du reverse proxy tout aussi bien ?
Oui, Apache peut faire du reverse proxy efficacement, surtout avec event MPM et les bons modules proxy. L’ergonomie opérationnelle diffère souvent : les configs Nginx tendent à être plus standardisées pour les rôles d’edge.
10) Quelle est la pile par défaut la plus sûre pour une app PHP ?
Nginx en périphérie + PHP-FPM pour l’exécution, avec des timeouts explicites, des limites de taille de requête, du keepalive upstream quand c’est approprié et le timing upstream dans les logs.
Prochaines étapes réalisables cette semaine
- Décidez la frontière : qui termine le TLS, qui bufferise, qui définit les timeouts et où les overrides par locataire sont autorisés.
- Instrumentez le timing upstream dans vos logs pour pouvoir répondre « edge ou app ? » en 30 secondes.
- Exécutez les tâches pratiques ci‑dessus sur un hôte de production et notez ce qui vous a surpris. Cette surprise est l’endroit où se cache votre prochain incident.
- Alignez les timeouts entre proxy et app. Supprimez les décalages qui génèrent des 504 factices ou des connexions bloquées.
- Éliminez la dérive de configuration : imposez des tests de config en CI, standardisez les procédures de reload et arrêtez de laisser des edits ad‑hoc devenir « notre méthode ».
Si vous voulez la vraie réponse 2026 : choisissez le serveur qui rend votre système plus observable, vos changements plus contrôlés et vos modes de défaillance plus prévisibles. Tout le reste n’est que débats Internet.