Erreur HTTP 503 : diagnostiquer PHP et logs Apache/LiteSpeed

Diagnostic pas-à-pas d’un 503 : qui renvoie le code, quelles URLs, lire access/error logs Apache et logs LiteSpeed, différencier OOM, timeout, saturation et appliquer des correctifs.

Ordinateur portable affichant une erreur HTTP 503 avec des écrans de code en arrière-plan.

Table des matières :

  1. Comprendre ce que signifie vraiment un HTTP 503 côté Apache/LiteSpeed + PHP
  2. Qualifier le symptôme avant d’ouvrir les logs (sinon vous allez perdre 2 heures)
  3. Apache 2.4 : trouver la cause du 503 dans access/error log (et pas dans l’admin PrestaShop)
  4. LiteSpeed / OpenLiteSpeed : lire les bons logs (sinon vous passerez à côté de lsphp)
  5. PHP (FPM, mod_php, LSAPI) : différencier OOM, timeout, saturation et crash natif
  6. Cas concrets PrestaShop qui finissent en 503 (et comment les prouver par les logs)
  7. Stabiliser durablement : limites, observabilité, et 503 “propre” quand c’est volontaire

Comprendre ce que signifie vraiment un HTTP 503 côté Apache/LiteSpeed + PHP

Un HTTP 503 (Service Unavailable) n’est pas une “erreur PHP” par définition. C’est un code émis par la couche HTTP (proxy/CDN, serveur web, ou application) quand la requête ne peut pas être servie temporairement. La définition normative est claire :

