FoxWeb

софт для студентов
Искать здесь

Основы протокола HTTP: заголовки и управление кэшированием

Раздел: PHP, MySQL, веб-программирование Автор: foxweb
E-mail: спаму - нет! Www: http://foxweb.net.ru
Просмотров: 2194 Дата: 13.02.2007
Как правильно управлять кэшированием и что для этого нужно — об этом статья.

Для начала немного теории. Любой веб-программист, работая в протоколом HTTP, обязан знать, что поведение браузера, сервера, а также способы и правила обработки данных определяются HTTP-протоколом. Вот как выглядит простой запрос к сайту http://detiasfalta.ru :

Запрос от браузера к серверу:

GET / HTTP/1.1
Host: detiasfalta.ru
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.8.1.1) Gecko/20061204 Firefox/2.0.0.1
Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive


Ответ сервера браузеру:

HTTP/1.x 200 OK
Date: Mon, 12 Feb 2007 20:06:36 GMT
Server: Apache/1.3.37 (Win32) PHP/4.4.4
X-Powered-By: PHP/4.4.4
Expires: Tue, 28 Sep 2004 05:00:00 GMT
Cache-Control: no-cache, must-revalidate
Pragma: no-cache
Last-Modified: Mon, 12 Feb 2007 20:06:36GMT
Keep-Alive: timeout=15, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: text/html

<html> данные, данные, данные... </html>

Запрос и ответ (точнее их заголовки) в протоколе HTTP во многом схожи и интуитивно понятны, и это сильно облегчает взаимодействие различных платформ, программ и серверов по всему миру. В большинстве случаев ответ сервера генерируется автоматически. Например, при запросе обычного HTML-файла его содержимое считывается с диска, к нему "приклеиваются" HTTP-заголовок (вспомнили конверт с почтовым штампом?) и всё это отправляется браузеру. Но иногда необходимо генерировать собственные заголовки, тогда это можно сделать прямо из скрипта. Интерпретатор PHP тоже формирует заголовок автоматически перед началом отправки данных, но программист всегда может добавить и свои заголовки с помощью функции header(). Как это делается, будет рассказано ниже.

Самый простой способ защиты от кэширования браузером страниц вашего сайта — записать в HTML-коде в разделе <head></head> следующие мета-тэги:

<meta http-equiv="Cache-Control" content="no-cache, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="Mon, 12 Feb 2007 20:06:36GMT" />
<meta http-equiv="Last-Modified" content="Mon, 12 Feb 2007 20:06:36GMT" />

Это эквиваленты соответвующих HTTP-заголовков. Именно поэтому свойство мета-тэга так и называется — http-equiv.

  • Cache-Control — управление кэшированием.
  • Pragma — устаревший заголовок с произвольным содержанием (здесь no-cache тоже запрещает кэширование).
  • Expires — дата окончания действительности документа. Для запрета кэеширования указывается любая дата в прошлом, браузер в таком случае обновит документ полностью.
  • Last-Modified — дата последней модификации, можно указать любую дату в прошлом. Если это статический файл — берётся дата из файловой системы, если скрипт — то обычно указывается дата момента генерации документа.

Таким образом мы получаем четырёхкратную гарантию защиты от кэширования!

Есть и второй способ. В нём используются те же самые заголовки, но передаются они как часть HTTP-ответа. Прописывая их в HTML, то есть не совсем "по правилам" у нас нет никакой гарантии, что какой-нибудь хитроумный браузер распознает их и интерпретирует согласно протоколу HTTP. Итак наша цель — передать HTTP-заголовки согласно протоколу. Тут уж как ни крути, любой браузер обязан его соблюдать.

Чтобы передать свои HTTP-заголовки из PHP скрипта, нужно в сааамом-самом начале исходного текста, пока не отправился ни один байт данных, выполнить следующие команды:

