Маршрутизація
OxPHP маршрутизує вхідні HTTP-запити в одному з трьох режимів, який обирає ENTRY_FILE, а WORKER_MODE_ENABLED=true натомість перемикає маршрутизацію в режим воркерів. Кожен із трьох режимів відтворює знайому конфігурацію nginx try_files, тож ви можете точно передбачити, що станеться з будь-якою URL-адресою.
Як це працює
Кожен запит проходить спільний конвеєр, перш ніж вмикається логіка конкретного режиму:
- Фільтр шляхів із крапкою — шляхи з прихованими сегментами (
.git,.env) блокуються, за винятком/.well-known/*(RFC 8615) - Пошук у кеші маршрутів — нещодавно розв’язані URI повертаються з LRU-кешу (10 000 записів)
- Percent-декодування та очищення — закодовані послідовності на кшталт
%2e%2eдекодуються, а сегменти обходу (..,., порожні) видаляються - Правила заборони шляхів — якщо файл
.oxphpdenyмістить принаймні одне правило заборони або правило>, очищений шлях зіставляється ще до будь-якого звернення до диска: на правило заборони відповідаєPHP_DENY_FALLBACK, правило>передає запит вхідному скрипту, а шлях, що виходить за обмеження файлу на глибину й довжину, отримує 404 - Блокування PHP у well-known — ешелонований захист: скрипти
.phpусередині/.well-known/ніколи не виконуються - Класифікація URI — очищений шлях один раз класифікується як
NoExtension,PhpабоOtherExtension - Диспетчеризація за режимом — кожен режим обробляє три типи URI за власними правилами
- Перевірка символьних посилань — кожен розв’язаний шлях файлової системи після канонікалізації має лишатися всередині кореня документів, а статичний файл чи PHP-скрипт перевіряється ще раз, на файлі, який фактично відкрито, коли його читають або виконують
Саме крок класифікації дає основний виграш в ефективності: перевірка диска для статичних ресурсів (/style.css, /logo.png) виконується один раз у спільному шарі для URI типу OtherExtension, тож усі три режими платять однакову ціну.
Налаштування
| Змінна | Типово | Опис |
|---|---|---|
DOCUMENT_ROOT |
/var/www/html/public |
Кореневий каталог для віддавання файлів і PHP-скриптів |
ENTRY_FILE |
(не задано) | Єдиний канонічний вхідний скрипт. Не задано — Traditional. *.php — Framework. Не .php — SPA. Разом із WORKER_MODE_ENABLED=true — Worker. Приймає абсолютний шлях або шлях відносно DOCUMENT_ROOT (.. дозволено); розв’язаний шлях має існувати |
WORKER_MODE_ENABLED |
false |
Вмикає постійний режим воркерів. Потребує, щоб ENTRY_FILE вказував на скрипт .php |
Застарілі змінні INDEX_FILE і WORKER_FILE досі розбираються (з попередженням WARN під час запуску) і відображаються на нову модель. Див. Налаштування → Застарілі.
Режими маршрутизації
Режими Traditional, Framework і SPA обираються змінною ENTRY_FILE за WORKER_MODE_ENABLED=false, і кожен відповідає еквівалентній конфігурації nginx try_files.
Активний, коли ENTRY_FILE не задано (або порожня) і WORKER_MODE_ENABLED=false. Еквівалентна конфігурація nginx:
location / {
try_files $uri $uri/ /index.php /index.html =404;
}
location ~ \.php$ {
try_files $uri =404; # PATH_INFO splitting enabled
}Порядок розв’язання:
$uri— точний файл на диску → віддати (або виконати, якщо це.php)$uri/— каталог → шукати в ньомуindex.php, потімindex.html- Розділення PATH_INFO — коли URI містить
.php/, префікс скрипта зіставляється з диском, а решта стаєPATH_INFO(наприклад,/api.php/users/42→ скриптapi.php,PATH_INFO=/users/42) /index.php— резервний кореневий фронт-контролер/index.html— резервний кореневий статичний індекс- 404
Приклади:
| Запит | Результат |
|---|---|
/about.php |
Виконати about.php |
/style.css |
Віддати style.css |
/blog/ (є blog/index.php) |
Виконати blog/index.php |
/api.php/users/42 |
Виконати api.php з PATH_INFO=/users/42 |
/missing.txt |
Перехід до /index.php |
/some/route |
Перехід до /index.php |
У режимі Traditional розділення PATH_INFO увімкнене завжди. Перемикача через змінну оточення немає — колишній прапорець SPLIT_PATH_INFO_ENABLED вилучено.
Активний, коли ENTRY_FILE=index.php (або будь-яке значення, що закінчується на .php) і WORKER_MODE_ENABLED=false. Еквівалентна конфігурація nginx:
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 ...;
}Правила розв’язання:
| Тип URI | Поведінка |
|---|---|
.css, .png, .js, … (будь-яке розширення, крім php) |
Віддати файл, якщо він існує, інакше переписати на /index.php |
.php (будь-який шлях) |
Переписати на /index.php |
без розширення (/api/users, /) |
Переписати на /index.php |
PATH_INFO задається лише тоді, коли запит явно називає вхідний файл із додатковим сегментом (/index.php/extra); для маршрутів застосунку вихідний шлях читається з REQUEST_URI.
Приклади:
| Запит | Результат | $_SERVER['PATH_INFO'] |
|---|---|---|
/style.css (існує) |
Віддати style.css |
— |
/style.css (відсутній) |
Виконати index.php |
(відсутній) |
/api/users |
Виконати index.php |
(відсутній) |
/about.php |
Виконати index.php |
(відсутній) |
/index.php/news/local |
Виконати index.php |
/news/local |
/index.php (напряму) |
Виконати index.php |
(відсутній) |
/ |
Виконати index.php |
(відсутній) |
Для маршрутів застосунку вихідний шлях доступний через REQUEST_URI, тож ваш маршрутизатор читає $_SERVER['REQUEST_URI'] для диспетчеризації. Прямий доступ до /index.php більше не блокується — переписування ідемпотентне, тож прямий запит до нього дає той самий результат, що й запит до /.
Відсутній статичний ресурс передається фронт-контролеру замість швидкої відповіді 404 — це та сама поведінка try_files $uri /index.php, яку Laravel і Symfony постачають типово, тож ваш застосунок сам рендерить 404 для відсутніх ресурсів. Ціна цього — кожен запит до неіснуючого ресурсу тепер запускає PHP; якщо фронт-контролеру теж нічого віддати (сам /index.php відсутній), запит повертає жорстку 404.
Активний, коли ENTRY_FILE=index.html (або будь-яке значення не .php) і WORKER_MODE_ENABLED=false. Еквівалентна конфігурація nginx:
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
}Правила розв’язання:
| Тип URI | Поведінка |
|---|---|
.php |
Виконати файл, якщо він існує, інакше жорстка 404 |
.css, .png, … (будь-яке інше розширення) |
Віддати файл, якщо він існує, інакше жорстка 404 |
без розширення (/dashboard, /api/users, /) |
Одразу віддати /index.html — без перевірки $uri на диску |
Приклади:
| Запит | Результат |
|---|---|
/style.css (існує) |
Віддати style.css |
/style.css (відсутній) |
404 |
/dashboard |
Віддати /index.html |
/users/42/edit |
Віддати /index.html |
/api.php (існує) |
Виконати api.php |
/api.php (відсутній) |
404 |
/index.html (напряму) |
Віддати index.html |
Варто наголосити на двох особливостях:
- Шляхи без розширення не звертаються до диска — режим SPA ніколи не питає, «чи існує
/dashboardна диску?». Він завжди повертає індекс. Це правильно для клієнтських маршрутизаторів і дає змогу уникнути зайвих викликівstat(). - Відсутні статичні файли — це жорстка 404, а не перехід далі — відсутній
/style.cssне підмінюється непомітно наindex.html. Так биті посилання на ресурси виявляються рано, а не тоді, коли JS очікував CSS, а отримав HTML.
Оскільки режим SPA виконує наявні файли .php напряму, діє PHP_DENY_PATHS — використовуйте його, щоб заблокувати виконання в каталогах, доступних для запису, як-от /uploads.
Режим воркерів
Режим воркерів вмикається, коли WORKER_MODE_ENABLED=true і ENTRY_FILE вказує на скрипт .php. Маршрутизатор віддає статичні ресурси з диска, а всі інші запити передає воркеру ENTRY_FILE — воркер є єдиним фронт-контролером.
| Тип URI | Поведінка |
|---|---|
Статичні ресурси (.css, .png, … — будь-яке розширення, крім .php) |
Віддаються напряму з диска, якщо існують; відсутній ресурс передається воркеру ENTRY_FILE (а не жорстка 404) |
Усе інше (URI .php, шляхи без розширення, /) |
Передається воркеру ENTRY_FILE |
Довільні файли .php у корені документів у режимі воркерів ніколи не виконуються напряму — запит до /about.php потрапляє до колбеку воркера, як і будь-який інший маршрут, навіть якщо about.php існує на диску. Немає ані пошуку індексу каталогу, ані резервного кореневого index.php; такі запити воркер бачить сам.
Винятки — це захист на рівні сервера, що спрацьовує до диспетчеризації за режимом, і жоден із них не доходить до воркера. Шляхи з сегментами-крапками (/.git/config, /.env, голий /.well-known) відхиляються блокуванням шляхів із крапкою, а URI .php під /.well-known/ відхиляються як ешелонований захист, обидва — з 404. Якщо файл .oxphpdeny містить принаймні одне правило заборони або правило >, шлях, якому відповідає правило заборони, отримує статус PHP_DENY_FALLBACK, а шлях, що виходить за обмеження файлу на глибину й довжину, — 404.
Перевірка під час запуску відхиляє три комбінації:
WORKER_MODE_ENABLED=trueбезENTRY_FILE→WORKER_MODE_ENABLED=true requires ENTRY_FILE to be set.WORKER_MODE_ENABLED=trueзENTRY_FILEне.php→WORKER_MODE_ENABLED=true requires a .php ENTRY_FILE.- Правило заборони
.oxphpdenyразом зі скриптом уPHP_DENY_FALLBACK→PHP_DENY_FALLBACK is a script, but in worker mode every request runs the worker entry. Потік воркера не виконує нічого, крім вхідного скрипта, тож не може виконати резервний скрипт замість забороненого запиту; використовуйте код статусу.
Повні подробиці налаштування — у розділі Режим воркерів.
Поведінка PATH_INFO
$_SERVER['PATH_INFO'] заповнюється по-різному залежно від режиму:
| Режим | Коли задається | Значення |
|---|---|---|
| Traditional | Лише коли URI містить .php/ (розділення PATH_INFO) |
Хвіст після сегмента скрипта, наприклад /users/42 |
| Framework | Лише для явного запиту /index.php/extra |
Хвіст після вхідного файлу, наприклад /news |
| SPA | Ніколи | (PHP викликається лише для точних файлів .php; без PATH_INFO) |
PATH_INFO дотримується семантики CGI: він присутній лише тоді, коли SCRIPT_NAME (виконуваний скрипт) є буквальним префіксом шляху запиту. Переписування на фронт-контролер, який URL-адреса не називає, — маршрут застосунку, індекс каталогу, резервний перехід через відсутній статичний файл — не несе PATH_INFO; читайте натомість REQUEST_URI. У режимі Traditional колишню змінну оточення SPLIT_PATH_INFO_ENABLED вилучено.
Безпека шляхів
OxPHP застосовує кілька рівнів захисту від обходу каталогів, розкриття прихованих файлів і втечі через символьні посилання:
- Percent-декодування виконується до очищення, тож закодовані спроби обходу на кшталт
/%2e%2e/etc/passwdперехоплюються - Фільтрація сегментів видаляє з розв’язаного шляху
..,.і порожні сегменти - Перевірка символьних посилань канонікалізує кожен розв’язаний шлях і перевіряє, що він лишається в межах кореня документів. Символьні посилання, що ведуть за межі каталогу, який віддається, блокуються, зокрема й ті, ціль яких змінили після того, як шлях уперше було віддано: статичні файли й PHP-скрипти перевіряються ще раз на файлі, який фактично відкрито (див. Дозволені шляхи для символьних посилань)
- Блокування шляхів із крапкою блокує будь-який сегмент шляху, що починається з
.(наприклад,/.git/config,/.env), за винятком/.well-known/*згідно з RFC 8615 - Блокування PHP у well-known — навіть попри виняток для шляхів із крапкою, скрипти
.phpпід/.well-known/ніколи не виконуються (ешелонований захист) - Список заборони виконання PHP — у режимах прямого відображення (Traditional і SPA)
PHP_DENY_PATHSблокує виконання.phpза налаштованими glob-шаблонами (наприклад,/uploads/**або окремий файл на кшталт/admin/legacy.php) ще до будь-якого дискового вводу-виводу. Див. Список заборони виконання PHP - Правила заборони шляхів — файл
.oxphpdenyна верхньому рівніDOCUMENT_ROOTперелічує в синтаксисі gitignore шляхи, які ніколи не віддаються (як статичні файли, так і скрипти) або, з префіксом>, передаються вхідному скрипту. Він діє в кожному режимі маршрутизації одразу після того, як шлях декодовано й нормалізовано, і ще до будь-якого звернення до диска
Якщо каталог кореня документів не існує під час запуску, сервер завершує роботу з фатальною помилкою. Захист від втечі через символьні посилання потребує дійсного шляху до кореня документів, який можна розв’язати.
Усунення несправностей
Усі запити повертають 404 у режимі Traditional
Перевірте, що в корені документів є index.php або index.html. Ланцюжок try_files режиму Traditional переходить лише до них — якщо обидва відсутні й жоден файл не відповідає URL-адресі, ви отримаєте 404.
docker exec <container> ls /var/www/html/publicВідсутній статичний ресурс повертає 404 замість оболонки SPA
У режимі SPA це навмисно: відсутній /style.css — жорстка 404, а не непомітний перехід до index.html, що дає змогу рано виявляти биті посилання на ресурси. У режимах Framework і Traditional відсутній статичний файл передається фронт-контролеру (/index.php), тож 404 рендерить маршрутизатор вашого застосунку. Використовуйте режим SPA, якщо хочете жорсткі 404 для відсутніх ресурсів.
Прямий запит до /index.php більше не повертає 404
У режимі Framework прямий доступ до фронт-контролера тепер дозволено (переписування на /index.php ідемпотентне). Якщо раніше ви покладалися на 404, щоб виявляти прямі звернення, перевіряйте натомість REQUEST_URI усередині контролера.
PATH_INFO порожній у режимі Framework
Для маршрутів застосунку це очікувано. Режим Framework дотримується семантики CGI: PATH_INFO задається лише тоді, коли запит явно називає вхідний файл із додатковим сегментом (/index.php/news → /news). Для звичайного маршруту застосунку на кшталт /users/42 фронт-контролер досягається внутрішнім переписуванням, якого URL-адреса не називає, тож PATH_INFO відсутній — читайте шлях із $_SERVER['REQUEST_URI']. (Якщо ENTRY_FILE не закінчується на .php, OxPHP обирає режим SPA, який ніколи не заповнює PATH_INFO.)
Символьне посилання всередині кореня документів повертає 404
Символьні посилання, що ведуть за межі кореня документів, блокуються за задумом. Перенесіть цільовий вміст у корінь документів або змонтуйте його як каталог за потрібним шляхом.
Приклад для Docker
services:
app:
image: ghcr.io/oxphp/oxphp:0.13.0
ports:
- "8080:80"
volumes:
- ./src:/var/www/html
environment:
- DOCUMENT_ROOT=/var/www/html/public
- ENTRY_FILE=index.phpПромпти для вашого агента
Ці промпти розраховані на агента з підключеним MCP-сервером документації OxPHP — потрібні сторінки він прочитає сам.
Обери правильний режим маршрутизації OxPHP для цього проєкту. Прочитай сторінку `features/routing` через MCP-сервер документації OxPHP.
Визнач за фреймворком і коренем документів у цьому репозиторії, якому режиму він відповідає, і дай мені значення `DOCUMENT_ROOT` та `ENTRY_FILE` разом із блоком nginx `try_files`, який вони замінюють.URL-адреси, які працювали під nginx, на OxPHP повертають 404. Прочитай сторінку `features/routing` через MCP-сервер документації OxPHP.
Порівняй режим, який обирає моя конфігурація, зі шляхами, які цей репозиторій насправді обслуговує, — переписування на фронт-контролер, індекси каталогів, усе, що читає `PATH_INFO`, — і скажи, які з них цей режим не покриває.Див. також
- Статичні файли — визначення MIME-типів, кешування та потокова передача файлів, що віддаються
- Режим воркерів — постійні PHP-воркери та маршрутизація в режимі воркерів
- Довідник із налаштувань — повний перелік змінних оточення