Niestandardowe strony błędów

OxPHP serwuje markowe strony błędów HTML dla odpowiedzi 4xx i 5xx. Każda strona jest odczytywana z dysku raz przy starcie i serwowana z pamięci, więc podczas obsługi żądania nie dochodzi do żadnych operacji I/O na dysku.

Jak to działa

  1. Wczytywanie przy starcie. Przy starcie OxPHP odczytuje katalog wskazany przez ERROR_PAGES_DIR i wczytuje do pamięci każdy prawidłowy plik {status}.html.
  2. Zasady nazewnictwa. Nazwa pliku musi zawierać numeryczny kod statusu HTTP z zakresu 400–599 (na przykład 404.html, 503.html). Pliki o nazwach nienumerycznych, z kodami statusu spoza tego zakresu (w tym 200.html) lub z rozszerzeniem innym niż .html są po cichu ignorowane.
  3. Podmiana treści. Gdy OxPHP generuje odpowiedź 4xx lub 5xx, sprawdza, czy istnieje pasująca, wcześniej wczytana strona błędu. Jeśli istnieje, OxPHP podmienia jedynie treść oraz opisujące ją nagłówki: Content-Type jest ustawiany na text/html; charset=utf-8, Content-Length na rozmiar strony, a nagłówki powiązane z oryginalną treścią (Content-Encoding, ETag, Last-Modified) są usuwane, aby nie mogły błędnie opisać ani ponownie zweryfikować podmienionej zawartości (na przykład treść błędu PHP skompresowana przez ob_gzhandler nie sprawi, że strona HTML pozostanie oznaczona nagłówkiem Content-Encoding: gzip). Nagłówki opisujące semantykę odpowiedzi, a nie samą treść, są zachowywane w niestandardowej stronie — Content-Range przy 416 Range Not Satisfiable, Retry-After przy 529 Site is overloaded oraz Allow przy 405 Method Not Allowed.
  4. Zachowanie awaryjne. Jeśli katalog nie istnieje lub nie może zostać odczytany przy starcie, OxPHP zapisuje ostrzeżenie w logu i kontynuuje działanie bez niestandardowych stron błędów. Odpowiedzi błędów korzystają wtedy z treści w postaci zwykłego tekstu, dopóki katalog nie zostanie naprawiony, a serwer ponownie uruchomiony.

Konfiguracja

Zmienna Wartość domyślna Opis
ERROR_PAGES_DIR (nieustawione) Katalog zawierający pliki HTML z niestandardowymi stronami błędów. Pliki muszą mieć nazwę {status}.html dla kodów statusu 400–599. Gdy nieustawione, odpowiedzi błędów korzystają z treści w postaci zwykłego tekstu

Przykładowe strony

Każda strona błędu to samodzielny plik HTML o nazwie {status}.html. Utrzymuj je ze stylami umieszczonymi bezpośrednio w kodzie (inline), bez zewnętrznych zasobów — w przeciwnym razie nieudane żądanie pomocnicze zepsułoby samą stronę błędu.

Szablon wielokrotnego użytku

Skopiuj to do każdego pliku {status}.html i zmień <title>, <h1> oraz <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>

Kody statusu warte dostarczenia

OxPHP podmienia treść każdej odpowiedzi 4xx lub 5xx, która dociera do potoku odpowiedzi. Oto kody, które zwraca samodzielnie, więc dostarcz plik dla każdego z nich:

Plik Status Kiedy OxPHP go zwraca
400.html Bad Request Żądanie QUERY (RFC 10008) wysłane bez nagłówka Content-Type
404.html Not Found Brak pasującego pliku lub trasy; zablokowany plik z kropką (.env, .git/); bezpośrednie żądanie .php w trybie Framework; domyślne zachowanie awaryjne PHP_DENY_PATHS
413.html Payload Too Large Treść żądania przekracza maksymalny rozmiar
416.html Range Not Satisfiable Niespełnialny nagłówek Range przy pliku statycznym (Content-Range jest zachowywany)
500.html Internal Server Error Nieprzechwycony lub krytyczny błąd PHP
503.html Service Unavailable Łagodne opróżnianie podczas zamykania
504.html Gateway Timeout Żądanie przekroczyło REQUEST_TIMEOUT_SECONDS
529.html Site is overloaded Kolejka żądań jest pełna przy QUEUE_CAPACITY (Retry-After jest zachowywany)

Każdy inny kod 4xx lub 5xx działa tak samo — dodaj 403.html, 451.html i tak dalej dla kodów zwracanych przez Twoją aplikację PHP lub dla niestandardowego statusu PHP_DENY_FALLBACK. Jedynym wyjątkiem jest 429 z mechanizmu ograniczania liczby żądań, które jest generowane przed uruchomieniem tego handlera i zawsze korzysta ze swojej domyślnej treści (zobacz uwagę poniżej).

Gotowe przykłady

Minimalna strona 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>

Strona konserwacji 503 z automatycznym odświeżaniem:

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>

Rozwiązywanie problemów

Niestandardowe strony błędów się nie pojawiają

Sprawdź, czy ERROR_PAGES_DIR jest ustawione i czy pliki mają poprawne nazwy.

Sprawdzenie: Potwierdź aktywną ścieżkę katalogu oraz to, że OxPHP zapisał przy starcie wiersze „Loaded custom error page”:

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

Rozwiązanie: Upewnij się, że ścieżka katalogu jest poprawna, pliki mają nazwy {status}.html, a kontener ma prawo odczytu tego katalogu.

Ostrzeżenie przy starcie o braku katalogu stron błędów

OxPHP zapisuje ostrzeżenie w logu i kontynuuje działanie bez niestandardowych stron błędów, jeśli katalog ERROR_PAGES_DIR nie istnieje lub nie może zostać odczytany. Odpowiedzi błędów korzystają wtedy z treści w postaci zwykłego tekstu. Sprawdź, czy wolumin jest poprawnie zamontowany w Dockerze:

bash
docker run --rm -v ./errors:/var/www/errors:ro \ -e ERROR_PAGES_DIR=/var/www/errors \ ghcr.io/oxphp/oxphp:0.10.0
Odpowiedź 429 nadal pokazuje domyślną treść

Niektóre odpowiedzi generowane, zanim uruchomi się potok odpowiedzi — takie jak odrzucenia z powodu ograniczania liczby żądań — nie są przetwarzane przez handler stron błędów. Odpowiedź 429 Too Many Requests z mechanizmu ograniczania liczby żądań korzysta ze swojej domyślnej treści niezależnie od tego, czy plik 429.html jest obecny.

Przykład dla Dockera

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"

Struktura katalogów:

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

Dobre praktyki

  • Utrzymuj strony błędów jako samodzielne, z CSS umieszczonym bezpośrednio w kodzie (inline). Nie odwołuj się do zewnętrznych arkuszy stylów ani skryptów — te żądania pomocnicze same mogą się nie powieść.
  • Dodaj znacznik <meta http-equiv="refresh" content="30"> do 503.html, aby użytkownicy automatycznie ponawiali próbę po zakończeniu konserwacji.
  • Utrzymuj strony błędów małe. Każda wczytana strona jest przechowywana w pamięci przez cały czas życia procesu serwera.
Note

Niestandardowe strony błędów dotyczą odpowiedzi przepływających przez normalny potok żądań. Odpowiedź 429 Too Many Requests z mechanizmu ograniczania liczby żądań jest generowana przed uruchomieniem handlera stron błędów i korzysta ze swojej domyślnej treści w postaci zwykłego tekstu.

Zobacz też