header("Cache-Control: no-cache, must-revalidate");
header("Pragma: no-cache");
header("Expires: ".gmdate("D, d M Y H:i:s")."GMT");
header("Last-Modified: ".gmdate("D, d M Y H:i:s")."GMT");

Как мы видим, те же самые заголовки, но они включаются в HTTP-ответ сервера наравне с остальными. Единственное правило — заголовки всегда должны отправляться первыми. Как только начинается блок данных — заголовки не действуют. Эта "особенность" (даже не особенность, а то что само по себе и должно быть) часто сбивает с толку юных веб-программистов. При попытке послать заголовок командой header() в момент, когда уже начался блок данных, интерпретатор PHP выдаст ошибку: headers already sent.

<html>
<?php
/* такой код выдаст ошибку, поскольку уже началась отправка HTML-текста */
header('Location: http://www.example.com/');
?>

В своих проектах я обычно использую оба способа, но прямая передача заголовков надёжнее. Ну и напоследок: если вы хотите, чтобы файлы на вашем сайте наоборот сохранялись в кэше браузера (например, они оочень редко обновляются), необходимо убрать заголовки Cache-Control и Pragma, а в заголовках Expires и Last-Modified установить любую дату в будущем. Тогда браузер подумает, что документ достаточно "свежий" и не будет его обновлять. Запретом кэширования не стоит злоупотреблять, поскольку современные браузеры и сами прекрасно знают, что и когда кэшировать и это здорово экономит трафик и снижает нагрузку на сервер. Смысла в запрете кэширования сайта, обновляющегося раз в год, нет никакого, а вот для обновляющегося каждый день кэширование лучше отключить.

Комментарии

Максмко 22.11.2007 14:46:19 #
Первый нах
gendosparavoz 31.05.2008 02:23:56 #
Спасибо тебе мужик за пост.
antishock 26.08.2008 15:40:33 #
а что молчим про 304 и Etag !!!
смысл такой если браузер получал этот файл (html, php, jpg, gif ...)
обновля страницу он повторно отправляет дату Last-Modified получив её можно убедится что кеш браузера тру или фелс.
если тру выдать заголовок 304 и завершить скрипт exit;

$file=$_SERVER[SCRIPT_FILENAME];

$etag = md5_file($file);

header("Last-Modified: ".gmdate("D, d M Y H:i:s",filectime($file))." GMT");
header("Etag: ".$etag);

if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {

$if_modified_since = preg_replace('/;.*$/', '', $_SERVER['HTTP_IF_MODIFIED_SINCE']);

if(trim($_SERVER['HTTP_IF_NONE_MATCH'] == $etag) && $if_modified_since == gmdate("D, d M Y H:i:s",filectime($file)). " GMT") {
header('HTTP/1.0 304 Not Modified');
header('Cache-Control: max-age=604800, must-revalidate');
exit;
}

}else {
echo 'выводим еще раз кеш на неделю от '.date("r");
}



?>

п.с. 604800 — секунд неделя
antishock 26.08.2008 15:41:54 #
блин trim ne там закрыл :))
antishock 26.08.2008 15:54:29 #
$file=$_SERVER[SCRIPT_FILENAME];

$etag = md5_file($file);

header("Last-Modified: ".gmdate("D, d M Y H:i:s",filectime($file))." GMT");
header("Etag: ".$etag);

if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {

$if_modified_since = preg_replace('/;.*$/', '', $_SERVER['HTTP_IF_MODIFIED_SINCE']);

if(trim($_SERVER['HTTP_IF_NONE_MATCH']) == $etag && $if_modified_since == gmdate("D, d M Y H:i:s",filectime($file)). " GMT") {
header('HTTP/1.0 304 Not Modified');
header('Cache-Control: max-age=604800, must-revalidate');
exit;
}

}

echo 'Я тебя помню ты тут был в первы раз

'.date("r").'

';
?>

Оставить комментарий

Ваше имя

Ваш комментарий

Код   Защитный код. Если вы не видите здесь рисунок - обновите страницу.
Оценка   

Заметки по этой теме