Cache-Control — este un antet HTTP care definește regulile de cache a resurselor pe partea clientului, a serverelor proxy și a CDN-urilor cu ajutorul unui set de directive. Spre deosebire de antetul învechit Expires, Cache-Control suportă zeci de combinații: max-age setează durata de viață în secunde, private și public gestionează disponibilitatea cache-ului, no-cache și no-store — verificarea forțată. Conform Google Web Dev (2025), configurarea corectă a Cache-Control poate reduce timpul de încărcare a paginilor cu 50-80% pentru vizitele repetate. Acest lucru face antetul critic pentru performanța aplicațiilor web și mobile.
Principalele
Cache-Control — este un antet HTTP, standardizat în HTTP/1.1 (RFC 7234), care permite serverului să specifice cum și pentru cât timp clienții, proxy-urile și CDN-urile pot stoca în cache răspunsul. Spre deosebire de Expires (HTTP/1.0), Cache-Control utilizează directive — comenzi text care se combină prin virgule: Cache-Control: public, max-age=3600, must-revalidate. Antetul oferă un control fin asupra fiecărei verigi a lanțului de cache.
Cache-ul este unul dintre mecanismele fundamentale de performanță ale web-ului și aplicațiilor mobile. Fără el, fiecare cerere a utilizatorului ar merge direct la server, provocând încărcare excesivă și întârzieri. Cache-Control definește trei niveluri de cache: browser/aplicație (private cache), servere proxy (shared cache) și CDN (distributed cache). Fiecare nivel interpretează directivele în felul său.
Configurarea incorectă a Cache-Control este una dintre cele mai frecvente cauze ale problemelor de performanță. Un cache prea agresiv face ca utilizatorii să vadă date învechite. Un cache prea slab duce la cereri excesive la server și încărcare lentă. Conform Akamai (2025), optimizarea Cache-Control pentru conținutul static reduce încărcarea serverului cu 70-90% și îmbunătățește timpul de încărcare cu 40-60% pentru utilizatorii mobili.
Cache-Control a apărut în HTTP/1.1 (RFC 2616, 1999) ca înlocuitor al Expires. Expires avea o problemă fundamentală: folosea o dată absolută care depindea de fusul orar al serverului și al clientului. Cache-Control a rezolvat această problemă trecând la timp relativ (max-age în secunde de la momentul primirii răspunsului). Ulterior, în RFC 7234 (2014) au fost adăugate directive noi: immutable pentru statice, stale-while-revalidate și stale-if-error pentru verificarea întârziată.
Cache-Control include peste 10 directive împărțite în trei grupuri: directive de cerere (client → server), directive de răspuns (server → client) și extensii. În practică, în dezvoltarea mobilă se folosesc 6-7 directive de răspuns de bază care acoperă 95% din scenariile de cache. Să le analizăm pe fiecare cu exemple și recomandări.
| Directiva | Semnificație | Exemplu |
|---|---|---|
| max-age | Durata de viață în secunde de la momentul răspunsului | max-age=3600 — 1 oră |
| s-maxage | max-age pentru shared cache (proxy, CDN) | s-maxage=86400 — 1 zi pentru CDN |
| public | Permite cache tuturor (inclusiv proxy) | public, max-age=3600 |
| private | Permite cache doar browserului/aplicației | private, max-age=600 |
| no-cache | Nu utiliza fără verificare (304 obligatoriu) | no-cache |
| no-store | Interzicerea completă a cache-ului | no-store |
| must-revalidate | După max-age, verificare obligatorie la origin | max-age=3600, must-revalidate |
| immutable | Resursa nu se va schimba (pentru statice versionate) | max-age=31536000, immutable |
max-age — cea mai importantă directivă. Interzice clientului să facă o cerere la server pentru o perioadă specificată. Pentru statice (CSS, JS, imagini) max-age se setează de obicei de la 1 zi la 1 an. Pentru răspunsurile API — de la 0 secunde (date mereu proaspete) la 5-10 minute (date de referință). s-maxage permite setarea unei durate de viață diferite pentru CDN și browser: CDN păstrează copia 1 zi, browserul — 1 oră.
Aceste două directive sunt adesea confundate. no-cache nu interzice cache-ul — necesită verificarea copiei stocate în cache la fiecare utilizare printr-o cerere condiționată (If-Modified-Since sau If-None-Match). Dacă serverul răspunde cu 304 — clientul folosește cache-ul. Dacă 200 — actualizează. no-store, în schimb, interzice complet salvarea răspunsului în orice cache, inclusiv pe disc și în memorie. Folosiți no-store doar pentru date sensibile — token-uri, date de plată, documente personale.
Antetul Expires (HTTP/1.0) indică, de asemenea, durata de viață a resursei, dar folosește o dată absolută: Expires: Thu, 03 Jul 2026 12:00:00 GMT. Cache-Control max-age — timp relativ de la momentul răspunsului. Diferența este critică pentru sistemele distribuite: dacă serverul și clientul se află în fusuri orare diferite, Expires poate fi interpretat incorect. Cache-Control nu are această problemă — 3600 de secunde sunt întotdeauna 3600 de secunde.
Când ambele antete sunt prezente, Cache-Control are prioritate față de Expires. Acest lucru este definit în RFC 7234: „Dacă răspunsul conține Cache-Control cu directiva max-age, destinatarul TREBUIE să ignore Expires”. În practică, se recomandă să nu se returneze Expires pentru clienții moderni, deoarece Cache-Control acoperă toate scenariile Expires. Cu toate acestea, pentru compatibilitate inversă cu proxy-uri și browsere vechi, se pot returna ambele antete.
Expires a rămas în principal pentru conținutul static pe Nginx și Apache — aceste servere adaugă automat ambele antete. Dacă în proiectul dumneavoastră înâlniți Expires fără Cache-Control, înlocuiți-l cu Cache-Control cu max-age: precizia gestionării cache-ului crește, iar dependența de fusul orar este eliminată. Pentru migrare, este suficient să configurați serverul să adauge Cache-Control în loc de Expires.
# Nginx: Cache-Control pentru fișiere statice
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 30d;
add_header Cache-Control "public, immutable, max-age=2592000";
}
# Politici diferite pentru tipuri diferite de conținut
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";
}
În configurarea Nginx pentru fișierele statice (CSS, JS, imagini) se setează Cache-Control pe 30 de zile cu atributul immutable — acest atribut informează browserul că resursa nu se schimbă niciodată sub această adresă URL (versionare prin hash în numele fișierului). Punctele finale API utilizează no-cache pentru datele dinamice și public cu max-age scurt pentru datele de referință — liste frecvent solicitate și rar modificate.
În aplicațiile mobile, Cache-Control joacă un rol special datorită limitărilor rețelelor mobile: latență ridicată, conexiune instabilă, limite de trafic. Un cache corect permite utilizatorului să vadă datele instantaneu, chiar și offline, și să le actualizeze în fundal. OkHttp pe Android și URLSession pe iOS au sisteme de cache încorporate care iau în considerare Cache-Control.
OkHttp folosește CacheInterceptor, care citește Cache-Control din răspuns și gestionează automat cache-ul. Dacă serverul a returnat Cache-Control: max-age=3600, OkHttp nu va face o cerere la server timp de o oră. După expirarea max-age, OkHttp trimite o cerere condiționată cu If-Modified-Since și If-None-Match. Configurarea cache-ului în 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()
}
Codul creează un OkHttpClient cu un cache de 10 MB și suprascrie Cache-Control prin NetworkInterceptor. Dacă serverul nu returnează Cache-Control sau folosește Expires, interceptoul adaugă public, max-age=300 (5 minute). Interceptorul îndepărtează antetul învechit Pragma (HTTP/1.0) pentru compatibilitate. După un model similar funcționează cache-ul pe iOS prin URLCache.shared cu configurarea memoryCapacity și diskCapacity.
Directiva stale-while-revalidate permite utilizatorului să vadă cache-ul învechit (stale) în timp ce aplicația încarcă date proaspete în fundal. Acest lucru oferă un efect de răspuns instantaneu: utilizatorul vede conținutul imediat, iar după o secundă acesta se actualizează. Suportat de OkHttp îNCEpând cu versiunea 3.10 și URLCache pe iOS 14+. Exemplu: Cache-Control: max-age=3600, stale-while-revalidate=300 — 1 oră de cache actual, apoi 5 minute de afișare a cache-ului învechit cu actualizare în fundal.
Diferite tipuri de resurse necesită strategii diferite de cache. Să analizăm configurațiile optime pentru scenariile tipice în dezvoltarea mobilă. Pentru conținutul static cu hash în numele fișierului (bundle.abc123.js) se poate seta max-age de până la 1 an cu immutable. Pentru listele API care se actualizează rar (cataloage, categorii) — max-age de la 5 minute la 1 oră cu stale-while-revalidate.
| Tipul resursei | Cache-Control | Explicație |
|---|---|---|
| Statice versionate | public, max-age=31536000, immutable | 1 an, fișierele nu se schimbă (hash în URL) |
| Statice neversionate | public, max-age=86400, must-revalidate | 1 zi cu verificare obligatorie după |
| API: date de referință | public, max-age=600, stale-while-revalidate=60 | 10 minute cache + 1 minut stale |
| API: date utilizator | private, max-age=60 | 1 minut, doar pentru utilizatorul respectiv |
| API: date sensibile | no-store | Interzicerea completă a cache-ului |
| Pagini HTML | no-cache, must-revalidate | Verificare la fiecare cerere, 304 la neschimbare |
Este important să țineți cont de securitate: pentru răspunsurile care conțin date personale ale utilizatorului, setați întotdeauna private. Fără această directivă, un proxy public (de exemplu, corporativ) poate stoca în cache răspunsul și îl poate transmite unui alt utilizator. Pentru token-uri de autentificare și informații de plată, folosiți no-store — chiar și cache-ul privat nu ar trebui să salveze aceste date pe disc.
Pentru a verifica corectitudinea Cache-Control, utilizați antetul Age (câte secunde este păstrat cache-ul) și X-Cache (hit/miss pe CDN). În browser — fila Network, coloana Size arată „from disk cache” sau „304 Not Modified”. Dacă resursa ar trebui să fie stocată în cache dar se încarcă de fiecare dată — verificați dacă serverul nu adaugă Cache-Control: no-cache sau Pragma: no-cache împreună cu directivele dumneavoastră.
întrebări frecvente
max-age funcționează pentru toate cache-urile (inclusiv browsere), s-maxage — doar pentru shared cache (proxy, CDN). Dacă este specificat s-maxage, CDN-ul ignoră max-age și folosește s-maxage. Aceasta permite setarea unei durate de viață diferite pentru browser și CDN.
Nu, după trimiterea răspunsului cu max-age, clientul nu va face o cerere până la expirarea timerului. Pentru invalidarea imediată a cache-ului, trebuie să modificați URL-ul resursei (să adăugați o versiune/hash) și să trimiteți notificări push sau mesaje WebSocket pentru resetarea forțată.
Directiva immutable (RFC 8246) informează browserul că resursa nu se va schimba niciodată sub această adresă URL. Browserul nici măcar nu încearcă să facă o cerere condiționată la reîncărcarea paginii — folosește cache-ul până la expirarea max-age. Funcționează doar cu fișiere versionate.
Googlebot ține cont de Cache-Control: un cache lung accelerează re-scanarea. noindex cu cache rapid — ok. no-store poate încetini indexarea, deoarece Googlebot va încărca pagina de fiecare dată de la zero. Un max-age prea scurt crește încărcarea serverului în timpul scanării.
Prin helmet sau middleware: res.set('Cache-Control', 'public, max-age=3600'). Pentru statice, folosiți express.static cu parametrul maxAge: express.static('public', {maxAge: '1y'}). Pentru rute dinamice — individual în fiecare handler.
Rezumat
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și