HTTP-кеширование
Производительность веб-сайтов и приложений можно значительно повысить за счёт повторного использования ранее полученных ресурсов. Веб-кеши сокращают задержку и снижают сетевой трафик, уменьшая тем самым время, необходимое для отображения ресурсов. Используя HTTP-кеширование, сайты становятся более отзывчивыми.
Различные виды кеширования
Техника кеширования заключается в сохранении копии полученного ресурса для возврата этой копии в ответ на дальнейшие запросы. Запрос на ресурс, уже имеющийся в веб-кеше, перехватывается, и вместо обращения к исходному серверу выполняется загрузка копии из кеша. Таким образом снижается нагрузка на сервер, которому не приходится самому обслуживать всех клиентов, и повышается производительность — кеш ближе к клиенту и ресурс передаётся быстрее. Кеширование является основным источником повышения производительности веб-сайтов. Однако, кеш надо правильно сконфигурировать: ресурсы редко остаются неизменными, так что копию требуется хранить только до того момента, как ресурс изменился, но не дольше.
Существует несколько видов кешей, которые можно разделить на две основные категории: приватные кеши и кеши совместного использования. В кешах совместного использования (shared cache) хранятся копии, которые могут направляться разным пользователям. Приватный кеш (private cache) предназначен для отдельного пользователя. Здесь будет говориться в основном о кешах браузеров и прокси, но существуют также кеши шлюзов, CDN, реверсные прокси кеши и балансировщики нагрузки, разворачиваемые на серверах для повышения надёжности, производительности и масштабируемости веб-сайтов и веб-приложений.
Приватный (private) кеш браузера
Приватный кеш предназначен для отдельного пользователя. Вы, возможно, уже видели параметры кеширования в настройках своего браузера. Кеш браузера содержит все документы, загруженные пользователем по HTTP. Он используется для доступа к ранее загруженным страницам при навигации назад/вперёд, позволяет сохранять страницы, или просматривать их код, не обращаясь лишний раз к серверу. Кроме того, кеш полезен при отключении от сети.
Общий (shared) прокси-кеш
Кеш совместного использования — это кеш, который сохраняет ответы, чтобы их потом могли использовать разные пользователи. Например, в локальной сети вашего провайдера или компании, может быть установлен прокси, обслуживающий множество пользователей, чтобы можно было повторно использовать популярные ресурсы, сокращая тем самым сетевой трафик и время ожидания.
Цели кеширования
Кеширование в HTTP не является обязательным, однако в большинстве случаев бывает полезно повторно использовать ранее сохранённые ресурсы. Тем не менее, стандартные кеши HTTP обычно способны кешировать только ответы на запросы методом GET , а другие отклоняют.
Первичный ключ состоит из метода запроса и запрашиваемого URI (зачастую используется только URI, поскольку целью кеширования являются только GET-запросы). Вот примеры того, что обычно записывается в кеш:
- Успешно загруженные ресурсы: ответ 200 OK на запрос методом GET HTML-документов, изображений или файлов.
- Постоянные перенаправления: ответ 301 Moved Permanently («перемещено навсегда»).
- Сообщения об ошибках: ответ 404 Not Found («не найдено»).
- Неполные результаты: ответ 206 Partial Content («частичное содержимое»).
- Ответы на запросы отличные от GET , если есть что-либо, подходящее для использования в качестве ключа кеша.
Запись в кеше может также состоять из множества ответов, различаемых по вторичному ключу, если при формировании ответа производится согласование данных. Подробнее об этом рассказано ниже, в разделе, посвящённом заголовку Vary .
Управление кешированием
Заголовок Cache-control
Поле Cache-Control общего заголовка HTTP/1.1 используется для задания инструкций по механизму кеширования как в запросах, так и в ответах. Применяется для задания политик кеширования.
Полное отсутствие кеширования
В кеше не должно сохраняться ничего — ни по запросам клиента, ни по ответам сервера. Запрос всегда отправляется на сервер, ответ всегда загружается полностью.
Кешировать, но проверять актуальность
Перед тем, как выдать копию, кеш запрашивает исходный сервер на предмет актуальности ресурса.
Приватные (private) и общие (public) кеши
Директива «public» указывает, что ответ можно сохранять в любом кеше. Это бывает полезно, если возникает потребность сохранить страницы с HTTP-аутентификацией, или такими кодами ответа, которые обычно не кешируются. Директива же «private» указывает, что ответ предназначен отдельному пользователю и не должен храниться в кеше совместного использования. В этом случае ответ может сохраняться приватным кешем браузера.
Срок действия (Expiration)
Самой важной здесь является директива «max-age=<seconds>» — максимальное время, в течение которого ресурс считается «свежим». В отличие от директивы Expires , она привязана к моменту запроса. К неизменяющимся файлам приложения обычно можно применять «агрессивное» кеширование. Примером таких статических файлов могут быть изображения, файлы стилей (CSS) или скриптов (JavaScript).
Подробнее об этом рассказывается в разделе Свежесть ресурса.
Проверка актуальности
При использовании директивы «must-revalidate» кеш обязан проверять статус ресурсов с истёкшим сроком действия. Те копии, что утратили актуальность, использоваться не должны. Подробнее об этом рассказано ниже, в разделе Валидация кеша.
Заголовок Pragma
Pragma является заголовком HTTP/1.0. Он не описан для HTTP-ответов и, таким образом, не может служить надёжной заменой общему заголовку Cache-Control протокола HTTP/1.1, хотя его поведение аналогично «Cache-Control: no-cache» когда поле заголовка Cache-Control опущено в запросе. Использовать его следует только для совместимости с клиентами HTTP/1.0.
Свежесть сохранённой копии
Однажды попав в кеш, ресурс, теоретически, может храниться там вечно. Однако, поскольку объем хранилища конечен, записи периодически приходится оттуда удалять. Этот процесс называют вытеснением данных из кеша (cache eviction). Кроме того, ресурсы могут изменяться на сервере, поэтому кеш требуется обновлять. Поскольку HTTP является клиент-серверным протоколом, сервера не могут сами обращаться к кешам и клиентам при изменении ресурса; им необходимо договориться о сроке действия сохранённой копии. До его истечения ресурс считается свежим (fresh), после — устаревшим (stale). Алгоритмы вытеснения отдают предпочтение «свежим» ресурсам. Тем не менее, копия ресурса не удаляется из кеша сразу же по истечении её срока действия; при получении запроса на устаревший ресурс кеш передаёт его дальше с заголовком If-None-Match (en-US) на случай, если копия все ещё актуальна. Если это так, сервер возвращает заголовок 304 Not Modified («не изменялось»), а тело ресурса не посылает, экономя тем самым трафик.
Вот пример того, как протекает этот процесс при использовании совместного кеша прокси:

Срок действия (freshnessLifetime) вычисляется на основании нескольких заголовков. Если задан заголовок «Cache-control: max-age=N», то срок действия равен N. Если его нет, а это бывает очень часто, проверяется заголовок Expires , и, если он есть, то срок действия берётся равным значению заголовка Expires минус значение заголовка Date. Наконец, если нет ни того ни другого, смотрят заголовок Last-Modified. Если он есть, то срок действия равен значению заголовка Date минус значение заголовка Last-modified разделить на 10. Время устаревания (expirationTime) вычисляется следующим образом:
где responseTime — это время получения ответа по часам браузера, а currentAge — текущий возраст кеша.
Обновление статических ресурсов (Revved resources)
Чем больше ресурсов может быть взято из кеша, тем быстрее сайт реагирует на запросы и тем выше его производительность. Из этих соображений их «срок годности» имеет смысл делать как можно большим. Однако, возникает проблема с ресурсами, которые обновляются редко и нерегулярно. Как раз их кеширование даёт больше всего выгоды, но сильно затрудняет обновление. Такие ресурсы можно найти на любой веб-странице: файлы скриптов (JavaScript) и стилей (CSS) изменяются редко, но уж если это произошло, обновление надо произвести как можно быстрее.
Веб-разработчики разработали метод, который Стив Сандерс (Steve Sounders) назвал revving[1], что можно перевести как «оборачиваемость». Для редко обновляемых файлов используют особый способ именования: в их URL, обычно в имя файла, добавляют номер релиза или версии. Таким образом, каждая новая версия считается отдельным ресурсом, срок устаревания которого отодвинут далеко в будущее, как правило, на год, или больше. Недостатком этого метода является то, что для получения новых версий ресурса приходится обновлять все ссылки на него — это некоторое усложнение, справиться с которым разработчику помогает цепочка инструментов. Обновление статических ресурсов влечёт за собой обновление и часто изменяемых ресурсов. Когда считываются первые, считываются и новые версии вторых.
Этот метод имеет дополнительное достоинство: одновременное обновление двух кешированных ресурсов не приводит к ситуации, при которой устаревшая версия одного ресурса используется вместе с новой версией другого. Это очень важно для сайтов с взаимосвязанными файлами стилей CSS или JS-скриптов — связь может возникнуть, например, из-за ссылок на одни и те же элементы HTML-страницы.

