HTTP cachování

Školení, která pořádám

PHP standardně neposílá žádné hlavičky, které by umožňovaly cachování (Cache-Control, Expires nebo Last-Modified). Pokud přesně víme, kdy vygenerovaná stránka vyprší (např. pokud vydáváme články jednou denně), je nejlepší posílat hlavičku Expires a o nic dalšího se není třeba starat – proxy cache nebo prohlížeče si o stránku řeknou až po vypršení tohoto data. Mnohem častější případ ale je, kdy nemůžeme přesně říct, kdy stránka vyprší (protože na ní máme např. diskuse), ale dokážeme alespoň určit, kdy byla naposledy změněna. V tom případě se vyplatí posílat hlavičku Last-Modified:

<?php
$last_modified = mysql_result(mysql_query("
    SELECT UNIX_TIMESTAMP(GREATEST(clanky.zmeneno, MAX(diskuse.vytvoreno)))
    FROM clanky
    LEFT JOIN diskuse ON clanky.id = diskuse.clanek
    WHERE clanky.id = " . intval($_GET["id"])
), 0);
if ($_SERVER["HTTP_IF_MODIFIED_SINCE"] && $last_modified <= strtotime($_SERVER["HTTP_IF_MODIFIED_SINCE"])) {
    header("HTTP/1.1 304 Not Modified");
    exit;
}
header("Last-Modified: " . gmdate("D, d M Y H:i:s", $last_modified) . " GMT");
?>

Poslání této hlavičky má dva příjemné následky:

  1. Pokud proxy cache vyhodnotí, že byl objekt změněn dávno a je tudíž nepravděpodobné, že by byla k dispozici jeho nová verze, tak nás s požadavkem vůbec neobtěžuje.
  2. Pokud má prohlížeč nebo proxy cache k dispozici starší verzi požadovaného dokumentu, pošle nám hlavičku If-Modified-Since, na základě které se můžeme rozhodnout, zda pošleme aktualizovanou stránku nebo zda prohlížeči hlavičkou 304 sdělíme, že jeho verze stále platí.

Pokud o expiraci nevíme nic, je možné posílat hlavičku Cache-Control s hodnotou max-age a tím zaručit, že stránka se bude aktualizovat např. nejpozději po 10 minutách. Touto hlavičkou lze ovlivnit i další nastavení cachování.

Proč vůbec cachovat?

Radost z kešování nám mírně můžou zkazit prohlížeče, které se chovají jinak, než by bylo ideální:

Ideální chování
Podle mě by se prohlížeč měl ptát serveru na novou verzi dokumentu, pokud (now - cached_time) > x * (now - Last-Modified), kde x může být řekněme kolem 0.1.
Firefox
Pomocí browser.cache.check_doc_frequency lze nastavit, podle jakých pravidel se mají nové verze dokumentů kontrolovat. Výchozí hodnota 3 je přijatelná v tom, že se rozhoduje na základě hlavičky Expires, jinak se zeptá serveru.
Internet Explorer
Chování lze ovlivnit pomocí dialogu Dočasné soubory Internetu | Nastavení, výchozí volba Automaticky zajišťuje podle nápovědy také přijatelné chování (předchozí relace nebo předchozí den, časem po ještě delší době).
Opera
Opera se chová ze všech prohlížečů nejhůř. Chování lze sice nastavit zvlášť pro stránky, obrázky a ostatní data, ale výchozí hodnota Každých 5 hodin je pro často se měnící dokumenty zcela nepoužitelná. Kromě toho prohlížeč nabízí už jen jiné časy a volby Vždy a Nikdy, automatické rozhodování ostatních prohlížečů chybí. Pro správnou funkci je použitelný buď krátký čas nebo volba Vždy, což zase může zbytečně zdržovat.

Podrobnější informace o cachování lze nalézt v překladu Kešovací návod od Yuhůa.

Přijďte si o tomto tématu popovídat na školení Výkonnost webových aplikací (23.3.2017, Praha).

Jakub Vrána, Seznámení s oblastí, 25.1.2006, diskuse: 10 (nové: 0)

