Routage
OxPHP achemine les requêtes HTTP entrantes selon l'un des trois modes, contrôlé par une seule variable d'environnement. Chaque mode reflète une configuration nginx try_files familière, ce qui vous permet de prédire exactement ce qui se passe pour n'importe quelle URL.
Fonctionnement
Chaque requête passe par un pipeline partagé avant que la logique propre au mode n'entre en jeu :
- Filtre des chemins cachés — les chemins contenant des segments masqués (
.git,.env) sont bloqués, avec une exception pour/.well-known/*(RFC 8615) - Consultation du cache de routes — les URI récemment résolues sont renvoyées depuis un cache LRU (10 000 entrées)
- Décodage des pourcentages + assainissement — les séquences encodées comme
%2e%2esont décodées et les segments de traversée (..,., vides) sont supprimés - Blocage PHP well-known — défense en profondeur : les scripts
.phpsitués dans/.well-known/ne s'exécutent jamais - Classification des URI — le chemin assaini est classé une seule fois en
NoExtension,PhpouOtherExtension - Répartition selon le mode — chaque mode gère les trois types d'URI avec ses propres règles
- Validation des liens symboliques — chaque chemin de système de fichiers résolu doit se canonicaliser à l'intérieur de la racine du document
L'étape de classification est la clé de l'efficacité : la vérification sur disque des fichiers statiques (/style.css, /logo.png) est effectuée une seule fois dans la couche partagée pour les URI OtherExtension, de sorte que les trois modes paient le même coût.
Configuration
| Variable | Défaut | Description |
|---|---|---|
DOCUMENT_ROOT |
/var/www/html/public |
Répertoire racine pour servir les fichiers et les scripts PHP |
ENTRY_FILE |
(non défini) | Script d'entrée canonique unique. Non défini = Traditionnel. *.php = Framework. Non-.php = SPA. Avec WORKER_MODE_ENABLED=true = Worker. Accepte un chemin absolu ou relatif à DOCUMENT_ROOT (.. autorisé) ; le chemin résolu doit exister |
WORKER_MODE_ENABLED |
false |
Active le mode worker persistant. Nécessite que ENTRY_FILE pointe vers un script .php |
Les anciennes variables INDEX_FILE et WORKER_FILE sont toujours analysées (avec un WARN au démarrage) et se rattachent au nouveau modèle. Voir Configuration → Déprécié.
Modes de routage
Les modes Traditionnel, Framework et SPA sont sélectionnés par ENTRY_FILE lorsque WORKER_MODE_ENABLED=false, et chacun correspond à une configuration nginx try_files équivalente.
Actif lorsque ENTRY_FILE n'est pas défini (ou vide) et que WORKER_MODE_ENABLED=false. Configuration nginx équivalente :
location / {
try_files $uri $uri/ /index.php /index.html =404;
}
location ~ \.php$ {
try_files $uri =404; # PATH_INFO splitting enabled
}Ordre de résolution :
$uri— fichier exact sur le disque → servi (ou exécuté si.php)$uri/— répertoire → recherche deindex.php, puis deindex.htmlà l'intérieur- Découpage PATH_INFO — lorsque l'URI contient
.php/, le préfixe du script est mis en correspondance sur le disque et le reste devientPATH_INFO(par ex./api.php/users/42→ scriptapi.php,PATH_INFO=/users/42) /index.php— repli sur le contrôleur frontal racine/index.html— repli sur l'index statique racine- 404
Exemples :
| Requête | Résultat |
|---|---|
/about.php |
Exécute about.php |
/style.css |
Sert style.css |
/blog/ (avec blog/index.php) |
Exécute blog/index.php |
/api.php/users/42 |
Exécute api.php avec PATH_INFO=/users/42 |
/missing.txt |
Repli sur /index.php |
/some/route |
Repli sur /index.php |
Le découpage PATH_INFO est toujours actif en mode Traditionnel. Il n'existe pas de bascule d'environnement — l'ancien indicateur SPLIT_PATH_INFO_ENABLED a été supprimé.
Actif lorsque ENTRY_FILE=index.php (ou toute valeur se terminant par .php) et que WORKER_MODE_ENABLED=false. Configuration nginx équivalente :
location ~ \.(?!php$)[a-zA-Z0-9]+$ {
try_files $uri /index.php; # static assets: fall back to front controller
}
location / {
rewrite ^ /index.php last; # everything else → front controller
}
location = /index.php {
fastcgi_split_path_info ^(.+\.php)(/.*)$;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_pass ...;
}Règles de résolution :
| Type d'URI | Comportement |
|---|---|
.css, .png, .js, … (toute extension non-php) |
Sert le fichier s'il existe, sinon réécriture vers /index.php |
.php (n'importe quel chemin) |
Réécriture vers /index.php |
sans extension (/api/users, /) |
Réécriture vers /index.php |
PATH_INFO n'est défini que lorsque la requête nomme explicitement le fichier d'entrée avec un segment final (/index.php/extra) ; pour les routes applicatives, le chemin d'origine est lu depuis REQUEST_URI.
Exemples :
| Requête | Résultat | $_SERVER['PATH_INFO'] |
|---|---|---|
/style.css (existe) |
Sert style.css |
— |
/style.css (manquant) |
Exécute index.php |
(absent) |
/api/users |
Exécute index.php |
(absent) |
/about.php |
Exécute index.php |
(absent) |
/index.php/news/local |
Exécute index.php |
/news/local |
/index.php (direct) |
Exécute index.php |
(absent) |
/ |
Exécute index.php |
(absent) |
Pour les routes applicatives, le chemin d'origine est exposé via REQUEST_URI, de sorte que votre routeur lit $_SERVER['REQUEST_URI'] pour effectuer la répartition. L'accès direct à /index.php n'est plus bloqué — la réécriture est idempotente, donc l'atteindre directement produit le même résultat que d'atteindre /.
Un fichier statique manquant se replie sur le contrôleur frontal plutôt que de renvoyer un 404 immédiat — c'est le même comportement try_files $uri /index.php que Laravel et Symfony fournissent par défaut, de sorte que votre application rend sa propre page 404 pour les fichiers manquants. Le compromis, c'est que chaque requête vers un fichier inexistant exécute désormais PHP ; si le contrôleur frontal n'a toujours rien à servir (/index.php lui-même absent), la requête renvoie un 404 définitif.
Actif lorsque ENTRY_FILE=index.html (ou toute valeur non-.php) et que WORKER_MODE_ENABLED=false. Configuration nginx équivalente :
location ~ \.php$ {
try_files $uri =404; # PHP: file must exist, no fallback
}
location ~ \. {
try_files $uri =404; # other extensions: hard 404 if missing
}
location / {
try_files /index.html =404; # no-extension paths: straight to index.html
}Règles de résolution :
| Type d'URI | Comportement |
|---|---|
.php |
Exécute le fichier s'il existe, sinon 404 définitif |
.css, .png, … (toute autre extension) |
Sert le fichier s'il existe, sinon 404 définitif |
sans extension (/dashboard, /api/users, /) |
Sert /index.html directement — aucun accès disque pour $uri |
Exemples :
| Requête | Résultat |
|---|---|
/style.css (existe) |
Sert style.css |
/style.css (manquant) |
404 |
/dashboard |
Sert /index.html |
/users/42/edit |
Sert /index.html |
/api.php (existe) |
Exécute api.php |
/api.php (manquant) |
404 |
/index.html (direct) |
Sert index.html |
Deux sémantiques méritent d'être soulignées :
- Les chemins sans extension ignorent le disque — le mode SPA ne demande jamais «
/dashboardexiste-t-il sur le disque ? ». Il renvoie toujours l'index. C'est le comportement correct pour les routeurs côté client et cela évite des appelsstat()inutiles. - Les fichiers statiques manquants donnent un 404 définitif, pas un repli — un
/style.cssmanquant ne sert pas silencieusementindex.html. Cela détecte tôt les références de fichiers cassées au lieu de renvoyer du HTML là où le JS attendait du CSS.
Comme le mode SPA exécute directement les fichiers .php existants, PHP_DENY_PATHS s'applique — utilisez-le pour bloquer l'exécution dans les répertoires accessibles en écriture tels que /uploads.
Mode worker
Le mode worker s'active lorsque WORKER_MODE_ENABLED=true et que ENTRY_FILE pointe vers un script .php. Le routeur sert les fichiers statiques depuis le disque et achemine toutes les autres requêtes vers l'ENTRY_FILE du worker — le worker est l'unique contrôleur frontal.
| Type d'URI | Comportement |
|---|---|
Fichiers statiques (.css, .png, … — toute extension non-.php) |
Servis directement depuis le disque s'ils sont présents ; un fichier manquant se replie sur l'ENTRY_FILE du worker (pas de 404 définitif) |
Tout le reste (URI .php, chemins sans extension, /) |
Acheminé vers l'ENTRY_FILE du worker |
Les fichiers .php arbitraires dans la racine du document ne sont jamais exécutés directement en mode worker — une requête vers /about.php atteint le callback du worker comme n'importe quelle autre route, même si about.php existe sur le disque. Il n'y a ni recherche d'index de répertoire, ni repli sur un index.php racine ; le worker voit lui-même ces requêtes.
Deux exceptions, toutes deux des défenses au niveau du serveur qui s'exécutent avant la répartition selon le mode : les chemins dont un segment commence par un point (/.git/config, /.env, /.well-known seul) sont rejetés par le blocage des chemins cachés, et les URI .php sous /.well-known/ sont refusées par défense en profondeur. Les deux renvoient un 404 et n'atteignent jamais le worker.
La validation au démarrage rejette deux combinaisons :
WORKER_MODE_ENABLED=truesansENTRY_FILE→WORKER_MODE_ENABLED=true requires ENTRY_FILE to be set.WORKER_MODE_ENABLED=trueavec unENTRY_FILEnon-.php→WORKER_MODE_ENABLED=true requires a .php ENTRY_FILE.
Voir Mode worker pour tous les détails de configuration.
Comportement de PATH_INFO
$_SERVER['PATH_INFO'] est renseigné différemment selon le mode :
| Mode | Quand il est défini | Valeur |
|---|---|---|
| Traditionnel | Uniquement lorsque l'URI contient .php/ (découpage PATH_INFO) |
Ce qui suit le segment du script, par ex. /users/42 |
| Framework | Uniquement pour une requête explicite /index.php/extra |
Ce qui suit le fichier d'entrée, par ex. /news |
| SPA | Jamais | (PHP n'est invoqué que pour les fichiers .php exacts ; pas de PATH_INFO) |
PATH_INFO suit la sémantique CGI : il n'est présent que lorsque SCRIPT_NAME (le script exécuté) est un préfixe littéral du chemin de la requête. Une réécriture vers le contrôleur frontal que l'URL ne nomme pas — une route applicative, un index de répertoire, un repli sur fichier statique manquant — ne porte aucun PATH_INFO ; lisez plutôt REQUEST_URI. En mode Traditionnel, l'ancienne variable d'environnement SPLIT_PATH_INFO_ENABLED a été supprimée.
Sécurité des chemins
OxPHP applique plusieurs couches de protection pour empêcher la traversée de répertoires, la divulgation de fichiers cachés et les attaques par évasion de lien symbolique :
- Le décodage des pourcentages s'exécute avant l'assainissement, de sorte que les tentatives de traversée encodées comme
/%2e%2e/etc/passwdsont interceptées - Le filtrage des segments supprime les segments
..,.et vides du chemin résolu - La validation des liens symboliques canonicalise chaque chemin résolu et vérifie qu'il reste à l'intérieur de la racine du document. Les liens symboliques qui pointent en dehors du répertoire servi sont bloqués
- Le blocage des chemins cachés bloque tout segment de chemin commençant par
.(par ex./.git/config,/.env), avec une exception pour/.well-known/*conformément à la RFC 8615 - Blocage PHP well-known — même avec l'exception des chemins cachés, les scripts
.phpsous/.well-known/ne sont jamais exécutés (défense en profondeur) - Liste de refus d'exécution PHP — dans les modes à mappage direct (Traditionnel et SPA),
PHP_DENY_PATHSbloque l'exécution.phpselon des motifs glob configurés (par ex./uploads/**, ou un fichier unique comme/admin/legacy.php) avant toute E/S disque. Voir Liste de refus d'exécution PHP
Si le répertoire de la racine du document n'existe pas au démarrage, le serveur se termine avec une erreur fatale. La protection contre l'évasion de lien symbolique nécessite un chemin de racine du document valide et résolvable.
Dépannage
Toutes les requêtes renvoient un 404 en mode Traditionnel
Vérifiez que index.php ou index.html existe à la racine du document. La chaîne try_files du mode Traditionnel ne se replie que sur ces fichiers — si les deux sont absents et qu'aucun fichier ne correspond à l'URL, vous obtenez un 404.
docker exec <container> ls /var/www/html/publicUn fichier statique manquant renvoie un 404 au lieu de la coquille SPA
C'est intentionnel en mode SPA : un /style.css manquant donne un 404 définitif, et non un repli silencieux vers index.html, ce qui détecte tôt les références de fichiers cassées. Dans les modes Framework et Traditionnel, un fichier statique manquant se replie sur le contrôleur frontal (/index.php), de sorte que le routeur de votre application rend le 404. Utilisez le mode SPA si vous voulez des 404 définitifs sur les fichiers manquants.
/index.php direct ne renvoie plus de 404
En mode Framework, l'accès direct au contrôleur frontal est désormais autorisé (la réécriture vers /index.php est idempotente). Si vous vous appuyiez auparavant sur le 404 pour détecter les accès directs, passez à la vérification de REQUEST_URI depuis l'intérieur du contrôleur.
PATH_INFO est vide en mode Framework
C'est le comportement attendu pour les routes applicatives. Le mode Framework suit la sémantique CGI : PATH_INFO n'est défini que lorsque la requête nomme explicitement le fichier d'entrée avec un segment final (/index.php/news → /news). Pour une route applicative normale comme /users/42, le contrôleur frontal est atteint par une réécriture interne qu'elle ne nomme pas, donc PATH_INFO est absent — lisez le chemin depuis $_SERVER['REQUEST_URI']. (Si ENTRY_FILE ne se termine pas par .php, OxPHP choisit le mode SPA, qui ne renseigne jamais PATH_INFO.)
Un lien symbolique dans la racine du document renvoie un 404
Les liens symboliques qui pointent en dehors de la racine du document sont bloqués par conception. Déplacez le contenu cible à l'intérieur de la racine du document, ou montez-le comme un répertoire au bon emplacement.
Exemple Docker
services:
app:
image: ghcr.io/oxphp/oxphp:0.10.0
ports:
- "8080:80"
volumes:
- ./src:/var/www/html
environment:
- DOCUMENT_ROOT=/var/www/html/public
- ENTRY_FILE=index.phpVoir aussi
- Fichiers statiques — détection MIME, mise en cache et streaming des fichiers servis
- Mode worker — processus PHP persistants et routage en mode worker
- Référence de configuration — liste complète des variables d'environnement