Номер версии, добавляемый к статическому ресурсу, не обязательно записывать в виде стандартного номера версии наподобие 1.1.3, или другого возрастающего числового значения. Это может быть что угодно, позволяющее избежать совпадений — например, дата.
Валидация кеша
Валидация кеша запускается при нажатии пользователем кнопки перезагрузки. Кроме того, она может выполняться в ходе обычного просмотра страниц, если кешированный ответ включает заголовок «Cache-control: must-revalidate». Другим фактором являются настройки кеширования браузера — можно потребовать принудительной валидации при каждой загрузке документа.
При истечении срока годности документа он либо проходит валидацию, либо повторно доставляется с сервера. Валидация может выполняться только если на сервере реализован сильный валидатор или слабый валидатор.
Заголовки ETag
Заголовок ответа ETag является непрозрачным для клиентского приложения (агента) значением, которое можно использовать в качестве сильного валидатора. Суть в том, что клиент, например, браузер, не знает, что представляет эта строка и не может предсказать, каким будет её значение. Если в ответе присутствует заголовок ETag , клиент может транслировать его значение через заголовок If-None-Match (en-US) будущих запросов для валидации кешированного ресурса.
Заголовок ответа Last-Modified можно использовать в качестве слабого валидатора. Слабым он считается из-за того, что имеет 1-секундное разрешение. Если в ответе присутствует заголовок Last-Modified , то для валидации кешированного документа клиент может выводить в запросах заголовок If-Modified-Since .
При запросе на валидацию сервер может либо проигнорировать валидацию и послать стандартный ответ 200 OK , либо вернуть ответ 304 Not Modified (с пустым телом), тем самым указывая браузеру взять копию из кеша. В последнем случае в ответ могут входить также заголовки для обновления срока действия кешированного ресурса.
Изменяющиеся ответы
Заголовок HTTP-ответа Vary определяет, как по заголовкам будущих запросов понять, может ли быть использована копия из кеша, или нужно запросить новые данные у сервера.
Если кеш получает запрос, который можно удовлетворить сохранённым в кеше ответом с заголовком Vary , то использовать этот ответ можно только при совпадении всех указанных в Vary полей заголовка исходного (сохранённого в кеше) запроса и нового запроса.

Это может быть полезно, например, при динамическом предоставлении контента. При использовании заголовка Vary: User-Agent кеширующие сервера, принимая решение об использовании страницы из кеша, должны учитывать агент пользователя. Так можно избежать ситуации, когда пользователи мобильных устройств по ошибке получат десктопную версию вашего сайта. Вдобавок, это может помочь Google и другим поисковым системам обнаружить мобильную версию страницы, и может также указать им на то, что здесь нет никакой подмены контента с целью поисковой оптимизации (Cloaking).
Поскольку значение заголовка User-Agent различается («varies») у мобильных и десктопных клиентов, закешированный мобильный контент не будет по ошибке отсылаться пользователям десктопов и наоборот.
Как запретить браузеру отдавать кэш на http запрос

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

