Był wpis o ETag (protokół HTTP), teraz czas na alternatywne rozwiązanie buforowania w przeglądarce klienta. Mowa tutaj o nagłówku Expires, który pozwala na jeszcze efektywniejsze usprawnienie buforowania niż wcześniej omawiany nagłówek ETag.
Co robi nagłówek Expires w praktyce? Jak zautomatyzować dołączanie tego nagłówka do wszystkich obiektów pobieranych z serwera Apache? O tym w niniejszej notce.
Nagłówek Expires jest wysyłany przez serwer w nagłówkach HTTP do klienta (HTTP Responses). Wygląda on mniej więcej następująco:
1 | Expires: Thu, 01 Dec 1994 16:00:00 GMT |
Wartością tego nagłówku jest data ważności dokumentu przesyłanego do klienta (przeglądarki). Przeglądarka odczytuje tę datę – jeżeli data jest późniejsza niż obecna oraz przeglądarka posiada dany plik w buforze – odczytuje ten plik bezpośrednio z buforu (o ile taki posiada) i kończy komunikację z serwerem.
Dzięki temu zaoszczędzasz na transferze serwera, a klient otrzymuje obiekt bezpośrednio z buforu, co znacząco skróca czas wczytywania strony.
Expires vs ETag
Nagłówek ten jest o tyle lepszy od nagłówka ETag, iż nie wymaga sprawdzania aktualności plików w buforze i na serwerze, co zabierało sporo czasu.
Z drugiej strony jest to też wada, gdyż nie ma pewności, iż obiekt który otrzyma klient jest nadal aktualny.
Sam musisz ocenić dla których plików dać jaki nagłówek. Jedno jest pewne – jeżeli nie zamierzasz w najbliższych miesiącach zmienić danego obiektu (pliku graficznego, animacji) i dopuszczasz przechowywanie przez klienta tego pliku w buforze – nagłówek Expires będzie lepszym rozwiązaniem.
Musisz także wiedzieć, że wysyłając do klienta w odpowiedzi HTTP zarówno nagłówek ETag, jak i Expires – nagłówek ETag będzie miał pierwszeństwo i tym faktem będzie się kierowała przeglądarka.
Expires i Apache
Serwer Apache udostępnia specjalny moduł, który w sposób ekspresowy pozwala zająć się nagłówkiem Expires. Mowa oczywiście o mod_expires.
Stosowane przeze mnie ustawienia dla tego modułu wyglądają następująco (.htaccess):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <IfModule mod_expires.c> Header set cache-control: public ExpiresActive on ExpiresDefault "access plus 1 month" ExpiresByType text/html "access plus 1 hour" ExpiresByType application/rss+xml "access plus 1 hour" ExpiresByType image/png "access plus 1 month" ExpiresByType image/jpg "access plus 1 month" ExpiresByType image/jpeg "access plus 1 month" ExpiresByType video/ogg "access plus 1 month" ExpiresByType audio/ogg "access plus 1 month" ExpiresByType video/mp4 "access plus 1 month" ExpiresByType image/x-icon "access plus 1 year" ExpiresByType font/ttf "access plus 1 year" ExpiresByType image/svg+xml "access plus 1 year" </IfModule> |
Co tutaj się wyprawia? Najpierw użyta została wcześniej wspomniana dyrektywa IfModule. Sprawdza on, czy na danym serwerze został wbudowany moduł mod_ expires. Jeśli modułu brak, to kod zostaje pominięty.
Następnie wykorzystujemy dyrektywę Header do ustawienia nagłówka HTTP, jakim jest Cache-Control. Ma to na celu zmuszenie przeglądarek do korzystania z buforowania (jeśli mają taką funkcjonalność zaimplementowaną).
Gdyby nagłówek Cache-Control został ustawiony na wartość no-cache to inne nagłówki odpowiedzialne za buforowanie zostałyby zignorowane.
Następnie uruchamiamy moduł przez wpisanie dyrektywy:
1 | ExpiresActive on |
Kolejnym krokiem jest ustawienie domyślnej „daty ważności” dla wszystkich plików wczytywanych z serwera poprzez dodanie dyrektywy:
1 | ExpiresDefault "access plus 1 month" |
Powoduje to domyślne ustawienie dla wszystkich plików z serwera nagłówka Expires na bieżącą datę pobrania pliku z serwera + 1 miesiąc.
Kolejne dyrektywy ustawiają datę ważności dla wybranych typów plików (typ MIME). Powoduje to nadpisane domyślnych ustawień dla wybranych rodzajów plików, a robi się to w następujący sposób:
1 2 | ExpiresByType text/html "access plus 1 hour" ExpiresByType application/rss+xml "access plus 1 hour" |
Dokumenty HTML i XML (np. sitemap.xml, kanały RSS i Atom) są natomiast przechowywane przez 1 godzinę:
1 2 3 4 5 6 | ExpiresByType image/png "access plus 1 month" ExpiresByType image/jpg "access plus 1 month" ExpiresByType image/jpeg "access plus 1 month" ExpiresByType video/ogg "access plus 1 month" ExpiresByType audio/ogg "access plus 1 month" ExpiresByType video/mp4 "access plus 1 month" |
Pliki multimedialne oraz graficzne przechowywane są w buforze przez jeden miesiąc od czasu ostatniego pobrania:
1 2 3 | ExpiresByType image/x-icon "access plus 1 year" ExpiresByType font/ttf "access plus 1 year" ExpiresByType image/svg+xml "access plus 1 year" |
Najdłużej w buforze przeglądarki są przechowywane pliki najrzadziej aktualizowane, czyli ikony (favicon.ico), pliki czcionek oraz grafika w formacie SVG.
Kilka słów podsumowania
I to by było na tyle. Niewiele trudu trzeba włożyć w to rozwiązanie, a może dać całkiem korzystne efekty dla każdej ze stron (klient & serwer).
Z drugiej strony, powyższe rozwiązanie jest niezbyt uniwersalne. Co jeśli serwerem nie będzie Apache? Jak więc widzisz, nie jest to idealne rozwiązanie. Czasem lepiej poświęcić trochę więcej czasu, napisać prostą klasę w PHP i mieć ustawianie nagłówków Expires (buforowania i innych) na poziomie języka, nie serwera.
Decyzja jednak należy do każdego indywidualnie. Będę jednak wdzięczny za podzielenie się sposobem wykorzystywanym przez innych programistów :-).
To co napisałeś o „no-cache” trochę gryzie się z tym, co mamy tutaj:
http://pl.wikipedia.org/wiki/Lista_nag%C5%82%C3%B3wk%C3%B3w_HTTP#Cache-Control
Z tego co napisałeś wnioskuję, że no-cache = no-store, a wiki mówi, że właśnie dla no-cache jest sprawdzana aktualność strony…
maly, przepraszam za tempo odpowiedzi, nie wiem jakim cudem przeoczyłem Twój komentarz.
Zarówno no-cache, jak i no-store nie mogą pojawić się wraz z Expires z prostego powodu – jeden nie pozwala trzymać plików w buforze, a drugi każdorazowo każe sprawdzać aktualność pliku (czyli robi to samo co ETag i ma te same wady wydajnościowe). Dlatego też ważne jest, by nagłówek Cache-control ustawić na „public” :)