Last-Modified — je HTTP hlavička odpovědi, která udává datum a čas poslední změny zdroje na serveru a umožňuje klientovi provádět podmíněné požadavky prostřednictvím If-Modified-Since. Pokud se zdroj od uvedeného data nezměnil, server vrátí 304 Not Modified bez odeslání těla odpovědi, což významně šetří šířku pásma. Podle RFC 7232 (IETF, 2014) zkracují podmíněné požadavky s Last-Modified čas načítání stránek o 30–60 % při opakovaných návštěvách. Hlavička je automaticky podporována většinou HTTP serverů a proxy.
Hlavní body
Last-Modified — je HTTP hlavička patřící do skupiny hlaviček podmíněných požadavků (conditional requests). Server ji přidá do odpovědi na GET nebo HEAD a uvádí datum a čas poslední změny požadovaného zdroje ve formátu HTTP-date: Last-Modified: Wed, 02 Jul 2025 14:30:00 GMT. Klient (prohlížeč, mobilní aplikace, proxy) toto datum uloží spolu s zdrojem v mezipaměti. Při opakovaném požadavku klient odešle hlavičku If-Modified-Since se stejným datem a server je porovná s aktuálním časem změny zdroje.
Protokol podmíněných požadavků s Last-Modified je definován v RFC 7232 a je podporován všemi moderními HTTP servery. Formát data je přísně regulován — pouze GMT (Greenwich Mean Time) bez uvedení časového pásma. Server může vrátit datum ve třech možných formátech: RFC 1123 (standardní), RFC 850 (zastaralý) nebo ANSI C asctime. V praxi téměř všechny servery používají formát RFC 1123 s pevnou délkou 29 znaků.
Last-Modified patří do kategorie validačních mechanismů mezipaměti: neříká klientovi, zda lze odpověď ukládat do mezipaměti, ale poskytuje nástroj pro kontrolu aktuálnosti již uloženého zdroje. Politika ukládání do mezipaměti se nastavuje samostatně prostřednictvím hlavičky Cache-Control. Podle výzkumu Akamai (2025) správná konfigurace Last-Modified společně s Cache-Control snižuje zátěž origin serverů až o 70 % pro statický obsah.
Hlavička Last-Modified byla definována již v HTTP/1.0 (RFC 1945, 1996) a byla jedním z prvních mechanismů pro správu mezipaměti na webu. Před příchodem ETag v HTTP/1.1 to byl jediný způsob, jak provádět podmíněné požadavky. Navzdory svému stáří zůstává hlavička aktuální díky své jednoduchosti — server nemusí počítat hash obsahu, stačí přečíst časové razítko souboru ze souborového systému nebo pole updated_at z databáze.
Celý cyklus zahrnuje tři fáze. Při prvním požadavku server vrátí zdroj s hlavičkou Last-Modified a HTTP stavem 200 OK. Klient uloží odpověď do mezipaměti spolu s datem. Při opakovaném požadavku klient odešle hlavičku If-Modified-Since s uloženým datem. Server toto datum porovná s aktuálním časem změny zdroje. Pokud se zdroj nezměnil — vrátí se 304 Not Modified s prázdným tělem. Pokud se změnil — 200 OK s novými daty a novým Last-Modified.
// První požadavek — server vrátí zdroj s datem
HTTP/1.1 200 OK
Content-Type: application/json
Last-Modified: Wed, 02 Jul 2025 14:30:00 GMT
[{"id": 1, "name": "Alice"}]
// Opakovaný požadavek — klient odešle uložené datum
GET /api/users HTTP/1.1
Host: example.com
If-Modified-Since: Wed, 02 Jul 2025 14:30:00 GMT
// Odpověď — data se nezměnila
HTTP/1.1 304 Not Modified
Pro mobilní aplikace je Last-Modified zvláště užitečný při synchronizaci dat. Aplikace si uloží datum poslední úspěšné aktualizace a odešle jej serveru v If-Modified-Since. Pokud je více dat nebo se změnila — server vrátí kompletní sadu. Pokud ne — 304 a aplikace použije lokální kopii. OkHttp a URLSession podporují tento mechanismus automaticky prostřednictvím vestavěných systémů mezipaměti.
Pro statické soubory Nginx a Apache berou datum z atributů souborového systému — mtime (čas změny). Pro dynamický obsah musí kód serveru explicitně nastavit Last-Modified na základě business logiky: pole updated_at z databáze, datum posledního commitu v Gitu, časové razítko sestavení artefaktu. Pokud Last-Modified není explicitně nastaven, server nemusí hlavičku vrátit vůbec a klient nebude moci provádět podmíněné požadavky podle data.
Last-Modified a ETag plní podobný úkol — umožňují klientovi zkontrolovat aktuálnost mezipaměti — ale mají zásadní rozdíly. Last-Modified používá časové razítko, ETag — jedinečný identifikátor verze. Každý přístup má scénáře, kde je účinnější, a doporučení HTTP specifikace je používat obě hlavičky společně.
| Kritérium | Last-Modified | ETag |
|---|---|---|
| Podstata | Datum poslední změny | Jedinečný identifikátor verze |
| Přesnost | Až na sekundu | Až na bit (hash) |
| Složitost implementace | Nízká — automaticky ze souborového systému | Střední — vyžaduje výpočet hashe |
| Clustrované servery | Problém: mtime se může na uzlech lišit | Stabilní při stejných datech na uzlech |
| Podpora rozsahů | Neovlivňuje Range requests | Vyžaduje silný ETag pro rozsahy |
| Doporučení | Pro statický obsah a jednoduchá API | Pro API, kde je důležitá přesná kontrola |
Hlavní výhodou Last-Modified je jednoduchost. Server nemusí počítat hash obsahu, což šetří prostředky CPU při každém požadavku. Pro projekty s vysokou zátěží, které doručují statické soubory nebo data s jasnými časovými razítky, zůstává Last-Modified optimální volbou. ETag naopak poskytuje absolutní přesnost — změna jednoho písmene v JSON odpovědi změní ETag, ale nemusí změnit datum (pokud byl soubor přepsán stejnou verzí).
Specifikace doporučuje vrátit obě hlavičky současně. Server zahrne jak Last-Modified, tak ETag do odpovědi 200 OK. Klient odešle obě podmíněné hlavičky — If-Modified-Since a If-None-Match. Server nejprve zkontroluje ETag (má prioritu), poté Last-Modified. Pokud alespoň jedna signalizuje změnu — vrátí se úplná odpověď. To poskytuje maximální flexibilitu: ETag zajišťuje přesnost, Last-Modified — záložní kontrolu pro klienty, kteří ETag nepodporují.
Konfigurace Last-Modified závisí na typu serveru. Pro Nginx a Apache je Last-Modified pro statické soubory nastaven automaticky na základě mtime. Pro dynamické aplikace je třeba hlavičku nastavit v kódu serveru. Podívejme se na konfiguraci na oblíbených platformách.
// Express.js — nastavení Last-Modified
app.get("/api/users", async (req, res) => {
const updatedAt = await getLastUpdate()
const ifModifiedSince = req.get("If-Modified-Since")
// Kontrola If-Modified-Since
if (ifModifiedSince && new Date(ifModifiedSince)
>= updatedAt) {
return res.status(304).end()
}
const users = await getUsers()
res.set("Last-Modified", updatedAt.toUTCString())
res.json(users)
})
V příkladu na Express.js server získá datum poslední aktualizace dat z databáze, zkontroluje If-Modified-Since od klienta a pokud je mezipaměť aktuální, vrátí 304. Pokud se data změnila — nastaví nový Last-Modified a vrátí úplnou odpověď. toUTCString() převede datum do požadovaného HTTP formátu. V produkčním prostředí se vyplatí přidat ukládání updatedAt do Redis, aby se předešlo dotazu do databáze při každém přístupu.
Nginx automaticky nastavuje Last-Modified pro statické soubory na základě času poslední změny souboru. Zakázání nebo změna chování je možná pomocí direktivy etag (zakázání ETag) nebo prostřednictvím modulu ngx_http_headers_module. Pro proxy požadavky na backend je Last-Modified předáván z upstream odpovědi beze změny. Důležité: pokud backend nevrací Last-Modified, Nginx jej automaticky nepřidá pro dynamické odpovědi.
Last-Modified má několik známých omezení. Hlavním je přesnost na sekundu. Pokud se zdroj změnil dvakrát během jedné sekundy, klient může novou verzi zmeškat. V praxi je to vzácný scénář, ale pro vysokofrekvenční aktualizace (kanály kotací, chaty) se doporučuje ETag. Druhým omezením je problém clusterizace: na různých serverech může mít soubor různý mtime kvůli kopírování nebo nasazení, což způsobí nekonzistentnost Last-Modified.
Třetím omezením — zpracování If-Modified-Since s přesností na sekundu může vést k nadbytečným požadavkům při častém dotazování serveru. Pokud klient odesílá If-Modified-Since každých 500 ms, server pokaždé vrátí 200 OK, protože se datum nezměnilo, ale zdroj byl již ve skutečnosti aktualizován. Řešením je kombinace s ETag: ETag zachytí změnu během sekundy, zatímco Last-Modified zůstane jako záloha.
Čtvrtým problémem — Last-Modified nerozlišuje různé verze stejného zdroje se stejným datem. Pokud byl soubor obnoven ze zálohy a jeho mtime se shoduje s originálem, klient nepostřehne, že se obsah změnil. ETag tento problém řeší: hash obsahu se zaručeně změní při každé změně dat, bez ohledu na časové razítko. Pro kritická data vždy používejte obě hlavičky.
Často kladené dotazy
Pouze GMT (Greenwich Mean Time) ve formátu RFC 1123: den v týdnu, datum, měsíc, rok, hodiny:minuty:sekundy. Příklad: Wed, 02 Jul 2025 14:30:00 GMT. Časové pásmo je vždy GMT, jiné formáty nejsou povoleny.
Technicky ano, ale porušuje to RFC 7232. Pokud server vrátí datum v budoucnosti, klienti nebudou zdroj aktualizovat do příchodu tohoto data. Taková konfigurace je považována za chybu — datum musí být v minulosti nebo současnosti.
Ne, podmíněné požadavky If-Modified-Since fungují pouze s GET a HEAD. POST požadavky se neukládají do mezipaměti a nepoužívají validaci podle data. Pro kontrolu aktuálnosti dat při POST použijte ETag nebo vlastní mechanismy.
Cache-Control určuje politiku ukládání do mezipaměti (maximální dobu uložení, kdo může ukládat do mezipaměti), zatímco Last-Modified je validační mechanismus pro prošlou mezipaměť. Po vypršení max-age klient odešle If-Modified-Since pro kontrolu aktuálnosti.
Zkontrolujte, zda server opravdu nastavuje hlavičku z aktuálního zdroje — databáze, souborového systému nebo API. Pro dynamické odpovědi se ujistěte, že explicitně voláte res.setHeader(„Last-Modified”, ...) v kódu 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é