Diskuse

Lukáš:

HTTP NEcachování

Jak přinutit proxy cache a prohlížeče aby nikdy nic necachovali? Stačí hlavičky:

Last-Modified: Sat, 01 Jan 2000 00:00:00 GMT
Cache-Control: no-cache, must-revalidate
Expires: Sat, 01 Jan 2000 00:00:00 GMT

Potřebuji to do jednoho B2B projektu, který běží na veřejném serveru a občas narážím na problém, že některé podniky mají špatně nastavené firemní proxy servery a nebo i uživatelé počítače a zobrazuje se jim něco, co už neplatí.

Leo:

"občas narážím na problém, že některé podniky mají špatně nastavené firemní proxy servery a nebo i uživatelé počítače a zobrazuje se jim něco, co už neplatí."

S tim obecne nic nenedalete - posilanim http hlavicek nemuzete prerazit nastaveni (spatne nastaveni) klienta. Ale jinak staci

Expires: v minulosti
Cache-control: no-cache,no-store,max-age=0,must-revalidate;
Pragma: no-cache;

S Last-modified se pak uz nemusite obtezovat. Leo

Libor Tvrdík:

Máte pravdu. Jediné řešení jak obejít toto nastavení je měnit URL. Tento problém jsem řeši u mobilních telefonů (WAP), kde kešování dosahovalo neskutečných rozměrů.

Do URL jsem vkládal náhodný parametr s náhodnou hodnotou. Stránka se tvářila pokaždé jinak, jen bylo potřeba upravit všechny formuláře.

Např: http://php.vrana.cz/http-cachovani.php?TMDl4x5=94kP3Frb

Libor Tvrdík:

http://... není URL? Viz věta pod příspěvkem ...ale PHP kód uzavřený do <?php ?> bude zvýrazněn a URL budou převedeny na odkazy...

ikona Jakub Vrána OpenID:

závorky patří takhle: (PHP kód uzavřený do <?php ?> bude zvýrazněn) a (URL budou převedeny na odkazy). Prohodil jsem pořadí klauzulí, ať to je jednoznačné.

Andrew:

Také jsem tenhle trik používal u WAPu (inspirován panem Koskem), ale normálně mi to přijde hloupé - od čeho jsou hlavičky.

Libor Tvrdík:

Aby je proxy ignorovaly? ;) Uznávám ale že je to přinejmenším nepohodlné.

mic362:

Mam takový problem, server mi nevraci tuto hodotu - $_SERVER["HTTP_IF_MODIFIED_SINCE"];

Je potřeba nějaké spec. nastavení?

kuba:

pekne. problem cachovani jsem yrovna resil, tenhle priklad urcite vyuziju. diky moc

David:

Ahoj, zkouším si vaše příklady a zatím mi vše funguje. Nyní jsem narazil na problém v cachování. Používám webhosting: http://www.savana.cz/ a myslím si, že tam mají nějaké omezení pro cachování, nejsem ale odborník, je to můj názor. K=od mi vždy selže. Lze si ověřit, za tam je nějaké omezení, nebo je chyba u mne? Děkuji

Vložit komentář

Používejte diakritiku. Vstup se chápe jako čistý text, ale URL budou převedeny na odkazy a PHP kód uzavřený do <?php ?> bude zvýrazněn. Pokud máte dotaz, který nesouvisí s článkem, zkuste raději diskusi o PHP, zde se odpovědi pravděpodobně nedočkáte.

Jméno: URL:

avatar © 2005-2017 Jakub Vrána. Publikované texty můžete přetiskovat pouze se svolením autora. Ukázky kódu smíte používat s uvedením autora a URL tohoto webu bez dalších omezení Creative Commons. Můžeme si tykat. Skripty předpokládají nastavení: magic_quotes_gpc=Off, magic_quotes_runtime=Off, error_reporting=E_ALL & ~E_NOTICE a očekávají předchozí zavolání mysql_set_charset. Skripty by měly být funkční v PHP >= 4.3 a PHP >= 5.0.