« The 503 (Service Unavailable) status code indicates that the server is currently unable to handle the request due to a temporary overload or scheduled maintenance. » (RFC 9110, https://www.rfc-editor.org/rfc/rfc9110.html)

En pratique, un 503 peut être déclenché aussi bien par un pool PHP-FPM saturé que par un backend indisponible derrière un reverse-proxy (HAProxy, ALB, Varnish, CDN, etc.). C’est important car ça change totalement l’endroit où vous cherchez la cause.

Dans un stack e-commerce classique (PrestaShop 8.x/9.x sur PHP 8.2/8.3, Apache 2.4 ou LiteSpeed/OpenLiteSpeed), le chemin d’une requête ressemble à : CDN/WAF → serveur web → (PHP-FPM/LSAPI) → MySQL/Redis/externes. Un 503 peut sortir à plusieurs endroits :

  • CDN/WAF : maintenance, rate-limiting, “origin unreachable”, blocage pays/ASN, ou “health check” en échec.
  • Apache/LiteSpeed : manque de workers, timeouts, backend PHP non joignable.
  • Application/PHP : beaucoup plus rare en “pur”, mais certaines surcouches (proxy, règles de maintenance, plugins) peuvent transformer une indisponibilité applicative en 503.

Un repère utile : 503 ≠ 502 ≠ 504.

  • 502 : le proxy a parlé au backend, mais réponse invalide (ou handshake cassé).
  • 504 : le proxy a attendu le backend trop longtemps.
  • 503 : le serveur dit “je suis indisponible” (souvent surcharge ou maintenance), ou un composant amont choisit de le dire à sa place.

Côté SEO, 503 est un code utile si vous êtes en maintenance planifiée.

Astuce “maintenance propre” (souvent oubliée) : si vous servez une page statique en 503, ajoutez aussi des headers anti-cache si vous ne voulez pas qu’un proxy/CDN la conserve trop longtemps :

HTTP/1.1 503 Service Unavailable
Retry-After: 3600
Cache-Control: no-store, no-cache, must-revalidate
Content-Type: text/html; charset=utf-8

Qualifier le symptôme avant d’ouvrir les logs (sinon vous allez perdre 2 heures)

Avant d’aller “grepper au hasard”, commencez par répondre à trois questions : qui renvoie le 503, sur quelles URLs, dans quel contexte de charge.

Un 503 uniquement sur /admin*/ n’a rien à voir avec un 503 sur /order en pleine campagne. Un 503 sur toutes les URLs, mais uniquement via Cloudflare/Proxy/CDN, pointe souvent vers un souci d’origin, de firewall ou de health checks, pas vers PrestaShop.

Sur un serveur accessible en SSH, capturez une trace reproductible avec curl en incluant headers + timing. Exemples :

# Headers + code
curl -sS -D- -o /dev/null https://example.com/

# Détails et timing (TTFB, total)
curl -sS -o /dev/null -w "code=%{http_code} ttfb=%{time_starttransfer} total=%{time_total}\n" https://example.com/

# En cas de proxy/CDN, comparez avec l'IP d'origine si possible
curl -sS -H 'Host: example.com' http://ORIGIN_IP/ -D- -o /dev/null

Deux compléments très rentables pour qualifier “qui parle” :

  • repérer les en-têtes typiques (server, via, cf-ray, x-varnish, x-litespeed-cache, etc.) ;
  • tester plusieurs points réseau si vous suspectez un souci géographiquement localisé (par exemple un POP CDN en France vs un autre en Europe). Concrètement, vous pouvez refaire les mêmes curl depuis une VM en région parisienne (Scaleway PAR) et une VM ailleurs, ou via une sonde externe, pour vérifier si le 503 est global ou “local au chemin réseau”.

Si vous observez des 503 “en rafale” sous charge, documentez le pattern (taux 5xx/min, pages touchées, UA). Une façon simple de structurer votre collecte (à coller dans un ticket) :

  • fenêtre horaire (UTC + fuseau local)
  • URLs les plus touchées (top 10)
  • impact (checkout, recherche, pages produit)
  • périmètre (uniquement mobile ? uniquement un pays ? uniquement via CDN ?)
  • timing (TTFB élevé avant 503 ? ou 503 “instantané” ?)

Pour des boutiques PrestaShop, pensez aux pics provoqués par bots + facettes + recherche, qui font exploser le nombre de requêtes PHP simultanées : l’article PrestaShop lent : diagnostic TTFB, bots et optimisation serveur donne une grille de lecture très opérationnelle pour corréler TTFB et saturation serveur.

Enfin, ne négligez pas le “hors PHP” : déploiement récent, purge cache, rotation de certificats, changement DNS, montée de version. Si votre boutique a été reprise/migrée récemment, un pipeline CI/CD mal paramétré peut créer des fenêtres où PHP-FPM redémarre alors que le trafic continue d’arriver. Sur ce point, la démarche décrite dans PrestaShop : modernisation et reprise de boutique avec déploiements automatisés aide à éviter les redéploiements “à chaud” qui déclenchent du 503.

Apache 2.4 : trouver la cause du 503 dans access/error log (et pas dans l’admin PrestaShop)

Sous Apache, le 503 se diagnostique d’abord dans l’access log pour identifier quelles requêtes sont concernées, puis dans l’error log pour comprendre pourquoi elles échouent. Chemins fréquents (selon distro/panel) :

  • Debian/Ubuntu : /var/log/apache2/access.log et /var/log/apache2/error.log
  • RHEL/CentOS/Alma : /var/log/httpd/access_log et /var/log/httpd/error_log

Pour isoler rapidement les 503 :

# Lister les 503 (aperçu rapide)
grep ' 503 ' /var/log/apache2/access.log | tail -n 50

# Top URLs qui 503 (log au format "combined" classique)
awk '$9==503 {print $7}' /var/log/apache2/access.log | sort | uniq -c | sort -nr | head -n 15

# Top IP sources sur les 503 (utile si bots / abus)
awk '$9==503 {print $1}' /var/log/apache2/access.log | sort | uniq -c | sort -nr | head -n 15

# Voir les erreurs autour d'un timestamp (adaptez)
grep -n "\[25/Aug/2026:14:" /var/log/apache2/error.log | tail -n 200

Les messages Apache les plus utiles quand vous êtes en MPM event + PHP-FPM (proxy_fcgi) : timeouts, backend down, saturation de workers. Typiquement, vous allez voir des erreurs du type “timeout” ou “connection refused” vers le socket FPM (/run/php/php8.2-fpm.sock) ou vers un port TCP. Dans ce scénario, le 503 n’est pas “une page PrestaShop”, c’est Apache qui n’arrive pas à parler à FPM.

Mini-scénario très courant : PHP-FPM redémarre (upgrade, reload, crash), et pendant quelques secondes Apache essaye de proxyfier vers un socket absent → connection refused → 503/5xx. Si vous retrouvez une rafale courte de 503 exactement au moment d’un reload, ce n’est pas un bug applicatif : c’est de l’orchestration (et souvent corrigeable par des reload “gracieux” + readiness checks).

Autre cause classique : saturation côté Apache lui-même. La doc Apache est explicite sur MaxRequestWorkers :

« The MaxRequestWorkers directive sets the limit on the number of simultaneous requests that will be served. » (Apache HTTP Server 2.4, https://httpd.apache.org/docs/2.4/mod/mpm_common.html#maxrequestworkers)

Quand cette limite est atteinte, Apache met les requêtes en file d’attente et, selon la chaîne proxy/backends et les timeouts, vous pouvez finir en 503 (ou en 504). En production, une preuve concrète consiste à activer (temporairement) mod_status et vérifier le nombre de workers “Busy” (et si vous voyez une saturation continue).

Exemple minimal (à restreindre par IP — typiquement votre IP de VPN ou de bastion) :

<Location "/server-status">
    SetHandler server-status
    Require ip 203.0.113.10
</Location>

Si vous êtes déjà dans une logique de monitoring, instrumentez ces métriques au lieu de les découvrir lors d’une panne (voir Netdata : intégrer Prometheus, SNMP et StatsD pour tout surveiller).

LiteSpeed / OpenLiteSpeed : lire les bons logs (sinon vous passerez à côté de lsphp)

Sur LiteSpeed, l’erreur “503 Service Unavailable” est souvent générée par le serveur web parce que l’External App (lsphp) ne répond plus, ou parce que les limites de connexions/process sont atteintes. Les chemins de logs varient selon installation, mais le cas standard est :

  • LiteSpeed Enterprise / OpenLiteSpeed : /usr/local/lsws/logs/error.log
  • stderr PHP (souvent plus parlant) : /usr/local/lsws/logs/stderr.log
  • logs vhost/domain : configurés dans le WebAdmin ou via le panel (cPanel/DirectAdmin)

Diagnostic minimal en SSH :

sudo tail -n 200 /usr/local/lsws/logs/error.log
sudo tail -n 200 /usr/local/lsws/logs/stderr.log
sudo grep -R " 503 " -n /usr/local/lsws/logs/ | tail -n 50

Ce que vous cherchez concrètement : des mentions de “ExtApp”, de LSAPI, de “max connections”, de “timeout”, ou des erreurs système (OOM killer, “cannot allocate memory”, “too many open files”). Une erreur “too many open files” est particulièrement traître : côté navigateur vous voyez juste 503, mais la cause est souvent une limite ulimit -n trop basse ou une fuite de file descriptors (ou un pic de connexions).

Deux commandes rapides pour vérifier si vous êtes dans un problème de ressources “global serveur” pendant les 503 :

# Voir si le noyau a tué des process (OOM killer)
journalctl -k --since "2026-08-25 14:00" | grep -i -E "oom|killed process" | tail -n 50

# Voir la limite de fichiers ouverts (session courante)
ulimit -n

Point d’attention : LiteSpeed est fréquemment déployé avec des options de cache (LSCache côté serveur) qui peuvent masquer un problème jusqu’à ce que vous tombiez sur une URL non cachée (panier, compte, endpoints AJAX). Résultat : “le site marche” sur 90% des pages, mais le checkout renvoie du 503. Si vous avez déjà travaillé la stratégie de cache, recalez votre diagnostic avec les bonnes pratiques décrites dans Cache PrestaShop : stratégie 2026 OPcache, Redis, Varnish et CDN : c’est exactement le genre de situation où une couche cache cache la fumée mais pas le feu.

PHP (FPM, mod_php, LSAPI) : différencier OOM, timeout, saturation et crash natif

Un 503 “vu par le navigateur” cache très souvent un problème de capacité PHP : trop de requêtes simultanées, scripts trop lents, ou mémoire insuffisante. Selon votre mode d’exécution :

  • Apache + mod_php : les erreurs PHP remontent dans error.log Apache.
  • Apache + PHP-FPM : logs PHP-FPM dédiés + logs Apache “proxy_fcgi”.
  • LiteSpeed + lsphp (LSAPI) : erreurs PHP dans stderr.log LiteSpeed et parfois dans un php_errorlog par vhost.

Sur un serveur systemd, commencez par l’état des services et le journal :

# PHP-FPM (adaptez la version)
systemctl status php8.2-fpm --no-pager
journalctl -u php8.2-fpm -S "2026-08-25 14:00" | tail -n 200

# Apache
systemctl status apache2 --no-pager || systemctl status httpd --no-pager
journalctl -u apache2 -S "2026-08-25 14:00" | tail -n 200

Signatures typiques à repérer :

  • Saturation FPM : server reached pm.max_children, pool www seems busy, listen queue non nulle.
  • Timeout : request_terminate_timeout, ou côté Apache ProxyTimeout/Timeout trop bas (ou l’inverse : timeouts trop hauts qui “empilent” les requêtes).
  • OOM : Allowed memory size of ... bytes exhausted (niveau PHP) ou OOM killer (niveau kernel : journalctl -k).
  • Crash natif : child exited on signal 11 (SIGSEGV) (souvent extension buggée, opcache, image processing, librairie C).

Un bon réflexe de dimensionnement (simple mais efficace) : estimer le coût RAM d’un worker PHP en production, pour ensuite fixer pm.max_children sur une base rationnelle.

# Voir RSS (RAM) des process PHP-FPM (indicatif)
ps -ylC php-fpm8.2 --sort:rss | tail -n 10

Ensuite, vous calculez : RAM dispo pour PHP / RSS moyen d’un child = max_children théorique, puis vous gardez une marge (MySQL, Redis, OS, cache disque, etc.). Ce n’est pas “parfait”, mais ça évite le tuning à l’aveugle.

Si la charge est le problème, vous ne réglerez pas ça en “augmentant juste la RAM” sans comprendre la cause. Activez un slowlog PHP-FPM (et un request_slowlog_timeout réaliste) pour identifier les scripts bloquants, puis corrélez avec les requêtes SQL lentes. Pour le volet SQL, l’article Logs Amazon Lightsail : activer general log et slow query MySQL donne une procédure exploitable ; pour le volet PHP, Optimisation code source : audit performance PHP/Symfony avec profilage explique comment sortir du diagnostic “à l’aveugle” avec des profils concrets.

Référence pratique (officielle) si vous devez vérifier précisément les paramètres FPM et leur signification : https://www.php.net/manual/en/install.fpm.configuration.php

Cas concrets PrestaShop qui finissent en 503 (et comment les prouver par les logs)

PrestaShop n’émet pas “nativement” du 503 sur un fonctionnement nominal ; quand ça arrive, c’est généralement la conséquence d’un goulot d’étranglement ou d’une exécution trop coûteuse. Les déclencheurs récurrents en prod :

1) Indexation et recherche (notamment facettes) qui font exploser le CPU/PHP
2) Imports CSV/XML/API mal chunkés (un “gros” import en une requête = pic mémoire + verrouillages)
3) Tâches cron (nettoyage panier, emails, synchronisations) lancées en concurrence avec le trafic
4) Régénération d’images ou traitements média (GD/Imagick)
5) Modules qui s’accrochent à des hooks globaux et ajoutent de la latence sur toutes les pages

