Wędrując po internecie niejednokrotnie natrafimy na błędne adresy URL. Czasami przyczyną błędu jest przeniesienie strony WWW do innego folderu, kiedy indziej — literówka w adresie URL. Jeśli adres URL wskazuje nieistniejący plik, wówczas internauta ujrzy komunikat o błędzie. Artykuł opisuje, w jaki sposób przygotować własne strony błędów 404 wykorzystując serwer Apache oraz skrypty PHP.
12/01/07
Jeśli w przeglądarce internetowej wpiszesz nieistniejący adres URL to ujrzysz jeden z trzech możliwych komunikatów.
Pierwszym rodzajem błędu jest podanie niepoprawnego adresu serwera. Domena lorem.ipsum.pl, nie jest opisana w DNS, a zatem wpisanie w przeglądarce adresu:
http://lorem.ipsum.pl/dolor/sitamet.html
zakończy się błędem. W takiej sytuacji przeglądarka wyświetli komunikat Nie odnaleziono serwera widoczny na rysunku 1.
Rysunek 1. Komunikat wyświetlany przez Firefoksa po podaniu błędnego adresu serwera
Drugim rodzajem błędu jest podanie adresu URL zawierającego poprawną nazwę serwera oraz błędną nazwę pliku, np.:
http://example.net/lorem/ipsum/dolor.html
Domena example.net jest zarejestrowana i posiada stronę WWW, co możemy stwierdzić odwiedzając witrynę http://example.net. Jednak na serwerze nie istnieje plik o nazwie:
/lorem/ipsum/dolor.html
W tej sytuacji serwer example.net wysyła odpowiedź:
HTTP/1.x 404 Not Found
na co przeglądarka reaguje wyświetlając stronę Not found widoczną na rysunku 2.
Rysunek 2. Komunikat wyświetlany przez Firefoksa po połączeniu z serwerem, na którym nie zainstalowano obsługi błędu 404
Trzecim rodzajem stron wyświetlanych w odpowiedzi na nieistniejące adresy URL są dedykowane strony obsługi błędu 404. Strona taka zazwyczaj zachowuje stylistykę serwera, a przy tym zawiera komunikat informujący o zaistniałym błędzie. Rysunek 3 przedstawia stronę obsługi błędu 404 na serwerze google.pl po podaniu adresu:
http://www.google.pl/lorem/ipsum/dolor.html
Rysunek 3. Komunikat wyświetlany przez Firefoksa po połączeniu z serwerem, na którym zainstalowano obsługę błędu 404
Mamy zatem trzy rodzaje stron wyświetlanych po podaniu nieistniejącego adresu URL:
Oczywiście w przypadku pierwszego rodzaju błędu za wygląd wyświetlanej strony odpowiada wyłącznie przeglądarka. Z punktu widzenia webmastera jest to przypadek nieciekawy.
W artykule zajmiemy się tym, w jaki sposób na serwerze zainstalować obsługę błędów 404 tak, by zamiast komunikatu z rysunku 2 internauta ujrzał ładną stronę WWW z rysunku 3. Podane przykłady będą dotyczyły wyłącznie serwera Apache i interpretatora PHP.
Obsługę błędnych adresów URL przez serwer Apache ustalamy dyrektywą ErrorDocument. Dyrektywa ta może zostać umieszczona w ogólnym pliku konfiguracyjnym serwera Apache httpd.conf lub w plikach .htaccess. Ponieważ firmy hostingowe zazwyczaj nie umożliwiają modyfikowania pliku httpd.conf, zatem w przykładach wykorzystam pliki .htaccess.
Utwórz trzy pliki: .htaccess, error-404.html, error-404.css i umieść je w folderze głównym htdocs/ serwera Apache.
W pliku .htaccess wprowadź jedną linijkę:
ErrorDocument 404 /error-404.html
W pliku error-404.html wprowadź dowolny komunikat, zaś w stylach error-404.css dowolnie ustal wygląd witryny. Zwróć uwagę na to, by użyte adresy URL były bezwzględne. Adresy występują w dwóch miejscach:
Jeśli teraz podasz w przeglądarce adres:
http://localhost/lorem/ipsum/dolor.html
ujrzysz wykonaną przed chwilą stronę błędu.
Jeśli użyjesz względnych adresów URL, obsługa błędu 404 nie będzie wykonana poprawnie. Dyrektywa ErrorDocument zawierająca adres względny:
ErrorDocument 404 error-404.html
nie działa, zaś użycie względnego adresu stylów:
<link rel="stylesheet" type="text/css" href="error-404.css" />
spowoduje, że wyłącznie adresy URL dotyczące foldera głównego, czyli np.
http://localhost/a.html
będą sformatowane zgodnie z podanymi stylami. W przypadku strony pochodzącej z podfolderu, np.
http://localhost/a/b/c/d.html
style nie zostaną odnalezione.
Oczywiście strona obsługi błędu 404 może zostać wykonana w języku PHP. Utwórz plik error-404.php z dowolnym komunikatem po czym zmień dyrektywę ErrorDocument:
ErrorDocument 404 /error-404.php
Liczba 404 pojawiająca się w dyrektywie ErrorDocument jest jednym z tzw. kodów błędów protokołu HTTP. Przeglądarka WWW wysyła do serwera żądania (zdefiniowane protokołem HTTP). W odpowiedzi, serwer wysyła do przeglądarki odpowiedź HTTP. Każda odpowiedź serwera musi rozpoczynać się od kodu. Jeśli serwer nie może zrealizować żądań klienta, wówczas przeglądarka może — na podstawie kodu dołączonego do odpowiedzi — częściowo poznać przyczyny niepowodzenia. Kody błędów protokołu HTTP mają wartości od 400 do 417. Pełną listę błędów znajdziemy w specyfikacji RFC 2616 pod adresem http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.
Przykładowymi błędami są:
Do analizy zapytań i odpowiedzi HTTP wymienianych przez przeglądarkę i serwer możemy użyć wtyczki LiveHTTPHeaders przeglądarki Firefox dostępnej pod adresem http://livehttpheaders.mozdev.org lub dodatku ieHTTPHeaders Internet Explorera dostępnego pod adresem http://www.blunck.se/iehttpheaders/iehttpheaders.html.
Po zainstalowaniu wtyczki LiveHTTPHeaders wybierz opcję menu głównego Narzędzia → Live HTTP Headers. Ujrzysz zakładkę wyświetlającą zapytania i odpowiedzi HTTP wysyłane i odbierane przez przeglądarkę.
Po podaniu adresu:
http://google.pl
serwer google.pl wysyła do przeglądarki odpowiedź
HTTP/1.x 200 OK
widoczną na rysunku 4.
Rysunek 4. Nagłówki protokołu HTTP wyświetlana przez wtyczkę Live HTTP Headers
Odpowiedź wysyłana przez serwer google.pl po podaniu adresu (strona z rysunku 3):
http://www.google.pl/lorem/ipsum/dolor.html
zawiera kod 404 widoczny na rysunku 5.
Rysunek 5. Nagłówki odpowiedzi 404
W ten sposób możesz sprawdzić kod odpowiedzi HTTP wysyłany przez serwer WWW.
Poznana wcześniej dyrektywa ErrorDocument zawiera dwa parametry: kod błędu oraz adres URL:
ErrorDocument kod-błędu url
Po wystąpieniu podanego błędu serwer Apache wyśle w odpowiedzi do przeglądarki stronę o podanym adresie URL. Na przykład dyrektywa:
ErrorDocument 403 /brak-dostepu.html
będzie powodowała wysyłanie dokumentu brak-dostepu.html w odpowiedzi na błędy 403.
Przygotuj cztery pliki:
i umieść je w folderze głównym serwera Apache zachowując strukturę widoczną na rysunku 6.
Rysunek 6. Struktura folderów przykładu obsługi błędu 403
W pliku a/.htaccess umieść jedną linijkę:
Options -Indexes
Podana dyrektywa powoduje, że odwiedzenie przeglądarką adresu:
http://localhost/a/
zakończy się komunikatem 403.
W pliku .htaccess wprowadź natomiast dyrektywę:
ErrorDocument 403 /brak-dostepu.html
W ten sposób skonfigurujesz obsługę błędów 403. Jeśli teraz odwiedzisz stronę:
http://localhost/a/
to ujrzysz treść dokumentu brak-dostepu.html.
Oczywiście jeden plik .htaccess może zawierać wiele dyrektyw ErrorDocument:
ErrorDocument 403 /brak-dostepu.html ErrorDocument 404 /error-404.html ...
Ponadto obsługa wielu różnych błędów może być wykonywana przez jeden dokument:
ErrorDocument 403 /blad.html ErrorDocument 404 /blad.html ...
W języku PHP kod odpowiedzi wysyłanej przez serwer Apache możemy zmienić wywołując funkcję header():
header('HTTP/1.x 404 Not Found');
Należy pamiętać, że przed wywołaniem funkcji header() nie może pojawić się żadna instrukcja wysyłająca dane (np. echo lub print). Skrypt:
<?php
echo 'Błąd! Podana strona nie istnieje!';
header('HTTP/1.x 404 Not Found');
?>
jest niepoprawny.
Niektóre witryny stosują połowiczną obsługę błędu 404 nazywaną soft 404. Polega to na tym, że strona błędu 404 jest atrakcyjnie wizualna, ale opatrzona nagłówkiem poprawnej odpowiedzi HTTP, czyli
HTTP/1.x 200 OK
Jeśli skrypt składa się z kilku plików PHP dołączanych instrukcją require_once, to należy pamiętać o tym, by żadne skrypty zawierające tajne informacje nie były dostępne w ramach usługi WWW. Jeśli w pliku db.inc zapiszemy hasło dostępu do bazy danych:
$link = mysql_connect('localhost', 'appadm', 'tajnehaslo');
a plik umieścimy w folderze include/, to wystarczy użyć adresu:
http://example.net/include/db.inc
by poznać dane konta appadm.
Jeśli to tylko możliwe, to wszystkie biblioteki, szablony i pliki z danymi należy przenieść poza drzewo katalogów widoczne w ramach usługi WWW (np. poza folder public_html/). Niestety wielu dostawców hostingu nie umożliwia tego: wszystkie pliki nagrywane na serwerze znajdują się w folderze dostępnym poprzez WWW. W takiej sytuacji należy wykorzystać plik .htaccess.
Dyrektywa:
<Files ~ "\.php$">
Order allow,deny
Deny from all
</Files>
wyłącza dostęp do plików, których nazwa kończy się rozszerzeniem .php.
W podobny sposób możemy wyłączyć:
<Files ~ "\.txt$">
Order allow,deny
Deny from all
</Files>
lub włączyć
<Files ~ "\.png$">
Order allow,deny
Allow from all
</Files>
dostęp do dowolnego rodzaju plików.
Jako przykład praktyczny przeanalizujmy dwie witryny WWW. Jedna z nich będzie stosowała zwykłe adresy URL, a druga — adresy przyjazne. Witryny te są tak wykonane, by mogły zostać umieszczone na serwerze udostępnianym przez zewnętrzną firmę. Zakładamy, że:
Wygląd witryny jest przedstawiony na rysunku 7. Jest ona wykonana w PHP przy wykorzystaniu szablonów Smarty i plików tekstowych. Poziome menu witryny powstaje na podstawie pliku tekstowego dane/menu.txt o zawartości:
Lorem|lorem.txt|2 Ipsum|ipsum.txt|3 Dolor|dolor.txt|4 Sit amet|sit-amet.txt|5
Każda linijka pliku opisuje jedną opcję. Kolejne kolumny to:
Cała witryna stosuje następujące adresy URL:
index.php?id=1 - strona błędu 404 index.php?id=2 - strona lorem index.php?id=3 - strona ipsum index.php?id=4 - strona dolor index.php?id=5 - strona sit amet img/*.png - obrazy użyte na stronie style.css - style strony
Dodatkowo adres index.php jest przekierowaniem do strony domyślnej lorem o adresie index.php?id=2.
Rysunek 7. Przykładowa witryna
Witryna składa się z plików i folderów przedstawionych na rysunku 8. W ramach usługi WWW dostępnymi plikami powinny być wyłącznie:
index.php style.css img/*.png
żadne pliki z folderów:
dane/ include/ template/ template_c/
nie powinny być dostępne poprzez WWW.
Rysunek 8. Pliki i foldery omawianego przykładu
W przypadku witryny, która nie stosuje przyjaznych adresów URL w pliku .htaccess umieszczamy następujące dyrektywy:
Do ustalenia strony domyślnej stosujemy dyrektywy Options oraz DirectoryIndex:
Options -Indexes DirectoryIndex index.php
Pierwsza z nich wyłącza możliwość listowania zawartości katalogu, a druga ustala, że adres kończący się nazwą folderu, np.
http://localhost/przyklad/
będzie odwoływał się do pliku:
http://localhost/przyklad/index.php
Następnie ustalamy strony błędów:
ErrorDocument 404 /index.php?id=1 ErrorDocument 403 /index.php?id=1
pamiętając o tym, by w skrypcie index.php użyć funkcji header() do generowania nagłówka Not found:
if ($akcja == 1) {
header('HTTP/1.x 404 Not Found');
} else {
...
}
Na zakończenie blokujemy dostęp do wszystkich plików:
<Files ~ ".*$">
Order allow,deny
Deny from all
</Files>
po czym odblokowujemy dostęp do plików index.php, .css, .png oraz do folderu:
<Files ~ "index\.php$">
Order allow,deny
Allow from all
</Files>
<Files ~ "\.css$">
Order allow,deny
Allow from all
</Files>
<Files ~ "\.png$">
Order allow,deny
Allow from all
</Files>
<Files ~ "^$">
Order allow,deny
Allow from all
</Files>
Pełna zawartość pliku .htaccess jest przedstawiona na listingu 1.
Options -Indexes
DirectoryIndex index.php
ErrorDocument 404 /index.php?id=1
ErrorDocument 403 /index.php?id=1
<Files ~ ".*$">
Order allow,deny
Deny from all
</Files>
<Files ~ "index\.php$">
Order allow,deny
Allow from all
</Files>
<Files ~ "\.css$">
Order allow,deny
Allow from all
</Files>
<Files ~ "\.png$">
Order allow,deny
Allow from all
</Files>
<Files ~ "^$">
Order allow,deny
Allow from all
</Files>
Listing 1. Plik .htaccess rozwiązania bez przyjaznych adresów URL
Przypadek, w którym witryna stosuje przyjazne adresy URL jest nieco bardziej skomplikowany. Tym razem stosujemy adresy .html, które będą przekierowane na skrypty PHP:
error.html -> index.php?id=1 lorem.html -> index.php?id=2 ipsum.html -> index.php?id=3 dolor.html -> index.php?id=4 sit-amet.html -> index.php?id=5
Podobnie jak poprzednio, plik .htaccess rozpoczynamy od ustalenia strony domyślnej:
Options -Indexes DirectoryIndex index.php
Następnie dołączamy dyrektywy odpowiedzialne za translacje adresów:
RewriteEngine on RewriteRule ^error\.html$ index.php?id=1 RewriteRule ^lorem\.html$ index.php?id=2 RewriteRule ^ipsum\.html$ index.php?id=3 RewriteRule ^dolor\.html$ index.php?id=4 RewriteRule ^sit-amet\.html$ index.php?id=5
Po czym ustalamy strony błędów:
ErrorDocument 404 /error.html ErrorDocument 403 /error.html
i blokujemy dostęp do plików:
<Files ~ ".*$">
Order allow,deny
Deny from all
</Files>
<Files ~ "index\.php$">
Order allow,deny
Allow from all
</Files>
<Files ~ "\.html$">
Order allow,deny
Allow from all
</Files>
<Files ~ "\.css$">
Order allow,deny
Allow from all
</Files>
<Files ~ "\.png$">
Order allow,deny
Allow from all
</Files>
<Files ~ "^$">
Order allow,deny
Allow from all
</Files>
Kompletny plik .htaccess jest przedstawiony na listingu 2.
Options -Indexes
DirectoryIndex index.php
RewriteEngine on
RewriteRule ^error\.html$ index.php?id=1
RewriteRule ^lorem\.html$ index.php?id=2
RewriteRule ^ipsum\.html$ index.php?id=3
RewriteRule ^dolor\.html$ index.php?id=4
RewriteRule ^sit-amet\.html$ index.php?id=5
ErrorDocument 404 /error.html
ErrorDocument 403 /error.html
<Files ~ ".*$">
Order allow,deny
Deny from all
</Files>
<Files ~ "index\.php$">
Order allow,deny
Allow from all
</Files>
<Files ~ "\.html$">
Order allow,deny
Allow from all
</Files>
<Files ~ "\.css$">
Order allow,deny
Allow from all
</Files>
<Files ~ "\.png$">
Order allow,deny
Allow from all
</Files>
<Files ~ "^$">
Order allow,deny
Allow from all
</Files>
Listing 2. Plik .htaccess rozwiązania stosującego przyjazne URL-e
W kodzie skryptu PHP możemy dodatkowo zablokować dostęp do witryny adresami index.php:
if (preg_match('/index\.php/', $_SERVER['REQUEST_URI'])) {
header('Location: http://localhost/lorem.html');
header('HTTP/1.x 301 Moved Permanently');
}
Dzięki temu odwołania do stron:
index.php index.php?id=2 index.php?id=3 ...
będą przekierowywane na stronę główną.
Ponieważ przykładowe witryny stosują adresy bezwzględne:
/style.css /lorem.html /img/logo.png ...
przykłady należy uruchamiać w folderze głównym serwera.
Tabela 1. Przykłady do pobrania
Tabela 2. Adresy