HTTP 304 Not Modified to kod odpowiedzi z klasy 3xx, który mówi przeglądarce, że zasób nie zmienił się i można go wczytać z pamięci podręcznej.
To nie błąd, lecz mechanizm optymalizacyjny obniżający transfer i przyspieszający ładowanie stron.
Gdy jednak konfiguracja jest nieprawidłowa lub cache ulega uszkodzeniu, 304 może pojawiać się „bez powodu” i blokować dostęp do treści.
Podstawowe pojęcia – co to jest HTTP 304 i jak funkcjonuje w ekosystemie internetu
Definicja i klasyfikacja HTTP 304 Not Modified
HTTP 304 Not Modified zostało zdefiniowane w RFC 9110 (IETF) i służy do walidacji cache po stronie klienta.
304 nie sygnalizuje ani sukcesu, ani błędu – to informacja dla przeglądarki, że może użyć lokalnej kopii zasobu bez pobierania go z serwera.
Mechanizm ten wyraźnie redukuje obciążenie sieci i przyspiesza działanie serwisów, szczególnie przy wielu powtarzalnych żądaniach.
Anatomia warunkowego żądania HTTP
Podstawą 304 są warunkowe żądania, w których przeglądarka wysyła do serwera informacje o posiadanej wersji zasobu.
Mechanizm oparty o ETag wykorzystuje nagłówki ETag i If-None-Match. Jeśli wartości są zgodne, serwer odpowiada 304 i nie przesyła treści.
Mechanizm oparty o Last-Modified korzysta z Last-Modified i If-Modified-Since. Jeśli zasób nie zmienił się po wskazanej dacie, odpowiedzią również jest 304.
Przy odpowiedzi 304 serwer nie wysyła body, ale może dołączyć nagłówki aktualizujące politykę cache.
W odpowiedzi 304 najczęściej spotkasz następujące nagłówki:
- cache-control,
- expires,
- etag,
- vary.
Mechanika działania HTTP 304 w praktyce – od żądania do odpowiedzi
Sekwencja zdarzeń przy pierwszej wizycie na stronie
Przy pierwszym wejściu przeglądarka wysyła zwykłe żądanie i otrzymuje 200 OK z treścią oraz nagłówkami cache (Cache-Control, ETag, Last-Modified, Expires).
Następnie przechowuje zasób i metadane w pamięci podręcznej zgodnie z dyrektywą Cache-Control (np. max-age=3600).
Proces rewalidacji zasobów buforowanych
Po upływie ważności lub przy „odśwież” przeglądarka wysyła warunkowe żądanie z If-None-Match lub If-Modified-Since.
Jeśli wersja na serwerze jest identyczna, odpowiedź to 304; w przeciwnym razie serwer zwraca 200 z nową wersją i nagłówkami cache.
Nawet 20% żądań może kończyć się 304 – to realna oszczędność czasu i pasma, zwłaszcza przy właściwym użyciu dyrektywy immutable.
Przyczyny błędu HTTP 304 – kiedy optymalizacja staje się problemem
Rozróżnienie między legendarnym kodem 304 a problemowym „błędem 304”
Sam kod 304 nie jest błędem, ale błędy konfiguracji lub uszkodzony cache potrafią wywołać zjawisko „stale 304” – przeglądarka dostaje 304, lecz nie potrafi wyświetlić zawartości.
Do takich sytuacji prowadzą problemy po stronie serwera i/lub klienta:
- Problemy po stronie serwera – błędna konfiguracja cache (np.
mod_cachew Apache), niewłaściwe nagłówkiCache-Control, konfliktowe ETagi w środowisku z load balancerem, błędy DNS; - Problemy po stronie klienta – uszkodzony cache przeglądarki, złośliwe oprogramowanie ingerujące w ruch HTTP, wadliwe lub konfliktowe rozszerzenia przeglądarki.
Powszechne przyczyny związane z rejestracją i buforowaniem zasobów
Zbyt długie TTL-e (np. Cache-Control: max-age=31536000) dla często zmieniających się plików (CSS/JS) utrwalają stare wersje u użytkowników.
Niespójne ETagi (np. różne węzły za load balancerem generują inne wartości) uniemożliwiają prawidłową rewalidację albo powodują nieprzewidywalne 304.
Problemy DNS (stare wpisy, zainfekowany plik hosts) mogą kierować ruch do niewłaściwych serwerów i zwracać 304 wobec nieaktualnych zasobów.
Metody diagnostyki i lokalizowania przyczyn błędu HTTP 304
Używanie narzędzi deweloperskich przeglądarki do analizy nagłówków HTTP
Włącz narzędzia dev (F12) i w panelu Network porównaj nagłówki żądania/odpowiedzi, status i rozmiary.
W żądaniu szukaj If-None-Match i If-Modified-Since. W odpowiedzi zweryfikuj Cache-Control, ETag, Last-Modified, Expires, Vary.
Oto zestaw nagłówków, na których warto skupić uwagę podczas analizy:
- if-none-match,
- if-modified-since,
- cache-control,
- etag,
- last-modified,
- expires,
- vary.
W Firefox możesz testować tryb offline; w Safari włącz menu Develop, by dostać się do narzędzi.
Testowanie i walidacja za pomocą narzędzi online
curl pozwala wysyłać żądania warunkowe i sprawdzać odpowiedź serwera:
curl --http1.1 -I --header 'If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT' https://example.com
Użyj też GTmetrix i Google PageSpeed Insights, aby wykrywać braki w nagłówkach cache i problemy z rewalidacją.
Kompleksowe strategie rozwiązywania problemów z HTTP 304
Działania po stronie klienta – szybka ścieżka naprawy
Jeśli 304 blokuje wyświetlanie treści, wykonaj następujące kroki w podanej kolejności:
- Wyczyść pamięć podręczną i pliki cookie przeglądarki (zakres: cały czas).
- Wykonaj „hard refresh” strony (Windows: Ctrl+F5 / Ctrl+Shift+R; macOS: Cmd+Shift+R).
- Wyłącz rozszerzenia (szczególnie adblock, VPN/proxy, narzędzia debug) i sprawdź stronę ponownie.
- Przeskanuj system pod kątem malware (Windows Defender, narzędzie czyszczenia w Chrome pod
chrome://settings/cleanup). - Wyczyść cache DNS i odśwież połączenie sieciowe.
- Sprawdź zachowanie w innej przeglądarce/profilu użytkownika.
Najczęściej winny jest uszkodzony cache lub konfliktujące rozszerzenie – dlatego zaczynaj od tych kroków.
Usuwanie złośliwego oprogramowania i rozszerzenia przeglądarki
W Chrome użyj chrome://settings/cleanup, w Windows skorzystaj z Windows Defendera.
Wyłączaj rozszerzenia pojedynczo (chrome://extensions, about:addons) i po każdym teście sprawdzaj stronę. Winne usuń lub zaktualizuj.
Czyszczenie DNS i resetowanie TCP/IP
Odśwież lokalny cache DNS, by wyeliminować stare wpisy kierujące na niewłaściwe IP.
Windows:
ipconfig /flushdns
macOS:
sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder
Linux: zrestartuj usługę DNS właściwą dla używanego resolvera (np. systemd-resolved, dnsmasq).
Możesz też tymczasowo przełączyć się na Google DNS (8.8.8.8, 8.8.4.4), aby wykluczyć problemy po stronie dostawcy.
Działania po stronie serwera – konfiguracja Apache i Nginx
W Apache przejrzyj konfigurację mod_cache oraz politykę dla zasobów dynamicznych/statycznych.
Pliki konfiguracyjne: /etc/apache2/httpd.conf, /etc/apache2/apache2.conf. Po zmianach wykonaj restart:
sudo systemctl restart apache2
W Nginx zweryfikuj proxy_cache_path, proxy_cache, proxy_cache_valid oraz uprawnienia do katalogu cache (np. /var/cache/nginx).
Korekta ustawień Cache-Control i nagłówków buforowania
Właściwe dyrektywy Cache-Control zapobiegają „stale 304” i poprawiają wydajność. Poniżej praktyczne warianty i zastosowania:
| Kategoria zasobu | Przykładowy nagłówek | Typowe zastosowanie | Efekt |
|---|---|---|---|
| Statyczne, rzadko zmieniane | Cache-Control: public, max-age=31536000, immutable |
obrazy, czcionki, skompilowane JS/CSS | maksymalna wydajność bez rewalidacji |
| Półstatyczne | Cache-Control: public, max-age=0, must-revalidate |
artykuły, strony informacyjne | regularna rewalidacja, częste 304 zamiast 200 |
| Zawsze dynamiczne | Cache-Control: no-store |
API, treści generowane per żądanie | brak przechowywania po stronie klienta |
| Dane prywatne | Cache-Control: private, no-cache |
zasoby specyficzne dla użytkownika | cache tylko w przeglądarce, rewalidacja wymagana |
Optymalizacja buforowania – strategie maksymalizowania wydajności
Implementacja entity tags (ETags)
Konsekwentne ETagi są kluczowe, zwłaszcza w środowiskach z wieloma węzłami za load balancerem.
Rozważ generowanie ETagów z hashów zawartości lub wersjonowanie plików – unikniesz rozbieżności między serwerami.
Przykład w Node.js (ETag na bazie MD5 treści):
const crypto = require('crypto');
app.get('/api/data', (req, res) => {
const data = JSON.stringify({ message: 'Hello, world!' });
const etag = crypto.createHash('md5').update(data).digest('hex');
res.setHeader('ETag', `"${etag}"`);
res.setHeader('Cache-Control', 'max-age=3600');
if (req.headers['if-none-match'] === `"${etag}"`) {
res.status(304).end();
} else {
res.json(JSON.parse(data));
}
});
Strategia unieważniania cache’u dla zasobów statycznych
Cache busting (hash w nazwie pliku lub parametrze wersji) wymusza pobranie nowej wersji po zmianie treści.
Przykład: styles.a1b2c3d4.css zamiast styles.css lub styles.css?v=1.0.5.
Popularne narzędzia buildujące, które automatycznie dodają hash do nazw plików, to między innymi:
- webpack,
- rollup,
- parcel.
Integracja CDN i pamięci podręcznych reverse proxy
CDN (np. Cloudflare, Akamai, AWS CloudFront) skraca drogę do zasobów i odciąża serwer źródłowy.
Reverse proxy cache (np. Varnish) serwuje odpowiedzi z bliskiego cache, a dyrektywa s-maxage pozwala sterować TTL-em dla cache współdzielonych.
Łącz dyrektywy przeglądarkowe i CDN-owe (np. stale-while-revalidate), aby równoważyć świeżość i wydajność.
Zapobieganie problemom – najlepsze praktyki zarządzania buforowaniem
Testowanie i monitorowanie konfiguracji buforowania
Regularnie testuj rewalidację skryptami (curl, wget) i monitoruj raporty z GTmetrix oraz PageSpeed Insights.
Weryfikuj statusy 304/200 po zmianach w konfiguracji, by szybko wykrywać niepożądane zachowania.
Dokumentacja i standaryzacja polityki buforowania
Ustal i udokumentuj politykę cache dla kategorii zasobów: statyczne, półstatyczne, dynamiczne, prywatne.
Wdrażaj zmiany etapowo (staging → produkcja) i miej checklistę testów, aby uniknąć regresji wydajności i błędów 304.