Comment “prouver” sans débat d’opinion :

  • vous partez d’une URL qui 503 dans l’access log (ou dans une alerte)
  • vous récupérez timestamp + IP + URL + user-agent
  • vous cherchez la même fenêtre dans les logs PHP / PrestaShop / MySQL
  • vous vérifiez si c’est la même cause qui se répète (toujours le même endpoint, le même module, la même requête SQL, le même client)

Exemple d’approche “evidence-driven” à partir d’Apache (repérer une rafale, puis pivoter) :

# Rafales de 503 avec timestamp (combined log : $4 contient la date)
awk '$9==503 {print $4, $7, $1, $12}' /var/log/apache2/access.log | tail -n 80

Sur PrestaShop 1.7/8.x, les logs applicatifs Symfony sont généralement sous var/logs/ (ou var/log/ selon votre build). En prod, vérifiez aussi les logs “legacy” si un module écrit encore dans des fichiers custom. Si vous devez activer _PS_MODE_DEV_, faites-le sur staging, pas sur le frontal prod : c’est un risque sécurité + performance.

Mini-scenario typique “checkout qui 503 mais le reste va bien” :

  • les pages catalogue sont servies par cache (serveur/LSCache/CDN)
  • les pages panier/commande ne sont pas cachées, donc tapent PHP + DB à chaque fois
  • dès qu’il y a un pic (ads, emailing, bots, ou cron), le pool PHP atteint sa limite → 503

Lorsque le problème est “structurel” (trop de logique business dans le cycle HTTP), la solution n’est pas dans Apache/LiteSpeed mais dans l’architecture : découper l’import, passer certains traitements en asynchrone, limiter les hooks, tester les modules sans override, etc. Deux contenus internes utiles pour cadrer ça proprement : Import PrestaShop : automatiser catalogues produits via flux XML CSV API (pour éviter les imports “monolithiques” qui saturent PHP) et Module PrestaShop sans override : méthode de cadrage, tests et livraison du code (pour réduire les risques de régression et de surcoût runtime).

Stabiliser durablement : limites, observabilité, et 503 “propre” quand c’est volontaire

Une fois la panne identifiée, stabiliser veut dire : mettre des limites explicites, mesurer, et faire des déploiements réversibles.

Côté serveur web/PHP, ça passe par des valeurs cohérentes de MaxRequestWorkers (Apache) ou de limites de process (LiteSpeed), et par un dimensionnement FPM (pm.max_children, pm.max_requests, request_terminate_timeout) basé sur des métriques (RAM par worker, latence P95/P99, longueur de queue). Les changements doivent être testés en staging et appliqués avec reload propre (systemctl reload ...) ; un mauvais “restart” au mauvais moment peut créer du 503 (le fameux : “j’ai corrigé, et ça a coupé”).

Checklist simple “stabilité” (utile pour éviter le yo-yo) :

  • Limites connues et versionnées (infra-as-code ou au minimum changelog)
  • Timeouts cohérents entre CDN/WAF ↔ serveur web ↔ PHP ↔ DB
  • Un slowlog PHP-FPM activé (même si temporaire) pour identifier les appels lents
  • Un garde-fou contre les tâches concurrentes (cron avec lock, queue, ou découpage)
  • Des seuils d’alerte (5xx/min, saturation pool PHP, RAM, swap, OOM)

Ensuite, vous devez voir venir la saturation au lieu de la constater via un ticket “site down”. À minima : taux de 5xx, temps de réponse, load average, mémoire, et métriques PHP-FPM (status page) + Apache (server-status). Netdata fait le job rapidement si vous n’avez pas déjà un stack Prometheus/Grafana (voir Netdata collecteurs : supervision temps réel Kubernetes, Docker et AWS).

Enfin, si vous avez besoin de renvoyer du 503 volontairement (maintenance, migration, restauration), faites-le proprement : page statique légère, Retry-After, pas de redirections 302 en boucle, pas de 200 avec un message “maintenance” (mauvais pour le SEO, mauvais pour les outils).

Pour tout le reste, un 503 est un symptôme : tant que vous n’avez pas relié une ligne d’access log → une erreur serveur/PHP → une cause applicative ou infra, vous ne “corrigez” pas, vous déplacez le problème (et il reviendra au prochain pic : soldes, Black Friday, ou même un simple bot qui découvre une combinatoire de facettes).


À lire aussi