Власні сторінки помилок

OxPHP віддає фірмові HTML-сторінки помилок для відповідей 4xx і 5xx. Кожна сторінка зчитується з диска один раз під час запуску й віддається з памʼяті, тож під час обробки запиту жодних дискових операцій введення-виведення не відбувається.

Як це працює

  1. Завантаження під час запуску. Під час запуску OxPHP зчитує каталог, указаний у ERROR_PAGES_DIR, і завантажує в памʼять кожен коректний файл {status}.html.
  2. Правила іменування. Імʼя файлу має бути числовим кодом стану HTTP з діапазону 400–599 (наприклад, 404.html, 503.html). Файли з нечисловими іменами, кодами стану поза цим діапазоном (зокрема 200.html) або з розширенням, відмінним від .html, мовчки ігноруються.
  3. Заміна тіла. Коли OxPHP формує відповідь 4xx або 5xx, він перевіряє, чи є відповідна попередньо завантажена сторінка помилки. Якщо вона є, OxPHP замінює лише тіло та заголовки, що його описують: Content-Type набуває значення text/html; charset=utf-8, Content-Length — розміру сторінки, а заголовки, привʼязані до початкового тіла (Content-Encoding, ETag, Last-Modified), відкидаються, щоб вони не могли хибно позначити замінене тіло чи спричинити його повторну перевірку (наприклад, тіло помилки PHP, стиснене ob_gzhandler, не залишить HTML-сторінку з позначкою Content-Encoding: gzip). Заголовки, які описують семантику відповіді, а не її тіло, зберігаються у власній сторінці: Content-Range для 416 Range Not Satisfiable, Retry-After для 529 Site is overloaded і Allow для 405 Method Not Allowed.
  4. Резервний варіант. Якщо каталогу не існує або його не вдається прочитати під час запуску, OxPHP записує попередження в журнал і працює далі без власних сторінок помилок. Відповіді з помилками повертаються до тіл у вигляді звичайного тексту, доки каталог не буде виправлено, а сервер — перезапущено.

Налаштування

Змінна Типове значення Опис
ERROR_PAGES_DIR (не задано) Каталог з HTML-файлами власних сторінок помилок. Файли мають називатися {status}.html для кодів стану 400–599. Якщо змінну не задано, відповіді з помилками використовують тіла у вигляді звичайного тексту

Приклади сторінок

Кожна сторінка помилки — це самодостатній HTML-файл з іменем {status}.html. Тримайте стилі вбудованими, без зовнішніх ресурсів: інакше невдалий додатковий запит зламає саму сторінку помилки.

Шаблон для повторного використання

Скопіюйте його в кожен {status}.html і змініть <title>, <h1> та <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>

Коди стану, для яких варто мати сторінки

OxPHP замінює тіло кожної відповіді 4xx або 5xx, що доходить до конвеєра відповідей. Ось коди, які він повертає самостійно, тож підготуйте файл для кожного з них:

Файл Стан Коли OxPHP його повертає
400.html Bad Request Запит QUERY (RFC 10008), надісланий без заголовка Content-Type
404.html Not Found Немає відповідного файлу чи маршруту; заблокований dotfile (.env, .git/); прямий запит до .php у режимі Framework; типовий резервний варіант PHP_DENY_PATHS
413.html Payload Too Large Тіло запиту перевищує максимальний розмір
416.html Range Not Satisfiable Нездійсненний заголовок Range для статичного файлу (Content-Range зберігається)
500.html Internal Server Error Неперехоплена або фатальна помилка PHP
503.html Service Unavailable Плавне завершення обробки запитів під час зупинки сервера
504.html Gateway Timeout Вичерпано max_execution_time PHP (або ліміт set_time_limit()). Див. Тайм-аути
529.html Site is overloaded Запит вичерпав свій бюджет очікування (щонайбільше QUEUE_WAIT_TIMEOUT_MS, менше там, де сервер його скоротив), так і не потрапивши до воркера (очікуючи на місце в черзі або в самій черзі), або вже очікували QUEUE_MAX_WAITING запитів, або їхні тіла вже заповнили QUEUE_MAX_WAITING_BYTES (Retry-After зберігається)

Будь-який інший код 4xx чи 5xx працює так само — додайте 403.html, 451.html тощо для кодів, які повертає ваш PHP-застосунок, або для власного стану PHP_DENY_FALLBACK. Єдиний виняток — 429 від обмежувача частоти запитів: він формується ще до запуску цього обробника й завжди використовує типове тіло (див. примітку нижче).

Готові приклади

Мінімальна сторінка 404:

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>

Сторінка технічного обслуговування 503 з автоматичним оновленням:

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>

Усунення несправностей

Власні сторінки помилок не зʼявляються

Переконайтеся, що ERROR_PAGES_DIR задано, а файли названо правильно.

Перевірка: переконайтеся в правильності шляху до каталогу й у тому, що під час запуску OxPHP записав у журнал рядки «Loaded custom error page»:

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

Виправлення: переконайтеся, що шлях до каталогу правильний, файли мають імена {status}.html, а контейнер має доступ на читання до каталогу.

Попередження під час запуску про відсутній каталог сторінок помилок

Якщо каталогу ERROR_PAGES_DIR не існує або його не вдається прочитати, OxPHP записує попередження в журнал і працює далі без власних сторінок помилок. Тоді відповіді з помилками використовують тіла у вигляді звичайного тексту. Перевірте, чи правильно змонтовано том у Docker:

bash
docker run --rm -v ./errors:/var/www/errors:ro \ -e ERROR_PAGES_DIR=/var/www/errors \ ghcr.io/oxphp/oxphp:0.13.0
Відповідь 429 усе одно показує типове тіло

Деякі відповіді, що формуються до запуску конвеєра відповідей, — наприклад, відмови через обмеження частоти запитів — не обробляються обробником сторінок помилок. Відповідь 429 Too Many Requests від обмежувача частоти запитів використовує типове тіло незалежно від того, чи є файл 429.html.

Приклад для Docker

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

Структура каталогів:

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

Рекомендації

  • Робіть сторінки помилок самодостатніми, із вбудованим CSS. Не посилайтеся на зовнішні таблиці стилів чи скрипти — ці додаткові запити самі можуть завершитися помилкою.
  • Додайте до 503.html тег <meta http-equiv="refresh" content="30">, щоб браузери користувачів автоматично повторили спробу після завершення технічного обслуговування.
  • Робіть сторінки помилок невеликими. Кожна завантажена сторінка зберігається в памʼяті протягом усього життя процесу сервера.
Note

Власні сторінки помилок застосовуються до відповідей, що проходять звичайним конвеєром обробки запитів. Відповідь 429 Too Many Requests від обмежувача частоти запитів формується до запуску обробника сторінок помилок і використовує типове тіло у вигляді звичайного тексту.

Промпти для вашого агента

Ці промпти розраховані на агента, до якого підключено MCP-сервер документації OxPHP, — потрібні сторінки він прочитає сам.

prompt
Зроби для мого розгортання фірмові сторінки помилок OxPHP. Прочитай сторінку `features/error-pages` через MCP-сервер документації OxPHP. Візьми шрифти й кольори з власного макета цього репозиторію, напиши самодостатній файл для кожного стану, який OxPHP формує сам, і підключи каталог до мого контейнера.

Див. також