В старые добрые времена, когда создание веб-сайтов было таким же простым, как создание нескольких HTML-страниц, доставка веб-страницы в браузер была простой задачей — заставить веб-сервер получить файл. Посетители сайта увидят его маленькие текстовые страницы почти сразу, если они не будут использовать особенно медленные модемы. После загрузки страницы браузер кэширует ее где-то на локальном компьютере, чтобы в случае повторного запроса страницы после быстрой проверки на сервере, чтобы убедиться, что страница не была обновлена, браузер может отображать локально кэшированная версия. Страницы подавались максимально быстро и качественно, и все были счастливы.
Затем появились динамические веб-страницы и испортили вечеринку, представив две проблемы:
- Когда сервер получает запрос на динамическую веб-страницу, некоторая промежуточная обработка должна быть завершена, например, выполнение сценариев механизмом PHP. Эта обработка вводит задержку, прежде чем веб-сервер начинает доставлять выходные данные в браузер. Это может не быть значительной задержкой, когда речь идет о простых сценариях PHP, но для более сложных приложений движку PHP может потребоваться много работы, прежде чем страница будет наконец готова к доставке. Эта дополнительная работа приводит к заметной временной задержке между запросами пользователя и фактическим отображением страниц в браузере.
- Типичный веб-сервер, такой как Apache, использует время изменения файла, чтобы информировать веб-браузер о возрасте запрашиваемой страницы, позволяя браузеру предпринять соответствующие действия кэширования. С динамическими веб-страницами реальный PHP-скрипт может меняться только изредка; между тем, отображаемый контент, который часто выбирается из базы данных, будет часто меняться. Веб-сервер не может распознать обновления базы данных, поэтому он не отправляет дату последнего изменения. Если клиент (то есть браузер пользователя) не имеет указаний на то, как долго данные будут оставаться действительными, это займет предположение. Это проблематично, если браузер решает использовать локально кэшированную версию страницы, которая сейчас устарела, или если браузер решает запросить с сервера свежую копию страницы, которая фактически не имеет нового содержимого, делая запрос лишний. Веб-сервер всегда будет отвечать только что созданной версией страницы, независимо от того, изменились ли данные в базе данных.
Чтобы исключить возможность просмотра посетителем веб-сайта устаревшего контента, большинство веб-разработчиков используют метатег или заголовки HTTP, чтобы запретить браузеру использовать кэшированную версию страницы. Однако это сводит на нет естественную способность веб-браузера кешировать веб-страницы и влечет за собой некоторые серьезные недостатки. Например, содержимое, предоставляемое динамической страницей, может меняться только один раз в день, поэтому, безусловно, можно получить выгоду от кэширования страницы браузером, даже если только в течение 24 часов.
Если вы работаете с небольшим PHP-приложением, обычно можно решить обе проблемы. Но по мере того, как ваш сайт усложняется и привлекает больше трафика, у вас начнутся проблемы с производительностью. Однако обе эти проблемы могут быть решены: первая — с кэшированием на стороне сервера; во-вторых, взяв на себя управление кэшированием на стороне клиента из вашего приложения. Точный подход, который вы используете для решения этих проблем, будет зависеть от вашего приложения, но в этой главе мы рассмотрим как PHP, так и ряд библиотек классов из PEAR как возможные панацеи для вашей веб-страницы.
Обратите внимание, что в этой главе мы рассмотрим только те решения, которые могут быть реализованы в PHP. Для более общего введения окончательное обсуждение веб-кэширования представлено в руководстве Марка Ноттингема .
Кроме того, решения в этой главе не следует путать с некоторыми решениями для кэширования сценариев, которые работают на основе оптимизации и кэширования скомпилированных сценариев PHP, такими как Zend Accelerator и ionCube PHP Accelerator .
Эта глава взята из Антологии PHP: 101 Essential Tips, Tricks & Hacks, 2nd Edition .
Как запретить веб-браузерам кэшировать страницу?
Если своевременная информация имеет решающее значение для вашего веб-сайта, и вы хотите, чтобы устаревший контент никогда не был виден, вам необходимо понять, как предотвратить кэширование страниц веб-браузерами и прокси-серверами.
Решения
Существует два возможных подхода к решению этой проблемы: использование метатегов HTML и использование заголовков HTTP.
Использование метатегов HTML
Основным подходом к предотвращению кэширования страниц является тот, который использует метатеги HTML:
<meta http-equiv="expires" content="Mon, 26 Jul 1997 05:00:00 GMT"/> <meta http-equiv="pragma" content="no-cache" />
Вставка даты, уже переданной в метатег Expires
сообщает браузеру, что кэшированная копия страницы всегда устарела. При обнаружении этого тега браузер обычно не кэширует страницу. Хотя метатег Pragma: no-cache
не гарантирован, это довольно хорошо поддерживаемое соглашение, которому следует большинство веб-браузеров. Однако две проблемы, связанные с этим подходом, которые мы обсудим ниже, могут побудить вас взглянуть на альтернативное решение.
Использование заголовков HTTP
Лучшим подходом является использование самого протокола HTTP с помощью функции заголовка PHP для получения эквивалента двух метатегов HTML выше:
<?php header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); header('Pragma: no-cache'); ?>
Мы можем пойти еще дальше, используя заголовок Cache-Control
который поддерживается браузерами с поддержкой HTTP 1.1:
<?php header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); header('Cache-Control: no-store, no-cache, must-revalidate'); header('Cache-Control: post-check=0, pre-check=0', FALSE); header('Pragma: no-cache'); ?>
Чтобы получить точное описание заголовков HTTP 1.1 Cache-Control, взгляните на HTTP 1.1 RFC W3C . Другим хорошим источником информации о заголовках HTTP, который можно легко применить к PHP, является документация mod_perl о выдаче правильных заголовков .
обсуждение
Использование метатега Expires
звучит как хороший подход, но с ним связаны две проблемы:
- Сначала браузер должен загрузить страницу, чтобы прочитать метатеги. Если тег не присутствовал, когда браузер впервые запросил страницу, браузер останется в блаженном неведении и сохранит свою кешированную копию оригинала.
- Прокси-серверы, которые кэшируют веб-страницы, такие как общие для интернет-провайдеров, обычно не читают сами документы HTML. Веб-браузер может знать, что он не должен кэшировать страницу, но прокси-сервер между браузером и веб-сервером, вероятно, этого не делает — он продолжит доставлять ту же устаревшую страницу клиенту.
С другой стороны, использование протокола HTTP для предотвращения кэширования страниц по существу гарантирует, что ни один веб-браузер или промежуточный прокси-сервер не будут кэшировать страницу, поэтому посетители всегда будут получать новейший контент. Фактически, первый заголовок должен выполнить это самостоятельно; это лучший способ убедиться, что страница не кэшируется. Заголовки Cache-Control
и Pragma
добавлены для некоторой степени страхования. Хотя они не работают во всех браузерах или прокси Cache-Control
Pragma
заголовки Cache-Control
и Pragma
будут обнаруживать некоторые случаи, когда заголовок Expires не работает должным образом — например, если дата клиентского компьютера установлена неправильно.
Конечно, запрет на кеширование полностью ставит проблемы, которые мы обсуждали в начале этой главы: он сводит на нет естественную способность веб-браузера кешировать страницы и может создавать ненужные накладные расходы, поскольку всегда запрашиваются новые версии страниц, даже если эти страницы могут не обновлялись с момента последнего запроса браузера. Мы посмотрим на решение этих проблем в мгновение ока.
Как я могу контролировать кэширование на стороне клиента?
Мы рассмотрели задачу отключения кэширования на стороне клиента в разделе «Как предотвратить кэширование страницы в веб-браузерах?», Но отключение кэша редко является единственным (или лучшим) вариантом.
Здесь мы рассмотрим механизм, который позволяет нам использовать преимущества кэшей на стороне клиента таким способом, которым можно управлять из скрипта PHP.
Apache требуется!
Этот подход будет работать только в том случае, если вы используете PHP в качестве модуля веб-сервера Apache, поскольку он требует использования функции getallheaders — которая работает только с Apache — для извлечения заголовков HTTP, отправленных веб-браузером.
Решения
В управлении кэшированием на стороне клиента у вас есть две альтернативы. Вы можете установить дату истечения срока действия страницы или ответить на заголовки запросов браузера. Посмотрим, как выполняется каждая из этих тактик.
Установка заголовка срока действия страницы
Заголовок, который проще всего реализовать, это заголовок Expires
— мы используем его для установки даты истечения срока действия страницы, и до этого времени веб-браузерам разрешается использовать кэшированную версию страницы. Вот пример этого заголовка на работе:
expires.php (выдержка)
<?php function setExpires($expires) { header( 'Expires: '.gmdate('D, d MYH:i:s', time()+$expires).'GMT'); } setExpires(10); echo ( 'This page will self destruct in 10 seconds<br />' ); echo ( 'The GMT is now '.gmdate('H:i:s').'<br />' ); echo ( '<a href="'.$_SERVER['PHP_SELF'].'">View Again</a><br />' ); ?>
В этом примере мы создали пользовательскую функцию setExpires
которая устанавливает заголовок HTTP Expires
на точку в будущем, определенную в секундах. Выходные данные приведенного выше примера показывают текущее время по Гринвичу и предоставляют ссылку, которая позволяет нам снова просматривать страницу. Если мы перейдем по этой ссылке, мы заметим время обновления только один раз каждые десять секунд. При желании вы также можете поэкспериментировать, используя кнопку «Обновить» вашего браузера, чтобы сообщить браузеру обновить кэш, и посмотреть, что происходит с отображаемой датой.