Pages d'erreur personnalisées

OxPHP sert des pages d'erreur HTML personnalisées pour les réponses 4xx et 5xx. Chaque page est lue depuis le disque une seule fois au démarrage et servie depuis la mémoire, de sorte qu'aucune E/S disque ne se produit pendant le traitement des requêtes.

Fonctionnement

  1. Chargement au démarrage. Au démarrage, OxPHP lit le répertoire indiqué par ERROR_PAGES_DIR et charge en mémoire chaque fichier {status}.html valide.
  2. Règles de nommage. Les fichiers doivent être nommés avec un code de statut HTTP numérique compris dans la plage 400–599 (par exemple, 404.html, 503.html). Les fichiers portant un nom non numérique, un code de statut hors de cette plage (y compris 200.html) ou une extension autre que .html sont ignorés silencieusement.
  3. Remplacement du corps. Lorsqu'OxPHP génère une réponse 4xx ou 5xx, il vérifie s'il existe une page d'erreur préchargée correspondante. Si c'est le cas, OxPHP ne remplace que le corps et les en-têtes qui le décrivent : Content-Type est défini à text/html; charset=utf-8, Content-Length à la taille de la page, et les en-têtes liés au corps d'origine (Content-Encoding, ETag, Last-Modified) sont supprimés afin qu'ils ne puissent ni étiqueter à tort ni revalider le remplacement (par exemple, un corps d'erreur PHP compressé par ob_gzhandler ne laissera pas la page HTML marquée Content-Encoding: gzip). Les en-têtes qui décrivent la sémantique de la réponse plutôt que le corps sont conservés dans la page personnalisée : Content-Range sur un 416 Range Not Satisfiable, Retry-After sur un 529 Site is overloaded, et Allow sur un 405 Method Not Allowed.
  4. Repli. Si le répertoire n'existe pas ou ne peut pas être lu au démarrage, OxPHP enregistre un avertissement et continue sans pages d'erreur personnalisées. Les réponses d'erreur se replient sur des corps en texte brut jusqu'à ce que le répertoire soit corrigé et le serveur redémarré.

Configuration

Variable Valeur par défaut Description
ERROR_PAGES_DIR (non défini) Répertoire contenant les fichiers HTML des pages d'erreur personnalisées. Les fichiers doivent être nommés {status}.html pour les codes de statut 400–599. Lorsqu'elle n'est pas définie, les réponses d'erreur utilisent des corps en texte brut

Exemples de pages

Chaque page d'erreur est un fichier HTML autonome nommé {status}.html. Gardez-les avec des styles en ligne et sans ressources externes : une requête secondaire qui échouerait casserait sinon la page d'erreur elle-même.

Modèle réutilisable

Copiez ceci dans chaque {status}.html et modifiez le <title>, le <h1> et le <p> :

{status}.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>500 — Internal Server Error</title> <style> body { font-family: system-ui, sans-serif; text-align: center; padding: 4rem 1rem; color: #333; } h1 { font-size: 2rem; margin-bottom: 0.5rem; } p { color: #666; } </style> </head> <body> <h1>Something went wrong</h1> <p>Please try again in a moment.</p> </body> </html>

Codes de statut à fournir

OxPHP remplace le corps de chaque 4xx ou 5xx qui atteint le pipeline de réponse. Voici les codes qu'il renvoie de lui-même, fournissez donc un fichier pour chacun :

Fichier Statut Quand OxPHP le renvoie
400.html Bad Request Une requête QUERY (RFC 10008) envoyée sans en-tête Content-Type
404.html Not Found Aucun fichier ou route correspondant ; un dotfile bloqué (.env, .git/) ; une requête .php directe en mode Framework ; le repli par défaut de PHP_DENY_PATHS
413.html Payload Too Large Le corps de la requête dépasse la taille maximale
416.html Range Not Satisfiable Un en-tête Range non satisfiable sur un fichier statique (Content-Range est conservé)
500.html Internal Server Error Une erreur PHP non interceptée ou fatale
503.html Service Unavailable Drainage gracieux pendant l'arrêt
504.html Gateway Timeout La requête a dépassé REQUEST_TIMEOUT_SECONDS
529.html Site is overloaded La file d'attente des requêtes est pleine à QUEUE_CAPACITY (Retry-After est conservé)

Tout autre 4xx ou 5xx fonctionne de la même manière : ajoutez un 403.html, un 451.html, et ainsi de suite pour les codes que renvoie votre application PHP, ou pour un statut PHP_DENY_FALLBACK personnalisé. La seule exception est le 429 du limiteur de débit, qui est produit avant l'exécution de ce gestionnaire et utilise toujours son corps par défaut (voir la note ci-dessous).

Exemples prêts à l'emploi

Une page 404 minimale :

404.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>404 - Page Not Found</title> <style> body { font-family: system-ui, sans-serif; text-align: center; padding: 4rem 1rem; color: #333; } h1 { font-size: 2rem; margin-bottom: 0.5rem; } p { color: #666; } </style> </head> <body> <h1>Page Not Found</h1> <p>The page you requested does not exist.</p> </body> </html>

Une page de maintenance 503 avec rafraîchissement automatique :

503.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="refresh" content="30"> <title>503 - Service Unavailable</title> <style> body { font-family: system-ui, sans-serif; text-align: center; padding: 4rem 1rem; color: #333; } h1 { font-size: 2rem; margin-bottom: 0.5rem; } p { color: #666; } </style> </head> <body> <h1>Service Unavailable</h1> <p>We are performing maintenance. This page will refresh automatically.</p> </body> </html>

Dépannage

Les pages d'erreur personnalisées n'apparaissent pas

Vérifiez qu'ERROR_PAGES_DIR est défini et que les fichiers sont nommés correctement.

Vérification : Confirmez le chemin du répertoire actif et qu'OxPHP a bien enregistré des lignes « Loaded custom error page » au démarrage :

bash
docker logs my-app 2>&1 | grep "error page"

Correctif : Assurez-vous que le chemin du répertoire est correct, que les fichiers sont nommés {status}.html et que le conteneur dispose d'un accès en lecture au répertoire.

Avertissement au démarrage concernant un répertoire de pages d'erreur manquant

OxPHP enregistre un avertissement et continue sans pages d'erreur personnalisées si le répertoire ERROR_PAGES_DIR n'existe pas ou ne peut pas être lu. Les réponses d'erreur utilisent alors des corps en texte brut. Vérifiez que le volume est correctement monté dans Docker :

bash
docker run --rm -v ./errors:/var/www/errors:ro \ -e ERROR_PAGES_DIR=/var/www/errors \ ghcr.io/oxphp/oxphp:0.10.0
Une réponse 429 affiche toujours le corps par défaut

Certaines réponses générées avant l'exécution du pipeline de réponse — comme les rejets liés à la limitation de débit — ne sont pas traitées par le gestionnaire de pages d'erreur. La réponse 429 Too Many Requests du limiteur de débit utilise son corps par défaut, qu'un fichier 429.html soit présent ou non.

Exemple Docker

compose.yaml
services: app: image: ghcr.io/oxphp/oxphp:0.10.0 ports: - "8080:8080" volumes: - ./src:/var/www/html:ro - ./errors:/var/www/errors:ro environment: ERROR_PAGES_DIR: "/var/www/errors" ENTRY_FILE: "index.php"

Structure du répertoire :

text
project/ src/ public/ index.php errors/ 400.html 403.html 404.html 500.html 503.html 504.html 529.html

Bonnes pratiques

  • Gardez les pages d'erreur autonomes avec du CSS en ligne. Ne référencez pas de feuilles de style ou de scripts externes : ces requêtes secondaires pourraient elles-mêmes échouer.
  • Incluez une balise <meta http-equiv="refresh" content="30"> dans 503.html afin que les utilisateurs réessaient automatiquement une fois la maintenance terminée.
  • Gardez les pages d'erreur légères. Chaque page chargée est conservée en mémoire pendant toute la durée de vie du processus serveur.
Note

Les pages d'erreur personnalisées s'appliquent aux réponses qui transitent par le pipeline de requêtes normal. La réponse 429 Too Many Requests du limiteur de débit est générée avant l'exécution du gestionnaire de pages d'erreur et utilise son corps par défaut en texte brut.

Voir aussi