Взаимодействие клиентов с сервером
Прежде чем отправить запрос по какому-либо URL, браузер проверяет наличие требуемого объекта в собственном кэше, и если таковой имеется, используется он. В противном случае запрос отправляется до следующего сервера, где так же проверяется кэш. Если ни на одном из промежуточных серверов соответствия запросу не найдено, то в конце концов он достигает сервера-источника и отклик приходит оттуда. Стоит заметить, что прокси и шлюзы используются множеством пользователей, и потому их кэш называется публичным, или разделяемым.
Основной проблемой, возникающей в связи с использованием кэширования, обычно считается нарушение семантической прозрачности. Это означает, что поскольку запрос может не достигнуть сервера-источника, то существует вероятность того, что пользователь получит из кэша устаревшую версию страницы или файла. С другой стороны, кэширование способно многократно ускорить работу сайта в глазах пользователя без каких-либо затрат.
В любом случае следует помнить, что браузеры пользователей и прокси-серверы работают независимо от вас, поэтому если правильно не настроить сайт, то кэширование будет происходить по правилам, установленным по умолчанию администраторами того или иного кэша.
Как работает кэширование
Вместе с запрашиваемыми данными, сервер отправляет так называемые транспортные заголовки, в которых, помимо прочего, может сообщаться дата и время отклика, его срок годности, правила кэширования и т. д. Если кэширование не запрещено, то отклик сохраняется в кэше вместе с этими заголовками.
В свою очередь клиентский запрос также включает в себя набор заголовков. В частности, указываются дата и время самого запроса, по которым кэш может проверить свежесть (актуальность) хранимого отклика, сверив соответствующие значения.
Если закэшированные данные найдены, но уже устарели, то запрос направляется дальше, дополненный специальными заголовками-валидаторами, позволяющими серверу-источнику подтвердить или опровергнуть пригодность кэша. Если тот актуален, то вместо запрашиваемых данных сервер может отправить единственный заголовок (т. н. код 304), разрешающий использовать кэш. Этот механизм называется валидацией, или условным кэшированием.
Таким образом, управление HTTP-кэшированием сайта сводится к отправке заголовков отклика, сообщающих правила его кэширования и срок годности, а также проверке входящих заголовков GET-запросов на предмет свежести найденного кэша.
ПРИМЕЧАНИЕ: независимо от заголовков отклика, не могут быть закэшированы данные, запрошенные по безопасному протоколу HTTPS или методом, отличным от GET.
HTTP-заголовки
Наиболее простой, но наименее эффективный способ управления кэшированием — применение тегов <meta /> . Они размещаются внутри блока <head> , в HTML коде, и потому попросту игнорируются прокси-серверами, которые HTML не читают.
Гораздо более эффективны HTTP-заголовки. Они не видны в HTML коде и отправляются перед запрашиваемыми данными. Некоторые заголовки отправляются веб-сервером автоматически, однако вы можете переопределять и их, просто устанавливая необходимые значения. В языке PHP для отправки заголовков предназначена функция header() , единственным параметром которой является собственно сам заголовок. Помните, что отправлять их можно только до начала вывода данных.
Заголовок Expires
Одним из основных в механизме кэширования является заголовок Expires . В нем сообщается срок годности представленных в отклике данных. В значении указывается дата и время по Гринвичу (в формате RFC 1123), по истечении которых кэш должен считаться устаревшим, а запросы перенаправляться на сервер-источник для проверки его актуальности или получения свежего отклика:
Этот заголовок поддерживается практически везде. Он одинаково хорошо подходит как для кэширования статических изображений, так и для регулярно обновляющихся страниц. Достаточно указать подходящее значение. Если же выбрать уже прошедшую или неверную дату, то данные будут считаться некэшируемыми.
Применение заголовка Expires предполагает, что часы в кэше и на сервере-источнике синхронизированы. В противном случае одна и та же метка времени может трактоваться по-разному. Также не следует забывать обновлять срок годности, когда это необходимо. Иначе по его истечению кэширование перестанет работать, и все запросы станут поступать на сервер.
Заголовок Cache-Control
Более гибким инструментом кэширования является заголовок Cache-Control . Он поддерживает целый ряд директив, которые можно указывать через запятую. Например:
Ниже приведены допустимые директивы для заголовка отклика:
| Директива | Описание |
|---|---|
| max-age=n | Устанавливает срок годности ответа равным n секунд после запроса, что, в отличие от Expires , позволяет не беспокоиться о синхронизации времени и обновлении срока годности. |
| s-maxage=n | То же, что и max-age , но применимо к разделяемым (т. е. прокси) кэшам. |
| public | Разрешает кэширование запросов, защищенных аутентификацией, которые по умолчанию не кэшируются. |
| private | Разрешает кэширование только в однопользовательских кэшах (т. е. браузерах), запрещая его в разделяемых. Применяется, например, когда разным пользователям отдаются различные версии страницы. Однако этот метод не гарантирует конфиденциальность данных. Для этого рекомендуется использовать шифрованный протокол HTTPS, который вообще не кэшируется. |
| no-cache | Обязывает кэш направлять все запросы на сервер-источник для подтверждения свежести хранимого в кэше отклика. Применяется, если необходимо сохранить семантическую прозрачность, не теряя преимуществ условного кэширования. |
| no-store | Запрещает кэшировать отклик при любых условиях. Как и private , не является надежным средством защиты информации от третьих лиц. |
| must-revalidate | Обязывает кэш жестко следовать предоставленной информации о свежести хранимого отклика. Обычно при определенных обстоятельствах (например, при отсутствии интернет-соединения) HTTP позволяет использовать просроченный кэш. Указывая этот заголовок, вы сообщаете о строгой необходимости следовать установленным правилам. |
| proxy-revalidate | То же, что и must-revalidate , но применимо к разделяемым кэшам. |
| no-transform | Некоторые прокси-сервера могут в каких-либо целях преобразовывать заголовки и даже тело отклика (изменять формат изображения, дописывать заголовки, менять их порядок и т. п.). Данная директива запрещает любые подобные действия. |
В случае указания противоречивых директив приоритетными являются более прозрачные, т. е. обеспечивающие свежесть предоставляемых клиенту данных. В свою очередь директива max-age приоритетней, чем заголовок Expires .
HTTP/1.0 и заголовок Pragma
Следует упомянуть, что заголовок Cache-Control появился в протоколе HTTP/1.1, а значит клиенты и прокси, поддерживающие только HTTP/1.0, не поймут его директив. Поэтому в целях совместимости Expires можно оставить даже при наличии max-age , а если нужно запретить использование кэша, можно также указать дополнительный заголовок из протокола HTTP/1.0:
Это единственное назначение заголовка Pragma , который в большинстве случаев просто игнорируется кэшем с поддержкой HTTP/1.1.
Валидация, или условное кэширование
Выше уже упоминалось, что клиентский запрос может включать заголовки с информацией о закэшированном отклике. Получив такие заголовки, сервер может проверить его свежесть и принять решение о необходимости отправить новый отклик либо сообщить клиенту, что можно использовать кэшированный. Процесс проверки называется валидацией, а сами заголовки — валидаторами.
Наиболее распространенным валидатором является Last-Modified . Сервер может отправлять его с каждым откликом, сообщая в нем дату и время последнего изменения запрашиваемой страницы или файла. На языке PHP это может выглядеть так:
Значение может храниться в файловой системе, базе данных или вычисляться каким-либо другим образом — не суть важно. Клиент, закэшировав отклик с таким заголовком, в дальнейшем будет возвращать его значение в заголовке запроса If-Modified-Since , как бы спрашивая, изменялся ли документ с тех пор. Сравнивая полученный If-Modified-Since с реальным значением Last-Modified сервер решает, можно ли использовать кэшированный отклик или следует послать свежий:
Код 304 сообщает, что изменений в документе не было. Получив его, клиент использует данные из кэша. Таким образом, запрос все же достигает сервера, однако отклик ограничивается лишь заголовками.
Протокол HTTP/1.1 предлагает альтернативный способ определения свежести кэша с помощью валидатора ETag , представляющем собой уникальный идентификатор отклика — произвольную строку, заключенную в кавычки. Этот метод работает аналогичным образом. Сервер должен прикреплять к отклику заголовок ETag . Eсли в кэше найден подходящий отклик, то клиентский запрос снабжается заголовком If-None-Match с той же меткой. Сверив метки на сервере можно сделать соответствующие выводы, и если кэш достаточно свеж, отправить клиенту «304 Not Modified».
Значением метки ETag может быть дата последнего изменения, хэш содержимого или любая другая строка, однозначно определяющая текущую версию страницы или файла. Изменив содержимое отклика, позаботьтесь о смене значения ETag .
Большинство современных веб-серверов автоматически отправляют заголовки ETag и Last-Modified для статических файлов, однако в случае динамических страниц позаботиться об этом нужно самостоятельно. Для большей совместимости можно применять оба валидатора.
Заголовок Connection
Вместе с откликом «304 Not Modified» можно отправлять и другие заголовки, чтобы обновить их значения в кэше. И наоборот, заголовок Connection позволяет перечислить названия тех заголовков, которые обновлять не нужно:
Трюк в том, что перечисленные заголовки будут причислены к «заголовкам соединения», которые по умолчанию не кэшируются (это Connection , Keep-Alive , Proxy-Authenticate , Proxy-Authorization , TE , Trailers , Transfer-Encoding и Upgrade ).
Заголовок Vary
Для правильной работы кэша желательно, чтобы каждому URL соответствовал единственный вариант содержимого. Например, если страница представлена на сайте в двух языках, то у каждой версии должен быть свой URL. В противном случае пользователь может получить отклик из кэша на другом языке.
Однако не всегда возможно организовать уникальные URL каждому варианту отображения. Например, при использовании технологии сжатия страниц, кэш должен отдельно хранить сжатую и несжатую версии, поскольку не каждый клиент поддерживает gzip-сжатие.
Для подобных случаев предусмотрен заголовок Vary . Он позволяет сообщить кэшу названия тех заголовков запроса, при изменении которых сервер возвращает другой вариант документа. Например, если отображение страницы зависит от браузера и того, поддерживает ли он gzip-сжатие, то вместе с откликом серверу следует отправлять заголовок:
В этом случае в кэше будут сохраняться различные варианты запрошенной страницы для разных браузеров и поддерживаемых ими типов документа. Не забывайте также генерировать различные значения валидатора ETag для разных значений перечисленных в Vary заголовков.
Кэширование средствами Apache
Если ваш сайт работает под управлением веб-сервера Apache, то в вашем распоряжении могут оказаться два весьма полезных модуля — mod_expires и mod_headers. По умолчанию они входят в конфигурацию сервера, но выключены. Чтобы проверить их наличие, введите в консоли httpd -l (а также httpd -M в последних версиях Apache) или, например, воспользуйтесь PHP-функцией phpinfo() . Если модули недоступны, то для их установки понадобится административный доступ к серверу. Подробности можно узнать в руководстве веб-сервера Apache.
Модуль mod_expires управляет установкой заголовка Expires и директивы max-age заголовка Cache-Control . С его помощью можно определять срок годности кэша относительно времени последнего изменения физического файла на сервере, последнего доступа к нему или абсолютным значением даты/времени.
Модуль mod_headers позволяет управлять любыми заголовками, включая установку прочих директив Cache-Control . Команды обоих модулей прописываются в конфигурации Apache или в файле .htaccess , если такая возможность включена администратором. Например так:
Обратите внимание, что модуль mod_expires добавляет директиву max-age автоматически. Преимуществом использования этих модулей является возможность управлять кэшированием как динамических страниц, так и статических файлов на диске. Однако в гибкости кэширования динамических страниц они несколько уступают управлению из серверных скриптов, поскольку правила их жестко определяются в настройках сервера. Подробнее об этих модулях можно узнать из руководств mod_expires и mod_headers.
10 полезных рекомендаций
Старайтесь указывать срок годности документов и файлов в соответствии с регулярностью их обновления. Статическим данным можно указать большое значение max-age , ежедневно обновляемым страницам — около суток и т. д.
По возможности делайте каждый документ доступным только по одному URL. Не передавайте пользовательские данные через URL, если только генерируемая страница целиком не предназначена одному пользователю.
Если вы обновляете файл, предназначенный для скачивания, желательно изменить его имя и, соответственно, ссылки на него, чтобы пользователь по ошибке не скачал устаревший вариант из кэша. Так же можно поступить при необходимости срочно обновить, например, изображение, которое могло быть закэшировано на длительное время.
Следите, чтобы заголовок Last-Modified соответствовал реальной дате изменения содержимого. Не пересохраняйте файлы и страницы, если не собираетесь их менять.
Минимизируйте использование SSL, а POST-запросы используйте только там, где это необходимо.
Старайтесь избегать ситуаций, когда отображаемые данные зависят от куки. Их сложно кэшировать. Если на странице есть элементы, отображение которых отличается для разных пользователей, приемлемым решением может оказаться подгрузка их с помощью технологии AJAX.
Сокращайте количество ресурсов, требующих HTTP-аутентификации. Например, изображения на защищенной паролем странице обычно не должны требовать аутентификации. Вынесите их в директорию со свободным доступом. Если вы хотите кэшировать страницы, защищенные паролем, используйте заголовок:
Отправляйте заголовок Content-Length с указанием длины тела отклика в байтах (без длины заголовков). HTTP устроен так, что это позволит клиенту отправлять несколько запросов по одному соединению одновременно.
Если ваш сервер собирает статистику запросов, и вы боитесь ее потерять, оставьте на странице маленький некэшируемый элемент, вроде изображения размером 1×1 пиксель. Это позволит вам получать входящие запросы от каждого пользователя, даже если основной документ получен из кэша. Эффект от кэширования останется, хотя и будет подпорчен «лишним» запросом.
Проверить отправляемые сервером заголовки можно, например, на REDbot или с помощью этого плагина для Firefox.
13 комментариев
viktor37 @ 18 мая 2011
Алексей @ 18 мая 2011
mindwork @ 23 мая 2011
Конечно, использование nginx для статики предпочтительнее. Ускорение отклика будет существенным, если у вас высоконагруженный проект и Apache не успевает отдавать контент.
Ускорить сайт в глазах конечного пользователя можно также с помощью клиентской оптимизации (спрайты, сжатие текста, оптимизация верстки и JS). Описание методов можно почерпнуть в книге «Реактивные веб-сайты. Мациевский, Степанищев, Кондратенко». Там, кстати, есть и раздел о настройке сжатия в nginx и lighttpd.
Алексей @ 23 мая 2011
Максим @ 12 июля 2011
WarGot @ 29 июля 2011
Ярослав @ 4 марта 2012
Сергей @ 5 октября 2012
Гурген @ 17 марта 2013
Гурген, запретить кэширование изображений можно средствами веб-сервера, отправляя вместе с ними заголовок Cache-Control с директивой no-store . Для этого разместите в папке с картинками файл .htaccess со следующим содержимым:
Алексей @ 25 марта 2013
Дмитрий @ 26 августа 2013
1) Директива max-age более приоритетна, независимо от значения заголовка Expires .
2) Если в кэше есть свежая (подходящая по сроку годности) версия файла, то запрос попросту не дойдет до сервера. Если это недопустимо, можно воспользоваться условным кэшированием. Для этого отправляйте вместе с файлом заголовок Cache-Control с директивой no-cache и настройте сервер таким образом, чтобы он возвращал 304-й заголовок, если закэшированный файл все еще актуален.
Алексей @ 28 августа 2013
Краткий курс HTML 5
Этот курс знакомит читателя с основами языка HTML 5 и позволяет начать его практическое применение в кратчайшие сроки.
Не кэшировать
Современные браузеры достаточно часто используют в своей работе локальный кэш. Что это означает? Это означает что браузер, получив от сервера html-документ, картинку или другой ресурс, размещает его в своем локальном кэше (проще говоря, записывает полученный ресурс на жесткий диск машины пользователя) и при последующих запросах к такому ресурсу не обращается на сервер, а получает ресурс из локального кеша.
Данная алгоритм работы браузеров резко повышает скорость загрузки html-документов. Так как если ресурс уже загружался, и как следствие расположен в локальном кэше, то время доступа определяется не пропускной способностью канала связи (например, модемного подключения) а скоростью работы жесткого диска.
Однако наряду с достоинствами данный метод так же порождает ряд проблем. В частности большинство начинающих web-программистов, при разработке динамических сайтов, сталкивается с одной и той же проблемой. Суть этой проблемы заключается в том, что вместо повторного обращения на сервер за страницей, запускающей на сервере скрипт, модифицирующий некую информацию, браузер обращается в локальный кэш. И в результате, например трех обращений, происходит не три модификации информации, расположенной на сервере, а только одна.
Для того, что бы заставить браузер каждый раз обращаться за страницей на сервер необходимо запретить браузеру заносить данный ресурс в кэш. Ниже приведены наиболее распространенные методы, запрещающие кэширование или позволяющие его обойти.
Генерация нового URL
Допустим что запрашиваемый ресурс имеет следующий url: test.html?id=7. Как видно из url’а ему передается один параметр. Добавим, например, при помощи JavaScript, в url еще один параметр, а его значением сделаем случайное число. В результате url будет выглядеть следующим образом: test.html?id=7&rnd=0.6700820127538827. Случайный параметр будет каждый раз генерироваться заново. Ниже приводится листинг, демонстрирующий этот подход:
Каждый раз результат такого запроса будет кэшироваться, но так как кэширование производится по всему url, то каждый раз будет получаться новый url и браузер будет вынужден запрашивать с сервера ресурс, так как url двух запросов не будут совпадать в точности.
Поля заголовков
Управлять кэшированием можно так же со стороны сервера. Для этого ресурс, отправляемый браузеру, сопровождается полями заголовка. Детальное описание полей заголовка может быть найдено в стандарте Rfc 2068, который описывает протокол HTTP 1.1.
Поле заголовка Expires
Значением данного заголовка является дата, после которой содержимое ресурса устареет. Если пользователь после этой даты обратиться к ресурсу, браузер должен запросить ресурс у сервера, а не из локального кэша.
Если поле >Expires< содержит дату, прошедшую, по отношению к текущей, то при следующем обращении к ресурсу браузер будет вынужден снова обратиться к серверу. Это произойдет вследствие того, что либо документ не будет занесен в кэш — как уже устаревший, либо при обращении к кэшу браузер определит, что документ уже устарел. Следующий листинг на PHP демонстрирует использование заголовка Expires:
Поле заголовка Last-Modified
Значением данного заголовка является дата последнего обновления ресурса. Большинство современных браузеров используют следующий алгоритм, если ресурс уже находится в локальном кэше:
- запрашивает с сервера дату последнего обновления ресурса
- сравнивает полученную дату и дату ресурса в локальном кэше
- если ресурс на сервере новее ресурса в кэше — запрашивается ресурс с сервера
Если ресурс, расположенный на сервере, содержит в данном поле текущую дату, то браузер будет каждый раз запрашивать ресурс с сервера, а не из локального кэша. Следующий листинг демонстрирует использование поля заголовка Last-Modified:
Поля заголовка Cache-Control и Pragma
И, наконец, поля заголовка, непосредственно отвечающие за кэширование ресурса. Поле <Pragma> было определено в стандарте Rfc 1945, описывающим протокол HTTP 1.0. Данное поле считается устаревшим, но в некоторых случаях приходится использовать именно его. В частности некоторые proxy-сервера неправильно обрабатывают запросы к постоянно изменяющимся ресурсам, если вместе с ресурсом не передается данное поле заголовка.
Второе поле определено в стандарте Rfc 2068, который описывает протокол HTTP 1.1. Данное поле заголовка позволяет запретить кэширование, и каждый раз запрашивать ресурс с сервера. Следующий листинг демонстрирует использование полей заголовка Cache-Control и Pragma для запрета кэширования: