Cache przeglądarki — cichy bohater wydajności
Kiedy wchodzisz na stronę internetową po raz drugi, większość zasobów ładuje się niemal natychmiast. To zasługa cache przeglądarki — mechanizmu, który przechowuje lokalne kopie pobranych plików i serwuje je bez kontaktu z serwerem. Poprawnie skonfigurowany cache potrafi zredukować czas ładowania strony o 80-90% przy kolejnych wizytach.
Problem w tym, że domyślna konfiguracja cache rzadko jest optymalna. Zbyt agresywne buforowanie sprawia, że użytkownicy widzą nieaktualne treści. Zbyt słabe — że strona ładuje się wolno. Klucz leży w zrozumieniu nagłówków HTTP odpowiedzialnych za cache.
Cache-Control — główny mechanizm sterowania
Nagłówek Cache-Control to najważniejszy i najpotężniejszy mechanizm kontroli buforowania w HTTP/1.1. Składa się z dyrektyw oddzielonych przecinkami:
max-age=3600— zasób jest ważny przez 3600 sekund (1 godzinę). W tym czasie przeglądarka użyje lokalnej kopii bez pytania serwera.public— zasób może być buforowany przez wszystkie cache'e (przeglądarkę, CDN, proxy). Stosujemy dla plików statycznych.private— tylko przeglądarka użytkownika może buforować zasób. Ważne dla danych osobistych — np. strona profilu.no-cache— przeglądarka przechowuje kopię, ale za każdym razem pyta serwer, czy jest aktualna. Wbrew nazwie, nie oznacza braku cache'owania.no-store— całkowity zakaz buforowania. Zasób nie jest przechowywany nigdzie. Stosujemy dla danych wrażliwych.immutable— zasób nigdy się nie zmieni. Przeglądarka nie będzie weryfikować ważności nawet po odświeżeniu strony. Idealne dla plików z hashem w nazwie.
Przykładowa konfiguracja w Nginx dla plików statycznych z hashem wersji:
location ~* \.(css|js)$ {
add_header Cache-Control "public, max-age=31536000, immutable";
}
Rok cache'owania (max-age=31536000) z immutable — bezpieczne, bo zmiana pliku oznacza zmianę nazwy (np. style.a1b2c3.css).
ETag — cyfrowy odcisk palca zasobu
ETag (Entity Tag) to unikalny identyfikator konkretnej wersji zasobu. Serwer generuje go na podstawie zawartości pliku — najczęściej jest to hash MD5 lub SHA, ale może to być dowolny ciąg znaków.
Mechanizm działania jest elegancki:
- Przy pierwszym żądaniu serwer odpowiada nagłówkiem
ETag: "a1b2c3d4" - Przeglądarka zapisuje zasób wraz z ETagiem
- Przy kolejnym żądaniu przeglądarka wysyła
If-None-Match: "a1b2c3d4" - Serwer porównuje ETag — jeśli się zgadza, odpowiada 304 Not Modified (bez treści)
- Przeglądarka używa lokalnej kopii
Odpowiedź 304 jest minimalna — kilkadziesiąt bajtów zamiast kilobajtów czy megabajtów danych. Oszczędność transferu jest ogromna, choć sam round-trip do serwera nadal występuje.
W Go generowanie ETagu jest proste:
func generateETag(content []byte) string {
hash := sha256.Sum256(content)
return fmt.Sprintf(`"%x"`, hash[:8])
}
Last-Modified — starszy brat ETagu
Nagłówek Last-Modified zawiera datę ostatniej modyfikacji zasobu. Działa analogicznie do ETag, ale zamiast porównywania hashów, porównywane są daty:
- Serwer odpowiada:
Last-Modified: Tue, 02 Sep 2025 10:00:00 GMT - Przeglądarka przy kolejnym żądaniu wysyła:
If-Modified-Since: Tue, 02 Sep 2025 10:00:00 GMT - Serwer sprawdza datę — jeśli zasób nie był modyfikowany, zwraca 304
Last-Modified ma niższą precyzję niż ETag — rozdzielczość to jedna sekunda. Jeśli plik zmienia się częściej, ETag jest lepszym wyborem. W praktyce oba nagłówki często stosuje się jednocześnie — ETag ma wyższy priorytet.
Strategie cache'owania
Różne typy zasobów wymagają różnych strategii. Oto podejście, które stosujemy w naszych projektach:
Pliki statyczne z wersjonowaniem (CSS, JS z hashem w nazwie):
Cache-Control: public, max-age=31536000, immutable- Brak potrzeby rewalidacji — zmiana pliku = nowa nazwa
Pliki statyczne bez wersjonowania (favicon, robots.txt):
Cache-Control: public, max-age=86400- ETag do rewalidacji po wygaśnięciu
- Cache na 24 godziny z weryfikacją
Strony HTML:
Cache-Control: no-cache- ETag lub Last-Modified do rewalidacji
- Przeglądarka zawsze sprawdza aktualność, ale używa cache przy 304
Odpowiedzi API:
Cache-Control: private, max-age=0, must-revalidate- Dla danych publicznych:
Cache-Control: public, max-age=300(5 minut) - Dla danych osobistych:
Cache-Control: private, no-store
Obrazy (zdjęcia, ikony):
Cache-Control: public, max-age=2592000(30 dni)- ETag do rewalidacji
- Obrazy zmieniają się rzadko, ale nie chcemy
immutablebez wersjonowania
Nagłówek Vary — cache a negocjacja treści
Nagłówek Vary informuje cache, że odpowiedź zależy od wartości określonego nagłówka żądania. Najczęstsze zastosowanie:
Vary: Accept-Encoding
To mówi cache'owi: "odpowiedź jest inna dla klientów akceptujących gzip i tych, którzy go nie obsługują". Bez tego nagłówka cache mógłby podać skompresowaną wersję klientowi, który nie obsługuje kompresji.
Inne zastosowania Vary to negocjacja języka (Vary: Accept-Language) czy formatu obrazu (Vary: Accept — WebP vs JPEG).
Debugowanie cache
Narzędzia deweloperskie przeglądarki (zakładka Network) to najlepszy sposób na weryfikację cache. Zwracaj uwagę na:
- Kolumna Status — 200 (pobrany z serwera), 304 (rewalidacja), 200 (from cache) — pobrany z cache bez kontaktu z serwerem
- Nagłówki odpowiedzi — sprawdź
Cache-Control,ETag,Last-Modified - Rozmiar — odpowiedź 304 powinna mieć minimalny rozmiar
Polecenie curl -I jest przydatne do szybkiej weryfikacji nagłówków z linii poleceń:
curl -I https://example.com/style.css
Podsumowanie
Poprawna konfiguracja nagłówków cache to jedno z najskuteczniejszych narzędzi optymalizacji wydajności stron internetowych. Kombinacja Cache-Control z ETag lub Last-Modified pozwala precyzyjnie kontrolować, co i jak długo jest buforowane. Kluczowa zasada: pliki z hashem w nazwie cache'uj agresywnie, strony HTML rewaliduj przy każdym żądaniu.
Chcesz zoptymalizować wydajność swojej strony? Skontaktuj się z nami — przeanalizujemy konfigurację cache i zaproponujemy ulepszenia.