Cache-Control — HTTP hlavička, která definuje pravidla ukládání zdrojů do cache na straně klienta, proxy serverů a CDN pomocí sady směrnic. Na rozdíl od zastaralé hlavičky Expires podporuje Cache-Control desítky kombinací: max-age nastavuje dobu života v sekundách, private a public řídí dostupnost cache, no-cache a no-store vynucenou kontrolu. Podle Google Web Dev (2025), správná konfigurace Cache-Control může snížit dobu načítání stránek o 50–80% pro opakované návštěvy. To činí tuto hlavičku kritickou pro výkon webových a mobilních aplikací.
Hlavní body
Cache-Control — HTTP hlavička, standardizovaná v HTTP/1.1 (RFC 7234), která umožňuje serveru určit, jak a jak dlouho mohou klienti, proxy a CDN ukládat odpověď do cache. Na rozdíl od Expires (HTTP/1.0) používá Cache-Control směrnice — textové příkazy, které se kombinují čárkou: Cache-Control: public, max-age=3600, must-revalidate. Hlavička poskytuje jemnou kontrolu nad každým článkem řetězce ukládání do cache.
Ukládání do cache je jedním ze základních mechanismů výkonu webu a mobilních aplikací. Bez něj by každý požadavek uživatele šel přímo k serveru, což by způsobovalo nadměrnou zátěž a zpoždění. Cache-Control definuje tři úrovně ukládání do cache: prohlížeč/aplikace (private cache), proxy servery (shared cache) a CDN (distributed cache). Každá úroveň interpretuje směrnice svým způsobem.
Nesprávná konfigurace Cache-Control je jednou z nejčastějších příčin problémů s výkonem. Příliš agresivní ukládání do cache způsobuje, že uživatelé vidí zastaralá data. Příliš slabé ukládání vede k nadměrným požadavkům na server a pomalému načítání. Podle Akamai (2025), optimalizace Cache-Control pro statický obsah snižuje zátěž serveru o 70–90% a zlepšuje dobu načítání o 40–60% pro mobilní uživatele.
Cache-Control se objevil v HTTP/1.1 (RFC 2616, 1999) jako náhrada za Expires. Expires měl zásadní problém: používal absolutní datum, které záviselo na časovém pásmu serveru a klienta. Cache-Control tento problém vyřešil přechodem na relativní čas (max-age v sekundách od okamžiku obdržení odpovědi). Později byly v RFC 7234 (2014) přidány nové směrnice: immutable pro statické soubory, stale-while-revalidate a stale-if-error pro odloženou kontrolu.
Cache-Control obsahuje přes 10 směrnic rozdělených do tří skupin: směrnice požadavku (klient → server), směrnice odpovědi (server → klient) a rozšíření. V praxi se v mobilním vývoji používá 6–7 základních směrnic odpovědi, které pokrývají 95% scénářů ukládání do cache. Každou si prohlédneme s příklady a doporučeními.
| Směrnice | Význam | Příklad |
|---|---|---|
| max-age | Doba života v sekundách od okamžiku odpovědi | max-age=3600 — 1 hodina |
| s-maxage | max-age pro shared cache (proxy, CDN) | s-maxage=86400 — 1 den pro CDN |
| public | Povoluje ukládání do cache všem (včetně proxy) | public, max-age=3600 |
| private | Povoluje cache pouze prohlížeči/aplikaci | private, max-age=600 |
| no-cache | Nepoužívat bez kontroly (304 povinné) | no-cache |
| no-store | Úplný zákaz ukládání do cache | no-store |
| must-revalidate | Po max-age povinně zkontrolovat u originu | max-age=3600, must-revalidate |
| immutable | Zdroj se nezmění (pro verzované statické soubory) | max-age=31536000, immutable |
max-age — nejdůležitější směrnice. Zakazuje klientovi odesílat požadavek na server po stanovenou dobu. Pro statické soubory (CSS, JS, obrázky) se max-age obvykle nastavuje od 1 dne do 1 roku. Pro API odpovědi — od 0 sekund (vždy čerstvá data) do 5–10 minut (referenční data). s-maxage umožňuje nastavit různou dobu života pro CDN a prohlížeč: CDN ukládá kopii 1 den, prohlížeč 1 hodinu.
Tyto dvě směrnice jsou často zaměňovány. no-cache nezakazuje ukládání do cache — vyžaduje kontrolu uložené kopie při každém použití prostřednictvím podmíněného požadavku (If-Modified-Since nebo If-None-Match). Pokud server odpoví 304 — klient použije cache. Pokud 200 — aktualizuje. no-store naopak zcela zakazuje ukládání odpovědi do jakékoli cache, včetně diskové a operační. Používejte no-store pouze pro citlivá data — tokeny, platební údaje, osobní dokumenty.
Hlavička Expires (HTTP/1.0) také udává dobu života zdroje, ale používá absolutní datum: Expires: Thu, 03 Jul 2026 12:00:00 GMT. Cache-Control max-age — relativní čas od okamžiku odpovědi. Rozdíl je kritický pro distribuované systémy: pokud jsou server a klient v různých časových pásmech, může být Expires nesprávně interpretován. Cache-Control tento problém nemá — 3600 sekund je vždy 3600 sekund.
Když jsou přítomny obě hlavičky, Cache-Control má přednost před Expires. To je definováno v RFC 7234: „Pokud odpověď obsahuje Cache-Control se směrnicí max-age, příjemce MUSÍ ignorovat Expires”. V praxi se doporučuje nevracet Expires pro moderní klienty, protože Cache-Control pokrývá všechny scénáře Expires. Pro zpětnou kompatibilitu se starými proxy a prohlížeči však lze vrátit obě hlavičky.
Expires zůstal především pro statický obsah na Nginx a Apache — tyto servery automaticky přidávají obě hlavičky. Pokud se ve vašem projektu setkáte s Expires bez Cache-Control, nahraďte jej Cache-Control s max-age: přesnost řízení cache se zvyšuje a závislost na časovém pásmu je odstraněna. Pro migraci stačí nakonfigurovat server tak, aby přidával Cache-Control místo Expires.
# Nginx: Cache-Control pro statické soubory
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 30d;
add_header Cache-Control "public, immutable, max-age=2592000";
}
# Různé politiky pro různé typy obsahu
location /api/config {
expires -1;
add_header Cache-Control "no-cache, must-revalidate";
}
location /api/static-data {
expires 5m;
add_header Cache-Control "public, max-age=300";
}
V konfiguraci Nginx pro statické soubory (CSS, JS, obrázky) se nastavuje Cache-Control na 30 dní s atributem immutable — tento atribut informuje prohlížeč, že se zdroj nikdy nemění pod touto URL (verzování pomocí hash v názvu souboru). API endpointy používají no-cache pro dynamická data a public s krátkým max-age pro referenční data — často požadované a zřídka se měnící seznamy.
V mobilních aplikacích hráje Cache-Control zvláštní roli kvůli omezením mobilních sítí: vysoké latenci, nestabilnímu připojení, limitům provozu. Správné ukládání do cache umožňuje uživateli vidět data okamžitě, dokonce i offline, a aktualizovat je na pozadí. OkHttp na Androidu a URLSession na iOS mají vestavěné systémy cache, které berou v úvahu Cache-Control.
OkHttp používá CacheInterceptor, který čte Cache-Control z odpovědi a automaticky řídí ukládání do cache. Pokud server vrátil Cache-Control: max-age=3600, OkHttp nebude odesílat požadavek na server po dobu jedny hodiny. Po vypršení max-age odešle OkHttp podmíněný požadavek s If-Modified-Since a If-None-Match. Nastavení cache v OkHttp: OkHttpClient.Builder().cache(Cache(directory, maxSize)).
fun createCachedClient(cacheDir: File): OkHttpClient {
return OkHttpClient.Builder()
.cache(Cache(cacheDir, 10L * 1024 * 1024))
.addNetworkInterceptor { chain ->
val response = chain.proceed(chain.request())
response.newBuilder()
.header("Cache-Control",
"public, max-age=300")
.removeHeader("Pragma")
.build()
}
.build()
}
Kód vytváří OkHttpClient s cache 10 MB a přepisuje Cache-Control pomocí NetworkInterceptor. Pokud server nevrací Cache-Control nebo používá Expires, interceptor přidá public, max-age=300 (5 minut). Interceptor odstraňuje zastaralou hlavičku Pragma (HTTP/1.0) pro kompatibilitu. Podle stejného schématu funguje cache na iOS pomocí URLCache.shared s konfigurací memoryCapacity a diskCapacity.
Směrnice stale-while-revalidate umožňuje uživateli vidět zastaralou cache (stale), zatímco aplikace na pozadí načítá čerstvá data. To poskytuje efekt okamžité odezvy: uživatel vidí obsah ihned a po sekundě se aktualizuje. Podporováno OkHttp od verze 3.10 a URLCache na iOS 14+. Příklad: Cache-Control: max-age=3600, stale-while-revalidate=300 — 1 hodina aktuální cache, poté 5 minut zobrazení zastaralé cache s aktualizací na pozadí.
Různé typy zdrojů vyžadují různé strategie ukládání do cache. Podívejme se na optimální konfigurace pro typické scénáře v mobilním vývoji. Pro statický obsah s hash v názvu souboru (bundle.abc123.js) lze nastavit max-age až na 1 rok s immutable. Pro API seznamy, které se aktualizují zřídka (katalogy, kategorie) — max-age od 5 minut do 1 hodiny se stale-while-revalidate.
| Typ zdroje | Cache-Control | Vysvětlení |
|---|---|---|
| Verzované statické soubory | public, max-age=31536000, immutable | 1 rok, soubory se nemění (hash v URL) |
| Neverzované statické soubory | public, max-age=86400, must-revalidate | 1 den s povinnou kontrolou poté |
| API: referenční data | public, max-age=600, stale-while-revalidate=60 | 10 minut cache + 1 minuta stale |
| API: uživatelská data | private, max-age=60 | 1 minuta, pouze pro konkrétního uživatele |
| API: citlivá data | no-store | Úplný zákaz ukládání do cache |
| HTML stránky | no-cache, must-revalidate | Kontrola při každém požadavku, 304 při nezměněnosti |
Je důležité pamatovat na bezpečnost: pro odpovědi obsahující osobní údaje uživatele vždy nastavte private. Bez této směrnice může veřejný proxy (např. firemní) uložit odpověď do cache a předat ji jinému uživateli. Pro autentizační tokeny a platební informace používejte no-store — ani private cache by neměla ukládat tato data na disk.
Pro kontrolu správnosti Cache-Control použijte hlavičku Age (kolik sekund je cache uložena) a X-Cache (hit/miss na CDN). V prohlížeči — karta Network, sloupec Size zobrazuje „from disk cache” nebo „304 Not Modified”. Pokud by měl být zdroj ukládán do cache, ale načítá se pokaždé — zkontrolujte, zda server nepřidává Cache-Control: no-cache nebo Pragma: no-cache spolu s vašimi směrnicemi.
Často kladené otázky
max-age platí pro všechny cache (včetně prohlížečů), s-maxage — pouze pro shared cache (proxy, CDN). Pokud je uveden s-maxage, CDN ignoruje max-age a používá s-maxage. To umožňuje nastavit různou dobu života pro prohlížeč a CDN.
Ne, po odeslání odpovědi s max-age nebude klient odesílat požadavek do vypršení časovače. Pro okamžitou invalidaci cache je třeba změnit URL zdroje (přidat verzi/hash) a rozeslat push oznámení nebo WebSocket zprávy pro vynucený reset.
Směrnice immutable (RFC 8246) informuje prohlížeč, že se zdroj nikdy nezmění pod touto URL. Prohlížeč se ani nepokouší odeslat podmíněný požadavek při obnovení stránky — používá cache do vypršení max-age. Funguje pouze s verzovanými soubory.
Googlebot bere v úvahu Cache-Control: dlouhé ukládání do cache urychluje opětovné skenování. noindex s rychlou cache — ok. no-store může zpomalit indexaci, protože Googlebot bude stránku načítat pokaždé od začátku. Příliš krátký max-age zvyšuje zátěž serveru během skenování.
Přes helmet nebo middleware: res.set('Cache-Control', 'public, max-age=3600'). Pro statické soubory použijte express.static s parametrem maxAge: express.static('public', {maxAge: '1y'}). Pro dynamické trasy — individuálně v každém handleru.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také