GAJDAW


MOJE KSIĄŻKI

Książka pt. Git. Recipes
Książka pt. Git. Rozproszony system kontroli wersji
Książka pt. PhpStorm Starter
Książka pt. Symfony w przykładach
Książka pt. GIMP. Praktyczne projekty. Wydanie II
Książka pt. PHP, MySQL i MVC. Tworzenie witryn WWW opartych na bazie danych
Książka pt. PHP. Praktyczne projekty

PROGRAMY

Obsługa błędów 404 Not Found w Apache/PHP. Przewodnik

Włodzimierz Gajda

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.

1. Strony wyświetlane po podaniu błędnego adresu URL

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.

2. Dyrektywa ErrorDocument

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

3. Kody błędów protokołu HTTP

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ą:

4. Podglądanie odpowiedzi serwera WWW

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.

5. Dyrektywa ErrorDocument i pozostałe błędy HTTP

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
...

6. Ustalanie odpowiedzi HTTP w PHP

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

7. Usuwanie dostępu do plików

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.

8. Przykład praktyczny

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:

8.1 Przykładowa witryna Lorem ipsum

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

8.2 Rozwiązanie pierwsze: witryna bez przyjaznych URL-i

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

8.3 Rozwiązanie drugie: witryna z przyjaznymi adresami URL-i

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ą.

Uwaga

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.


Reklama

Szkolenia z Zend Framework 2.0