HTTP Caching — Cache-Control, ETag, CDN vodic
Kako ubrzati sajt 5-10x za ponovne posete pravilnim kesiranjem resursa
Sadrzaj
1. Sta je HTTP caching i zasto je bitan
HTTP caching je mehanizam koji cuva kopije resursa (HTML, CSS, JS, slike) blize korisniku — u browseru, CDN-u ili proxy serveru. Kad korisnik ponovo poseti sajt, resursi se ucitavaju iz kesa umesto da se ponovo preuzimaju sa servera.
- Brzina: Kesiran resurs se ucitava za ~1ms umesto 200-500ms sa servera
- Bandwidth: Smanjuje kolicinu podataka za 60-90% za ponovne posete
- Server opterecenje: Manje zahteva ka serveru = bolje skaliranje
- Offline pristup: Sa Service Worker-om, sajt moze raditi bez interneta
2. Cache-Control header
Cache-Control je najvazniji HTTP header za kontrolu kesiranja. Server ga salje u odgovoru, a browser/CDN ga postuje.
| Direktiva | Znacenje | Primer |
|---|---|---|
max-age=N | Kesiraj N sekundi od zahteva | max-age=3600 = 1 sat |
s-maxage=N | Kao max-age ali samo za CDN/proxy | s-maxage=86400 = CDN cuva 1 dan |
public | Bilo ko moze kesirati (browser + CDN) | Staticke slike, CSS, JS |
private | Samo browser kesa (ne CDN) | Personalizovani sadrzaj, sesije |
no-cache | Kesiraj ALI revalidiraj svaki put | HTML stranice (cest sadrzaj) |
no-store | NE kesiraj uopste | Osetljivi podaci (bankarstvo) |
must-revalidate | Posle isteka, MORA revalidirati | Sa max-age za striktnost |
immutable | Resurs se NIKAD ne menja | Fajlovi sa hash-om u imenu |
stale-while-revalidate=N | Koristi stari kes dok revalidira u pozadini | Balans brzine i svezine |
no-cache NE znaci "ne kesiraj"! Znaci "kesiraj ali proveri sa serverom svaki put". Za potpuno iskljucivanje kesa koristite no-store.3. Preporuke po tipu resursa
| Resurs | Cache-Control | Zasto |
|---|---|---|
| HTML | no-cache ili max-age=0, must-revalidate | Sadrzaj se menja — uvek proveri svezinu |
| CSS (sa hash-om) | max-age=31536000, immutable | Hash u imenu se menja kad se fajl promeni |
| JS (sa hash-om) | max-age=31536000, immutable | Isto kao CSS — ime se menja pri promeni |
| Slike | max-age=2592000 (30 dana) | Retko se menjaju, ali nemaju hash |
| Fontovi | max-age=31536000, immutable | Nikad se ne menjaju |
| API odgovori | no-store ili max-age=60 | Dinamicki podaci, cesto se menjaju |
| Osetljivi podaci | no-store, private | Nikad kesirati bankarske/medicinske podatke |
# Primer odgovora servera za CSS sa hash-om
HTTP/1.1 200 OK
Content-Type: text/css
Cache-Control: public, max-age=31536000, immutable
ETag: "a1b2c3d4"
# Primer za HTML (uvek revalidiraj)
HTTP/1.1 200 OK
Content-Type: text/html
Cache-Control: no-cache
ETag: "page-v42"
4. ETag — validacija kesa
ETag (Entity Tag) je jedinstven identifikator verzije resursa. Server ga generise (obicno hash sadrzaja) i browser ga koristi za revalidaciju.
Kako radi
- Prva poseta: server salje resurs +
ETag: "abc123" - Browser cuva resurs i ETag
- Druga poseta: browser salje
If-None-Match: "abc123" - Server poredi ETag-ove:
- — Ako se poklapa: 304 Not Modified (bez body-ja, ~100 bajtova)
- — Ako se ne poklapa: 200 OK sa novim sadrzajem i novim ETag-om
# Zahtev za revalidaciju
GET /style.css HTTP/1.1
If-None-Match: "a1b2c3d4"
# Odgovor: nije se promenilo — usteda bandwidth-a!
HTTP/1.1 304 Not Modified
ETag: "a1b2c3d4"
Cache-Control: public, max-age=3600
Weak vs Strong ETag
| Tip | Format | Znacenje |
|---|---|---|
| Strong | "abc123" | Bajt-za-bajt identican sadrzaj |
| Weak | W/"abc123" | Semanticki ekvivalentan (moze imati male razlike) |
5. Last-Modified / If-Modified-Since
Stariji mehanizam validacije (pre ETag-a). Server salje datum poslednje izmene, browser ga cuva i pita "da li se promenilo od tada?"
# Prva poseta
HTTP/1.1 200 OK
Last-Modified: Wed, 09 Apr 2026 12:00:00 GMT
# Druga poseta
GET /page.html HTTP/1.1
If-Modified-Since: Wed, 09 Apr 2026 12:00:00 GMT
# Odgovor: 304 ako se nije promenilo
6. Cache-busting strategije
Problem: ako kesirate CSS za 1 godinu, kako naterate browser da preuzme novu verziju kad promenite dizajn?
Hash u filename-u (preporuceno)
<!-- Stara verzija -->
<link rel="stylesheet" href="/css/style.a1b2c3.css">
<!-- Nova verzija — drugaciji hash = browser preuzima ponovo -->
<link rel="stylesheet" href="/css/style.d4e5f6.css">
Build alati (Webpack, Vite, Parcel) automatski dodaju hash u ime fajla. Kad se fajl promeni, hash se menja, browser preuzima novu verziju jer je to "novi" URL.
Query string (alternativa)
<link rel="stylesheet" href="/css/style.css?v=2.1">
<script src="/js/app.js?v=20260409"></script>
7. Browser vs CDN vs Server cache
| Nivo | Gde | Ko kontrolise | Brzina | Primer |
|---|---|---|---|---|
| Browser cache | Korisnikov uredjaj | Cache-Control header | ~1ms | Ponovna poseta sajtu |
| CDN cache | Edge server (blizu korisnika) | s-maxage, CDN pravila | ~20-50ms | Cloudflare, Vercel Edge |
| Server cache | Origin server | Redis, Memcached, aplikacija | ~5-20ms | Database query cache |
| Origin (bez kesa) | Server + baza | — | ~100-500ms | Svezi podaci iz baze |
Zahtev prolazi nivoe: Browser → CDN → Server → Origin. Ako bilo koji nivo ima validan kes, odgovor se vraca odatle bez dalje propagacije.
8. Service Worker cache
Service Worker je JavaScript koji radi u pozadini i moze presresti mrezne zahteve. Sa njim mozete napraviti offline-first sajt.
// sw.js — Service Worker sa cache-first strategijom
const CACHE_NAME = 'v1';
const ASSETS = ['/', '/index.html', '/css/style.css', '/js/app.js'];
// Instalacija: kesiraj kriticne resurse
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME).then(cache => cache.addAll(ASSETS))
);
});
// Zahtev: vrati iz kesa, fallback na mrezu
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(cached => {
return cached || fetch(event.request).then(response => {
// Kesiraj novi resurs za sledecu posetu
const clone = response.clone();
caches.open(CACHE_NAME).then(cache => cache.put(event.request, clone));
return response;
});
})
);
});
Cache strategije
| Strategija | Opis | Kada koristiti |
|---|---|---|
| Cache First | Kes → mreza (ako nema u kesu) | Staticni resursi (CSS, JS, slike) |
| Network First | Mreza → kes (ako mreza ne radi) | Dinamicki sadrzaj (API, HTML) |
| Stale While Revalidate | Vrati iz kesa odmah + azuriraj u pozadini | Sadrzaj gde je svezina pozeljna ali ne kriticna |
9. Cache invalidation
Phil Karlton je rekao: "There are only two hard things in Computer Science: cache invalidation and naming things."
- Hash u filename-u — najcistije resenje. Promena fajla = novi hash = novi URL = browser automatski preuzima.
- CDN purge — rucno ili API pozivom ocistite CDN kes. Cloudflare: purge by URL ili purge all.
- Verzioniranje API-ja —
/api/v2/usersumesto/api/userskad se format promeni. - stale-while-revalidate — korisnik dobija stari kes odmah, novi se ucitava u pozadini za sledecu posetu.
- Service Worker azuriranje — promena CACHE_NAME triggeruje reinstalaciju i kesiranje novih resursa.
10. Konfiguracija: Nginx, Apache, Vercel
Nginx
# /etc/nginx/conf.d/cache.conf
location ~* \.(css|js|woff2|png|jpg|webp|avif|svg)$ {
expires 1y;
add_header Cache-Control "public, max-age=31536000, immutable";
}
location ~* \.html$ {
add_header Cache-Control "no-cache";
}
location /api/ {
add_header Cache-Control "no-store";
}
Apache (.htaccess)
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType text/css "access plus 1 year"
ExpiresByType application/javascript "access plus 1 year"
ExpiresByType image/webp "access plus 1 year"
ExpiresByType text/html "access plus 0 seconds"
</IfModule>
<IfModule mod_headers.c>
<FilesMatch "\.(css|js|woff2|png|jpg|webp)$">
Header set Cache-Control "public, max-age=31536000, immutable"
</FilesMatch>
<FilesMatch "\.html$">
Header set Cache-Control "no-cache"
</FilesMatch>
</IfModule>
Vercel (vercel.json)
{
"headers": [
{
"source": "/assets/(.*)",
"headers": [
{"key": "Cache-Control", "value": "public, max-age=31536000, immutable"}
]
},
{
"source": "/(.*).html",
"headers": [
{"key": "Cache-Control", "value": "no-cache"}
]
}
]
}
11. Reference i resursi
- MDN — HTTP Caching
- web.dev — HTTP Cache
- RFC 7234 — HTTP Caching
- MDN — Cache-Control
- MDN — ETag
- web.dev — Service Worker vs HTTP Caching
- MDN — Service Worker API
HTTP Caching — Cache-Control, ETag, CDN Guide
How to make your site 5-10x faster for return visits with proper resource caching
Table of Contents
1. What is HTTP caching and why it matters
HTTP caching stores copies of resources closer to the user. On return visits, resources load from cache (~1ms) instead of the server (200-500ms).
2. Cache-Control header
| Directive | Meaning | Example |
|---|---|---|
max-age=N | Cache for N seconds | max-age=3600 = 1 hour |
s-maxage=N | CDN/proxy only cache | s-maxage=86400 = CDN 1 day |
public | Anyone can cache | Static images, CSS, JS |
private | Browser only (not CDN) | Personalized content |
no-cache | Cache BUT revalidate every time | HTML pages |
no-store | Don't cache at all | Sensitive data (banking) |
immutable | Resource NEVER changes | Files with hash in name |
no-cache does NOT mean "don't cache"! It means "cache but check with server every time". Use no-store to disable caching completely.3. Recommendations by resource type
| Resource | Cache-Control | Why |
|---|---|---|
| HTML | no-cache | Content changes — always check freshness |
| CSS/JS (hashed) | max-age=31536000, immutable | Hash changes when file changes |
| Images | max-age=2592000 (30 days) | Rarely change, no hash |
| Fonts | max-age=31536000, immutable | Never change |
| API | no-store or max-age=60 | Dynamic data |
4. ETag — cache validation
ETag is a unique version identifier. Browser sends If-None-Match: "etag" → server returns 304 (no body) if unchanged, or 200 with new content.
| Type | Format | Meaning |
|---|---|---|
| Strong | "abc123" | Byte-for-byte identical |
| Weak | W/"abc123" | Semantically equivalent |
5. Last-Modified / If-Modified-Since
Older validation mechanism using dates. ETag is more precise (content hash vs date). Most servers send both.
6. Cache-busting strategies
Hash in filename (recommended): style.a1b2c3.css → build tools auto-generate. File change = new hash = browser downloads.
Query string (alternative): style.css?v=2.1 — less reliable with some CDNs.
7. Browser vs CDN vs Server cache
| Level | Where | Speed | Example |
|---|---|---|---|
| Browser | User's device | ~1ms | Return visit |
| CDN | Edge server (near user) | ~20-50ms | Cloudflare, Vercel |
| Server | Origin server | ~5-20ms | Redis, Memcached |
| Origin | Server + database | ~100-500ms | Fresh data |
8. Service Worker cache
Service Worker intercepts requests and can serve from cache even offline.
| Strategy | Description | Use for |
|---|---|---|
| Cache First | Cache → network fallback | Static assets |
| Network First | Network → cache fallback | Dynamic content |
| Stale While Revalidate | Return stale + update in background | Non-critical freshness |
9. Cache invalidation
"There are only two hard things in CS: cache invalidation and naming things."
- Hash in filename — cleanest solution
- CDN purge — manual or API
- API versioning —
/api/v2/ - stale-while-revalidate — stale cache + background update
10. Configuration: Nginx, Apache, Vercel
Nginx: expires 1y + Cache-Control "public, max-age=31536000, immutable" for static assets.
Apache: mod_expires + mod_headers in .htaccess.
Vercel: vercel.json headers config per source pattern.
11. References and resources
- MDN — HTTP Caching
- web.dev — HTTP Cache
- RFC 7234 — HTTP Caching
- MDN — Cache-Control
- MDN — ETag
- web.dev — SW vs HTTP Caching