Nagłówek Cache-Control stanowi fundament zarządzania buforowaniem w protokole HTTP. Określa zasady przechowywania zasobów przez przeglądarki i serwery proxy, bezpośrednio wpływając na wydajność aplikacji webowych i bezpieczeństwo danych. Wprowadzony w HTTP/1.1, Cache-Control zastąpił starsze mechanizmy kontroli buforowania, oferując znacznie większą precyzję i elastyczność.
Cache-Control został zdefiniowany po raz pierwszy w RFC 2616 z 1999 roku, a następnie zaktualizowany w RFC 7234 z 2014 roku. Najnowsza specyfikacja, RFC 9111 z 2022 roku, wprowadza dodatkowe udoskonalenia i nowe dyrektywy, takie jak must-understand. Standard ten jest obecnie powszechnie implementowany przez wszystkie współczesne przeglądarki i serwery HTTP.
Ewolucja Cache-Control była odpowiedzią na rosnące potrzeby optymalizacji wydajności w internecie. Wcześniejsze mechanizmy, takie jak nagłówek Expires, oferowały jedynie podstawową kontrolę nad czasem wygaśnięcia zasobów. Cache-Control wprowadził koncepcję dyrektyw, które pozwalają na szczegółowe określenie zachowania buforów w różnych scenariuszach.
Cache-Control funkcjonuje jako zestaw dyrektyw przekazywanych między klientem a serwerem. Dyrektywy te definiują, czy zasób może być buforowany, jak długo pozostaje aktualny oraz w jakich warunkach wymaga ponownego pobrania. Nagłówek może występować zarówno w żądaniach HTTP, jak i odpowiedziach serwera.
Każda dyrektywa składa się z tokenu identyfikującego rodzaj instrukcji. Niektóre dyrektywy przyjmują dodatkowe parametry w formie liczby sekund, na przykład max-age=3600 oznacza, że zasób pozostaje świeży przez godzinę. Dyrektywy są niewrażliwe na wielkość liter i można je łączyć, oddzielając przecinkami.
Ważną cechą Cache-Control jest jego jednokierunkowość. Proxy muszą przekazywać dyrektywy w przesyłanych wiadomościach, niezależnie od tego, czy same implementują mechanizmy buforowania. To zapewnia spójność zasad buforowania w całym łańcuchu żądanie-odpowiedź.
Typy buforów i ich zachowanie
System buforowania HTTP obejmuje różne typy buforów, każdy z własnymi charakterystykami i ograniczeniami. Prywatne bufory, takie jak cache przeglądarki, przechowują zasoby dla pojedynczego użytkownika. Współdzielone bufory, włączając serwery proxy i CDN-y, mogą udostępniać buforowane zasoby wielu użytkownikom.
Bufory prywatne są zazwyczaj bardziej liberalne w swoich zasadach buforowania, ponieważ nie ma ryzyka udostępnienia danych jednego użytkownika innemu. Mogą również ignorować niektóre dyrektywy, takie jak proxy-revalidate, które dotyczą wyłącznie buforów współdzielonych.
Bufory współdzielone muszą być bardziej ostrożne, szczególnie w kontekście bezpieczeństwa i prywatności. Nie mogą buforować odpowiedzi oznaczonych jako private, chyba że zostały odpowiednio skonfigurowane do obsługi konkretnych użytkowników.
Dyrektywy dla odpowiedzi serwera
Kontrola czasu buforowania
max-age ustala czas w sekundach, przez który odpowiedź pozostaje świeża. Po przekroczeniu tego czasu bufor musi zwalidować zasób z serwerem przed ponownym użyciem. Dyrektywa nadpisuje nagłówek Expires i stanowi podstawową metodę kontroli czasu buforowania. Wartość zero oznacza natychmiastowe wygaśnięcie, a wartości ujemne są traktowane jako zero.
s-maxage działa podobnie do max-age, ale dotyczy wyłącznie współdzielonych buforów, takich jak serwery proxy czy CDN. Pozwala na różną politykę buforowania dla buforów prywatnych i publicznych. Automatycznie włącza również zachowanie proxy-revalidate. Ta dyrektywa jest szczególnie przydatna w architekturach z CDN, gdzie czas buforowania w sieci dostarczania treści może różnić się od czasu buforowania w przeglądarce użytkownika.
Kontrola walidacji
no-cache wymusza walidację z serwerem przed każdym użyciem buforowanego zasobu. Bufor może przechować odpowiedź, ale musi sprawdzić jej aktualność przed udostępnieniem klientowi. Opcjonalnie można wskazać konkretne pola nagłówka, które wymagają walidacji, na przykład no-cache="Set-Cookie" pozwala na buforowanie odpowiedzi, ale wymaga walidacji pola Set-Cookie.
must-revalidate wymaga walidacji z serwerem po wygaśnięciu odpowiedzi. Jeśli walidacja nie jest możliwa, bufor musi zwrócić błąd 504 Gateway Timeout zamiast udostępnić przestarzałą odpowiedź. Ta dyrektywa jest kluczowa dla aplikacji, gdzie aktualność danych ma pierwszorzędne znaczenie, takich jak systemy finansowe czy medyczne.
proxy-revalidate funkcjonuje podobnie do must-revalidate, ale dotyczy wyłącznie współdzielonych buforów. Prywatne bufory mogą ignorować tę dyrektywę, co pozwala na różne zachowanie w zależności od typu buforu.
Kontrola dostępu
no-store całkowicie zabrania buforowania żądania lub odpowiedzi. Dyrektywa ta jest kluczowa dla ochrony danych wrażliwych, takich jak informacje osobiste czy dane logowania. Żaden bufor nie może przechować takiej odpowiedzi, ani w pamięci, ani na dysku. Jest to najrestrykcyjniejsza forma kontroli buforowania.
private ogranicza buforowanie do prywatnych buforów użytkownika, wykluczając współdzielone bufory. Stosuje się ją dla personalizowanych treści, które nie powinny być dostępne dla innych użytkowników. Może być używana z kwalifikacją, na przykład private="Set-Cookie" oznacza, że tylko wskazane pole nie może być buforowane przez bufory współdzielone.
public eksplicite zezwala na buforowanie przez dowolny bufor, nawet w sytuacjach, gdy normalnie byłoby to zabronione. Przydatna przy odpowiedziach zawierających nagłówki autoryzacji, które zwykle uniemożliwiają buforowanie przez bufory współdzielone.
Nowoczesne rozszerzenia
immutable wskazuje, że zasób nie zmieni się, dopóki pozostaje świeży. Dyrektywa ta jest szczególnie przydatna dla zasobów z wersjonowanymi URL-ami, gdzie zmiana treści oznacza zmianę adresu. Wprowadzona w RFC 8246, jest wspierana przez większość nowoczesnych przeglądarek i może znacząco poprawić wydajność przez eliminację niepotrzebnych żądań warunkowych.
stale-while-revalidate pozwala na użycie przestarzałej odpowiedzi przez określony czas, podczas gdy bufor waliduje zasób w tle. Mechanizm ten poprawia responsywność aplikacji, udostępniając natychmiast buforowaną treść. Użytkownik otrzymuje szybką odpowiedź, a aktualizacja odbywa się asynchronicznie.
stale-if-error umożliwia użycie przestarzałej odpowiedzi w przypadku błędów serwera, takich jak 500, 502, 503 czy 504. Zwiększa to odporność aplikacji na chwilowe problemy z serwerem, zapewniając ciągłość usługi nawet podczas awarii.
must-understand to najnowsza dyrektywa wprowadzona w RFC 9111. Ogranicza buforowanie do buforów, które rozumieją semantykę kodu statusu odpowiedzi. Jest szczególnie przydatna dla nowych kodów statusu, które mogą być nieprawidłowo interpretowane przez starsze implementacje buforów.
Dyrektywy dla żądań klienta
Preferencje czasowe
max-age w żądaniu oznacza, że klient preferuje odpowiedź nie starszą niż podana liczba sekund. Bufor może udostępnić świeżą odpowiedź lub zwalidować przestarzałą. Wartość zero wymusza walidację, działając podobnie do no-cache.
max-stale pozwala klientowi zaakceptować przestarzałą odpowiedź, opcjonalnie do określonej liczby sekund. Dyrektywa ta jest rzadko wspierana przez przeglądarki internetowe, ale może być przydatna w aplikacjach specjalistycznych, gdzie szybkość odpowiedzi jest ważniejsza niż jej aktualność.
min-fresh wymaga, aby odpowiedź pozostała świeża przez co najmniej podany czas. Klient preferuje odpowiedzi z większym marginesem świeżości, co może być przydatne w scenariuszach offline lub przy niestabilnych połączeniach.
Kontrola źródła
no-cache w żądaniu oznacza preferencję dla niebuforowanej odpowiedzi bez walidacji z serwerem. Często używane przy odświeżaniu strony (Ctrl+F5 w większości przeglądarek). Powoduje pominięcie lokalnego buforu i bezpośrednie żądanie do serwera.
only-if-cached instruuje bufor, aby zwrócił wyłącznie buforowaną odpowiedź. Jeśli odpowiednia odpowiedź nie istnieje w buforze, zwracany jest błąd 504 Gateway Timeout. Ta dyrektywa jest przydatna w aplikacjach offline lub w sytuacjach, gdy połączenie sieciowe jest kosztowne.
Kontrola transformacji
no-transform zapobiega modyfikacji treści przez pośredników w łańcuchu żądanie-odpowiedź. Niektóre proxy mogą kompresować obrazy, minifikować kod JavaScript czy wykonywać inne optymalizacje. Ta dyrektywa zapewnia, że treść dociera do odbiorcy w niezmienionej formie.
Interakcje z innymi nagłówkami
Cache-Control współpracuje z wieloma innymi nagłówkami HTTP, tworząc kompleksowy system kontroli buforowania. Nagłówek Expires, choć starszy, nadal jest używany jako fallback dla starszych systemów. Gdy obecny jest max-age lub s-maxage, Expires jest ignorowany.
Nagłówki walidacji, takie jak ETag i Last-Modified, współpracują z Cache-Control w procesie walidacji warunkowej. Pozwalają one na sprawdzenie, czy buforowany zasób jest nadal aktualny, bez konieczności pobierania całej treści.
Nagłówek Vary informuje bufory, które nagłówki żądania wpływają na odpowiedź. Jest kluczowy dla prawidłowego buforowania zasobów, które mogą się różnić w zależności od nagłówków takich jak Accept-Encoding czy User-Agent.
Praktyczne zastosowania
Zasoby statyczne
Statyczne zasoby, takie jak obrazy, arkusze stylów czy skrypty JavaScript, powinny używać długich czasów buforowania z dyrektywą max-age. Dla zasobów niezmiennych zaleca się dodanie dyrektywy immutable w połączeniu z wersjonowaniem URL-ów. Typowa konfiguracja: Cache-Control: public, max-age=31536000, immutable.
Obrazy mogą używać jeszcze dłuższych czasów buforowania, szczególnie gdy są wersjonowane. Dla obrazów awatarów użytkowników lub innych treści, które mogą się zmieniać, stosuje się krótsze czasy lub wymusza się walidację.
Treści dynamiczne
Dane wrażliwe wymagają zastosowania dyrektywy no-store, która całkowicie eliminuje ryzyko nieautoryzowanego dostępu do buforowanych informacji. Dotyczy to szczególnie stron logowania, danych osobowych czy informacji finansowych.
Dynamiczne treści, które zmieniają się często, powinny używać dyrektywy no-cache lub bardzo krótkich czasów max-age. Zapewnia to aktualność informacji przy zachowaniu możliwości buforowania po walidacji.
API i usługi web
Endpoints API wymagają indywidualnego podejścia w zależności od charakteru danych. Dane referencyjne, takie jak listy krajów czy kodów walut, mogą używać długich czasów buforowania. Dane transakcyjne powinny wymuszać walidację lub całkowicie wyłączać buforowanie.
REST API często wykorzystuje kombinację dyrektyw, na przykład Cache-Control: private, max-age=300 dla danych użytkownika, które są aktualne przez 5 minut. GraphQL endpoints mogą używać bardziej złożonych strategii, uwzględniających zależności między zapytaniami.
Personalizacja i uwierzytelnianie
Personalizowane treści wymagają zastosowania dyrektywy private, która zapobiega udostępnieniu danych jednego użytkownika innym przez współdzielone bufory. Dla treści uwierzytelnionych można również rozważyć must-revalidate, aby zapewnić aktualność uprawnień.
Sesje użytkowników i tokeny uwierzytelniające powinny zawsze używać no-store, aby zapobiec ich przypadkowemu buforowaniu i potencjalnemu przejęciu przez nieautoryzowane osoby.
Kompletny wykaz dyrektyw
| Dyrektywa | Typ | Znaczenie |
|---|---|---|
max-age=<sekundy> |
Żądanie/Odpowiedź | Określa maksymalny wiek odpowiedzi, którą klient zaakceptuje lub jak długo odpowiedź jest świeża. |
max-stale[=<sekundy>] |
Żądanie | Klient akceptuje przestarzałą odpowiedź, opcjonalnie do podanej liczby sekund. |
min-fresh=<sekundy> |
Żądanie | Klient chce odpowiedź, która pozostanie świeża przez co najmniej podany czas. |
no-cache |
Żądanie/Odpowiedź | Wymaga walidacji z serwerem przed użyciem buforowanej odpowiedzi. |
no-store |
Żądanie/Odpowiedź | Zabrania buforowania żądania lub odpowiedzi, np. dla danych wrażliwych. |
no-transform |
Żądanie/Odpowiedź | Zapobiega modyfikacji treści przez pośredników. |
only-if-cached |
Żądanie | Bufor zwraca zapisane dane, jeśli dostępne, w przeciwnym razie błąd 504. |
s-maxage=<sekundy> |
Odpowiedź | Określa czas świeżości dla współdzielonych buforów, nadpisując max-age. |
must-revalidate |
Odpowiedź | Wymaga walidacji z serwerem, jeśli odpowiedź jest przestarzała. |
proxy-revalidate |
Odpowiedź | Podobne do must-revalidate, ale tylko dla współdzielonych buforów. |
private |
Odpowiedź | Odpowiedź jest przeznaczona dla pojedynczego użytkownika, nie dla współdzielonych buforów. |
public |
Odpowiedź | Pozwala na buforowanie przez dowolny bufor, nawet jeśli normalnie byłoby to zabronione. |
must-understand |
Odpowiedź | Tylko bufory rozumiejące kod statusu mogą buforować odpowiedź. |
immutable |
Odpowiedź | Wskazuje, że zasób nie zmieni się, dopóki jest świeży, używane z wersjonowanymi URL-ami. |
stale-while-revalidate |
Odpowiedź | Pozwala na użycie przestarzałej odpowiedzi podczas walidacji w tle. |
stale-if-error |
Odpowiedź | Umożliwia użycie przestarzałej odpowiedzi w przypadku błędów serwera (np. 500, 502). |
Przykłady praktycznego zastosowania
| Scenariusz | Przykładowa dyrektywa | Opis |
|---|---|---|
| Statyczny zasób, długi czas buforowania | Cache-Control: public, max-age=604800 |
Obraz buforowany przez 7 dni. |
| Dane wrażliwe, brak buforowania | Cache-Control: no-store |
Dane logowania nie są buforowane. |
| Dynamiczna strona, walidacja za każdym razem | Cache-Control: no-cache, must-revalidate |
Strona zawsze walidowana z serwerem. |
| Offline, preferencja buforu | Cache-Control: only-if-cached (żądanie) |
Aplikacja offline używa buforu lub zwraca 504. |
| Wersjonowany zasób JavaScript | Cache-Control: public, max-age=31536000, immutable |
Skrypt buforowany przez rok, niezmienialny. |
| API z tolerancją na przestarzałe dane | Cache-Control: max-age=3600, stale-while-revalidate=86400 |
Dane świeże godzinę, tolerancja dobę. |
| Zasoby CDN z fallbackiem | Cache-Control: max-age=3600, stale-if-error=86400 |
W razie błędu serwera używa starych danych. |
| Personalizowana strona główna | Cache-Control: private, max-age=300 |
Strona buforowana prywatnie przez 5 minut. |
| Dane finansowe | Cache-Control: no-cache, must-revalidate, no-store |
Maksymalne zabezpieczenie danych wrażliwych. |
| RSS feed | Cache-Control: public, max-age=1800 |
Feed publiczny, odświeżany co 30 minut. |
Zalecenia dla różnych typów zasobów
Obrazy i multimedia
Obrazy produktów w sklepach internetowych powinny używać Cache-Control: public, max-age=604800 dla standardowych zasobów lub Cache-Control: public, max-age=31536000, immutable dla wersjonowanych plików. Awatary użytkowników wymagają krótszych czasów ze względu na możliwość zmiany.
Pliki wideo i audio mogą korzystać z bardzo długich czasów buforowania, szczególnie gdy są hostowane na CDN. Dla treści edukacyjnych czy rozrywkowych można stosować czasy buforowania do roku, z odpowiednimi strategiami cache busting przy aktualizacjach.
Favikony i ikony aplikacji powinny używać bardzo długich czasów buforowania z immutable, ponieważ zmieniają się rzadko, a ich rozmiar jest niewielki.
Arkusze stylów i skrypty
CSS i JavaScript wymagają rozróżnienia między zasobami wersjonowanymi a niewersjonowanymi. Wersjonowane pliki mogą używać bardzo długich czasów buforowania z dyrektywą immutable: Cache-Control: public, max-age=31536000, immutable.
Niewersjonowane pliki powinny używać krótszych czasów lub wymuszać walidację: Cache-Control: public, max-age=3600, must-revalidate. Framework-specific bundlery często automatycznie dodają hashe do nazw plików, umożliwiając długie buforowanie.
Dokumenty HTML
Strony główne zazwyczaj powinny używać Cache-Control: no-cache lub krótkich czasów max-age, aby zapewnić aktualność treści. Dla statycznych stron można zastosować umiarkowane czasy buforowania: Cache-Control: public, max-age=3600.
Landing pages marketingowe mogą używać dłuższych czasów buforowania, ale z możliwością szybkiej aktualizacji w przypadku kampanii reklamowych.
Strony błędów (404, 500) powinny mieć krótkie czasy buforowania lub wymagać walidacji, aby umożliwić szybkie poprawki.
API i dane strukturalne
Endpoints RESTful wymagają indywidualnego podejścia w zależności od częstotliwości zmian. Dane często zmieniające się powinny używać no-cache, podczas gdy stabilne informacje mogą korzystać z umiarkowanych czasów buforowania.
Dane konfiguracyjne aplikacji mogą używać długich czasów buforowania z must-revalidate, aby zapewnić spójność konfiguracji przy jednoczesnej optymalizacji wydajności.
Real-time data takie jak kursy walut czy notowania giełdowe wymagają bardzo krótkich czasów buforowania lub całkowitego wyłączenia cache.
Zasoby uwierzytelnione
Personalizowane treści muszą używać dyrektywy private lub no-store, w zależności od wrażliwości danych. Dashboardy użytkowników powinny wykorzystywać Cache-Control: private, max-age=300, must-revalidate.
Tokeny i sesje nigdy nie powinny być buforowane: Cache-Control: no-store, no-cache, must-revalidate.
Dane profilowe mogą używać prywatnego buforowania z krótkim czasem życia: Cache-Control: private, max-age=600.
Optymalizacja wydajności
Strategie cache warming
Cache warming polega na proaktywnym wypełnianiu buforów często używanymi zasobami. Można to osiągnąć przez odpowiednie ustawienie dyrektyw Cache-Control w połączeniu z strategiami pre-loading w aplikacjach.
Dla zasobów krytycznych można używać kombinacji długich czasów buforowania z mechanizmami push notification do CDN-ów, aby zapewnić dostępność zasobów przed ich faktycznym zapotrzebowaniem.
Hierarchiczne buforowanie
W złożonych architekturach z wieloma warstwami buforów (przeglądarki, proxy, CDN, load balancery) ważne jest odpowiednie ustawienie s-maxage dla różnych poziomów. CDN-y mogą buforować zasoby dłużej niż przeglądarki, co pozwala na szybkie dostarczanie przy zachowaniu możliwości szybkich aktualizacji na poziomie użytkownika.
Monitoring i analityka
Skuteczność strategii buforowania można monitorować poprzez analizę nagłówków odpowiedzi, takich jak X-Cache czy CF-Cache-Status w przypadku Cloudflare. Metryki cache hit ratio powinny być regularnie monitorowane i optymalizowane.
Bezpieczeństwo i prywatność
Ochrona danych wrażliwych
Dane osobowe, informacje finansowe i inne wrażliwe treści wymagają szczególnej uwagi przy konfiguracji Cache-Control. Dyrektywa no-store jest obowiązkowa dla takich zasobów, aby zapobiec przypadkowemu buforowaniu.
Szczególną ostrożność należy zachować przy API endpoints zwracających dane użytkowników. Nawet jeśli dane wydają się nieszkodliwe, mogą zawierać informacje pozwalające na identyfikację lub śledzenie użytkowników.
Zapobieganie atakom
Cache poisoning attacks mogą wykorzystywać nieprawidłowo skonfigurowane dyrektywy buforowania. Ważne jest, aby zapewnić odpowiednie walidacje i używać nagłówka Vary do kontroli, które parametry wpływają na buforowanie.
Cross-site request forgery (CSRF) może być ułatwiony przez niewłaściwe buforowanie formularzy czy tokenów. Wszystkie zasoby związane z uwierzytelnianiem powinny używać no-store.
Debugowanie i narzędzia
Narzędzia developerskie
Współczesne przeglądarki oferują zaawansowane narzędzia do analizy buforowania. Chrome DevTools, Firefox Developer Tools i Safari Web Inspector pokazują szczegółowe informacje o nagłówkach Cache-Control i zachowaniu buforów.
Network panel w narzędziach developerskich pokazuje, czy zasób został pobrany z buforu (cache hit) czy z serwera. Kolumna Size często wskazuje „(from disk cache)” lub „(from memory cache)”.
Testowanie strategii buforowania
Skuteczne testowanie strategii buforowania wymaga symulacji różnych scenariuszy: pierwszego załadowania, odświeżenia strony, powrotu po czasie, czy działania offline. Automaty testowe mogą weryfikować poprawność nagłówków i zachowanie aplikacji.
Tools like curl pozwalają na precyzyjne testowanie nagłówków HTTP: curl -I -H "Cache-Control: max-age=0" https://example.com/api/data może symulować wymuszenie odświeżenia.
Problemy i pułapki
Częste błędy konfiguracji
Jednym z najczęstszych błędów jest używanie zbyt długich czasów buforowania dla treści, które mogą się zmieniać. Kolejnym problemem jest nieprawidłowe łączenie dyrektyw, na przykład używanie public i private jednocześnie.
Błędne ustawienie s-maxage może prowadzić do sytuacji, gdzie CDN buforuje zasoby dłużej niż zamierzono, utrudniając szybkie wdrażanie aktualizacji.
Problemy z różnymi implementacjami
Różne systemy buforowania mogą interpretować dyrektywy w nieco odmienny sposób. Niektóre starsze proxy mogą nie rozumieć nowszych dyrektyw jak immutable czy stale-while-revalidate.
Mobilne przeglądarki czasem implementują bardziej agresywne strategie buforowania ze względu na ograniczenia łączy i baterii, co może wpływać na zachowanie aplikacji.
Przyszłość i nowe standardy
Emerging standards
Trwają prace nad nowymi rozszerzeniami Cache-Control, w tym dyrektywami związanymi z machine learning cache prediction i adaptive caching opartym na zachowaniu użytkowników.
HTTP/3 i QUIC wprowadzają nowe możliwości w zakresie multipleksingu i redukcji latencji, co może wpłynąć na strategie buforowania, szczególnie dla real-time applications.
Edge computing i serverless
Rozpowszechnienie edge computing i serverless architectures wymaga nowych podejść do buforowania. Cache-Control musi współpracować z systemami, gdzie zasoby mogą być generowane dynamicznie na edge nodes.
Service Workers w Progressive Web Apps oferują nowe możliwości programistycznego zarządzania cache, które uzupełniają tradycyjne mechanizmy HTTP caching.
Źródła
- RFC 9111 – HTTP Caching: https://httpwg.org/specs/rfc9111.html
- Cache-Control header – HTTP | MDN: https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cache-Control
- RFC 8246 – HTTP Immutable Responses: https://tools.ietf.org/html/rfc8246
- RFC 7234 – Hypertext Transfer Protocol (HTTP/1.1): Caching (Obsoleted by RFC 9111)
- Web Performance Best Practices – Google Developers
- HTTP Caching – Cloudflare Learning Center