Délais d'expiration
OxPHP applique deux délais d'expiration indépendants qui protègent contre les clients lents et les requêtes incontrôlées. Le délai d'expiration des en-têtes protège la phase de connexion au niveau du serveur. Le temps d'exécution PHP est borné par la directive ini max_execution_time propre à PHP (et par la fonction d'exécution set_time_limit()), exactement comme sur n'importe quel autre SAPI.
Fonctionnement
Chaque requête passe par les phases suivantes :
- Connexion acceptée — le délai d'expiration des en-têtes démarre. OxPHP attend que le client envoie un ensemble complet d'en-têtes HTTP.
- En-têtes reçus — le délai d'expiration des en-têtes se termine. La requête est distribuée à un worker PHP.
- PHP traite la requête — le code applicatif s'exécute sous le
max_execution_timepropre à PHP (piloté par SIGALRM). Lorsque la limite est atteinte, la requête est annulée et l'erreur fatale unifiéeRequest cancelled (timeout)se déclenche. - Réponse envoyée — sur les connexions keep-alive, le cycle recommence à l'étape 1.
graph TD A["TCP connect<br/>(+ TLS handshake if enabled)"] -->|HEADER_TIMEOUT_SECONDS| B["Headers received"] B -->|max_execution_time| C["Response sent"] C -->|next request, keep-alive| A
Sur les connexions keep-alive, les deux délais d'expiration s'appliquent indépendamment à chaque requête de la connexion.
Lorsque TLS est activé, le délai d'expiration des en-têtes démarre une fois la poignée de main TLS terminée, et non au moment où la connexion TCP est acceptée.
Le délai d'expiration des en-têtes protège contre les attaques de type slowloris, où un client envoie les en-têtes octet par octet pour maintenir les connexions ouvertes indéfiniment.
La gestion du temps d'exécution PHP est entièrement déléguée à PHP. Lorsque max_execution_time est dépassé, le mécanisme d'annulation unifié d'OxPHP :
- Définit
connection_status() & PHP_CONNECTION_TIMEOUTpour que le code utilisateur puisse détecter la cause. - Exécute tous les callbacks
register_shutdown_function(), exactement comme le fait PHP-FPM. - Renvoie un HTTP
504 Gateway Timeoutavec le messageRequest cancelled (timeout)inscrit dans le journal des erreurs.
Codes de statut d'annulation
OxPHP annule une requête pour plusieurs raisons distinctes. Chacune correspond à un statut transmis sur le réseau qui reflète la condition réelle plutôt qu'un 500 générique :
| Cause | Statut | Notes |
|---|---|---|
max_execution_time / set_time_limit() dépassé |
504 Gateway Timeout |
Épuisement du temps d'exécution côté serveur. |
| L'arrêt gracieux du serveur a purgé la requête | 503 Service Unavailable |
Ajoute Retry-After: 5 pour que les clients réessaient sur une instance rétablie ou de remplacement. |
| Le client a fermé la connexion en cours de requête | 499 |
« Client Closed Request » à la manière de nginx. La connexion a déjà disparu, ce statut n'apparaît donc que dans les journaux d'accès et les métriques — il n'est jamais transmis sur le réseau. Il expose les abandons initiés par le client comme des non-5xx afin qu'ils ne polluent pas les alertes d'erreur serveur. |
| Worker déclaré bloqué par le superviseur | 500 Internal Server Error |
Erreur serveur générique — la cause (interblocage, appel système bloqué, …) est inconnue. |
| Annulation initiée par le code utilisateur | 500 Internal Server Error |
Le code utilisateur peut définir son propre statut avec http_response_code() avant de déclencher l'annulation ; ce statut explicite est préservé. |
Si votre ERROR_PAGES_DIR ne fournit qu'un 500.html, ajoutez 504.html, 503.html et (facultativement) 499.html pour garder des pages stylisées cohérentes pour toutes les causes d'annulation.
Configuration
| Variable | Valeur par défaut | Description |
|---|---|---|
HEADER_TIMEOUT_SECONDS |
5 |
Nombre maximal de secondes pour recevoir les en-têtes de la requête après l'acceptation de la connexion. Protège contre les attaques slowloris. 0 n'est pas traité de façon particulière — il est transmis tel quel à hyper comme un délai d'expiration de zéro seconde, qui se déclenche immédiatement. Pour désactiver le délai d'expiration, laissez la variable non définie plutôt que de la fixer à 0 |
Le temps d'exécution PHP se configure via php.ini, et non via les variables d'environnement d'OxPHP :
; php.ini
max_execution_time = 30Ou par script à l'exécution :
set_time_limit(60); // 60 seconds from now
set_time_limit(0); // disable for this requestValeurs recommandées
| Scénario | Délai d'expiration des en-têtes | max_execution_time |
|---|---|---|
| Serveur d'API | 5 s | 30 s |
| Service web général | 5 s | 60 s |
| Téléversements de fichiers | 10 s | 300 s |
| SSE / long-polling | 5 s | 0 (désactivé, à définir par script) |
Ajustez ces valeurs en fonction des caractéristiques de votre application. Pour les endpoints SSE, appelez set_time_limit(0) en tête du script de streaming plutôt que de désactiver max_execution_time globalement.
Dépannage
Les clients reçoivent un 504 avec « Request cancelled (timeout) » de manière inattendue
La limite de temps d'exécution PHP s'est déclenchée avant que le script ne se termine.
Solution : Augmentez max_execution_time pour le script concerné, ou appelez set_time_limit($seconds) pour l'étendre à l'exécution :
// at the top of a slow script
set_time_limit(300);Pour les endpoints SSE ou de streaming où les connexions doivent rester ouvertes indéfiniment, désactivez le minuteur pour ce script :
set_time_limit(0);Les connexions sont abandonnées avant l'arrivée des en-têtes
Le délai d'expiration des en-têtes est trop court pour les clients sur des liaisons à forte latence ou derrière des répartiteurs de charge lents.
Solution : Augmentez le délai d'expiration des en-têtes :
HEADER_TIMEOUT_SECONDS=15OPcache provoque un délai d'expiration lors de la première requête après une modification de script
La recompilation OPcache ajoute de la latence lors de la première requête après une modification de fichier. C'est plus fréquent dans les environnements de développement comportant de nombreux fichiers. Augmentez max_execution_time ou désactivez-le pendant le développement.
Exemple Docker
services:
app:
image: ghcr.io/oxphp/oxphp:0.10.0
ports:
- "8080:8080"
environment:
HEADER_TIMEOUT_SECONDS: "5"
volumes:
- ./app:/var/www/html:ro
- ./php.ini:/usr/local/etc/php/conf.d/zz-app.ini:roAvec php.ini contenant :
max_execution_time = 30Bonnes pratiques
- Ne définissez jamais
max_execution_time = 0globalement en production sauf si vous disposez d'endpoints SSE ou de long-polling qui nécessitent des connexions indéfinies. Préférezset_time_limit(0)par script. - Utilisez des limites plus courtes pour les serveurs d'API. Les API ont des temps de réponse prévisibles. Un
max_execution_timede 30 secondes détecte rapidement les requêtes bloquées sans affecter le trafic normal. - Combinez avec la limitation de débit. Les délais d'expiration protègent les requêtes individuelles ; la limitation de débit protège contre un volume élevé de requêtes. Ensemble, elles couvrent aussi bien les schémas d'attaque lents que rapides.
Voir aussi
- Limitation de débit — limitation du débit de requêtes par IP
- Server-Sent Events (SSE) — conseils sur la désactivation des délais d'expiration pour les endpoints de streaming
- Référence de configuration — référence complète des variables d'environnement