Учебное пособие по кэшированию, часть 2
Вторая часть довольно подробного и интересного изложения материала, касающегося кэша и его использования. Часть 1.
Автор, Mark Nottingham, — признанный эксперт в области HTTP-протокола и веб-кэширования. Является председателем IETF HTTPbis Working Group. Принимал участие в редактировании HTTP/1.1, part. 6: Caching. В настоящий момент участвует в разработке HTTP/2.0.
От переводчика: об опечатках и неточностях просьба сообщать в личку. Спасибо.
Как (и как не) управлять кэшем
Существует несколько инструментов, которые веб-дизайнеры и веб-мастера могут использовать для тонкой настройки того, как кэш будет работать с их сайтами. Это может потребовать от вас небольшого ознакомления с конфигурацией сервера, но оно того стоит. Для получения более детальной информации о том, как использовать эти инструменты для работы с вашим сервером, обратитесь к разделам “Реализация” (прим. переводчика — будет в следующей части) ниже.
Мета-теги HTML и HTTP-заголовки
Специалисты, работающие с HTML, могут поместить определенные теги в раздел HTML-документа, в котором описываются такого рода атрибуты. Мета-теги часто используются в надежде, что они помогут пометить документ как некэшируемый или устаревающий в определенное время.
Мета-теги просты в использовании, однако не очень эффективны. Это происходит потому, что они удостаиваются внимания только некоторых браузерных кэшей, но не прокси (которые почти никогда не читают HTML-документ). Хотя это может быть заманчивым — поместить мета-тег Pragma: no-cache в HTML-код страницы, но этот шаг не обязательно сохранит страницу свежей.
С другой стороны, верные HTTP-заголовки дают вам серьезный контроль над тем, как и кэш браузера, и прокси обрабатывают ваш контент. Они не могу быть рассмотрены в HTML и, обычно, автоматически генерируются на стороне веб-сервера. Однако, вы можете управлять ими в некоторой степени, в зависимости от используемого сервера. В следующих разделах вы увидите, какие HTTP-заголовки представляют интерес, и как их применить на своем сайте.
HTTP-заголовки отправляются сервером прежде HTML и рассматриваются только браузером и любым промежуточным кэшем. Заголовки типичного HTTP/1.1 ответа могут выглядеть следующим образом:
HTTP/1.1 200 OK
Date: Fri, 30 Oct 1998 13:19:41 GMT
Server: Apache/1.3.3 (Unix)
Cache-Control: max-age=3600, must-revalidate
Expires: Fri, 30 Oct 1998 14:19:41 GMT
Last-Modified: Mon, 29 Jun 1998 02:28:12 GMT
ETag: «3e86-410-3596fbbc»
Content-Length: 1040
Content-Type: text/html
HTML будет следовать за этими заголовками, отделенный пустой строкой. Смотрите разделы “Реализация” (прим. переводчика — будет в следующей части), чтобы узнать о том, как установить HTTP-заголовки.
Примечание
Если ваш сайт размещен у интернет-провайдера (ISP) или на хостинг-ферме и они не позволяют вам устанавливать произвольные HTTP-заголовки (такие как Expires и Cache-Control ), упорнее выражайте недовольство; это инструменты, необходимые для вашей работы.
HTTP-заголовки Pragma (и почему они не работают)
Многие специалисты считают, что употребление HTTP-заголовока Pragma: no-cache делает контент некэшируемым. Это не всегда верно; спецификация HTTP не описывает никаких указаний для заголовков Pragma в ответе; но Pragma заголовки запроса (заголовки, которые браузер посылает серверу) были упомянуты. Хотя некоторые кэши могут учитывать эти заголовки, большинство — не будет, и их употребление не принесет никакого эффекта. Вместо этого используйте заголовки, указанные ниже.
Контроль свежести HTTP-заголовком Expires
HTTP-заголовок Expires — основной способ управления кэшем; он сообщает всем кэшам, как долго контент трактуется как свежий. По истечении этого времени, кэш всегда будет опрашивать исходный сервер, чтобы узнать, изменился ли контент. Заголовки Expires поддерживаются практически любым кэшем.
Большинство веб-серверов позволяют установить заголовки ответов Expires с помощью ряда методов. Как правило, они позволяют установить абсолютно точное время истечения; время, на основе последнего раза, когда клиент запрашивал контент (last access time); или время, основанное на дате последнего изменения документа на сервере (last modification time).
Заголовки Expires особенно хороши для кэширования статических изображений (таких как навигация и кнопки). Потому что они не изменяются часто и вы можете устанавливать им чрезвычайно долгое время истечения, делая ваш сайт более отзывчивым к пользователям. Они также полезны для управления кэшированием страниц, которые изменяются регулярно. Если вы обновляете страницу с новостями единоразово около шести часов утра, вы можете установить время истечения для контента на этот час — так кэш будет знать о том, когда необходимо получить свежую копию, без необходимости ручного обновления пользователями с помощью нажатия “Обновить”.
Отметим, что только HTTP-дата является валидным значением HTTP-заголовка Expires . Что-то помимо этого наиболее вероятно будет интерпретировано как уже истекшее, так что контент будет некэшируемым. Также, стоит напомнить, что время в HTTP-дате трактуется по Гринвичу (GMT), не в соответствии с местным часовым поясом.
Например:
Expires: Fri, 30 Oct 1998 14:19:41 GMT
Хотя заголовок Expires полезен, он имеет некоторые ограничения. Во-первых, часы веб-сервера и кэша должны быть синхронизированы, так как в расчетах присутствует время; если у них разное представление о текущих дате/времени, ожидаемый эффект не будет достигнут и кэш может ошибочно трактовать устаревший контент как свежий.
Другая проблема с Expires заключается в том, что довольно легко забыть, что вы установили некоторому контенту определенное время истечения. Если вы не обновите время в Expires , прежде чем оно придет, каждый запрос будет обращаться к серверу, увеличивая нагрузку и время ожидания.
Примечание
Очень важно быть уверенным в том, что часы вашего веб-сервера работают правильно, если вы используете заголовок Expires . Один из способов удостовериться в этом — использование сетевого протокола для синхронизации внутренних часов компьютера (Network Time Protocol, NTP); поговорите об этом с вашим системным администратором.
HTTP-заголовки Cache-Control
HTTP/1.1 ввел новый класс заголовков, заголовки ответа Cache-Control , чтобы дать веб-мастерам больший контроль над контентом и решить ограничения, связанные с Expires .
- max-age=[секунды] — описывает максимальный период времени, в течение которого контент остается свежим. Аналогично Expires , эта директива указывает время, относительное моменту запроса, а не абсолютную величину. [секунды] — количество секунд от момента запроса, в течение которых вы хотите, чтобы контент трактовался как свежий.
- s-maxage=[секунды] — подобен max-age, отличаясь тем, что применяется только к общему кэша (т.е. прокси).
- public — помечает авторизованные запросы, как кэшируемые; это нормально, если требуется HTTP-аутентификация, ответы автоматически становятся приватными.
- private — позволяет кэшу, который действует для определенного пользователя (т.е. в браузере) хранить ответ; общему кэшу (т.е. прокси) — нет.
- no-cache — принуждает кэш отправлять запрос на исходный сервер каждый раз для валидации, прежде чем выдать кэшированную копию. Это полезно, когда необходимо гарантировать, что аутентификация принята во внимание (в сочетании с public) или для поддержания жесткой свежести без потери преимуществ кэширования.
- no-store — указывает кэшу не сохранять копию контента, ни при каких условиях.
- must-revalidate — сообщает кэшу, что он должен подчиниться любой свежей информации, что вы ему предоставляете о контенте. HTTP позволяет кэшу хранить устаревший контент при определенных условиях; упомянув этот заголовок, вы сообщаете кэшу, что вы хотите, чтобы он строго следовал вашим правилам.
- proxy-revalidate — подобен must-revalidate, кроме того, что применяется только к прокси.
Когда оба, и Cache-Control , и Expires — присутствуют, больший приоритет имеет Cache-Control . Если вы планируете использовать Cache-Control , вам следует ознакомиться с документацией по HTTP/1.1.
Валидаторы и валидация
В секции “Как работает кэш” мы сказали, что валидация используется серверами и кэшами для взаимодействия, когда контент был изменен. Используя её, кэш избегает необходимости скачивания контента целиком, когда он уже имеет локальную копию, но не уверен в том, что она все еще свежая.
Валидаторы очень важны; если нет ни одного и не доступна любая информация о свежести ( Expires или Cache-Control ), кэш не будет хранить контент вообще.
Наиболее распространенный валидатор это время, когда документ был в последний раз именен, о чем сообщено в заголовке Last-Modified . Когда кэш хранит контент, который содержит заголовок Last-Modified , он (кэш) может использовать его для того, чтобы опросить сервер, чтобы узнать был ли изменен контент со времени его последнего просмотра, с помощью If-Modified-Since запроса.
HTTP/1.1 ввел новый вид валидатора, названный ETag . ETag — это уникальные идентификаторы, которые генерируются сервером и изменяются каждый раз, когда запрашивается контент. Поскольку сервер управляет тем, как сгенерированы ETag , кэш может быть уверен в том, что, если ETag совпадают по результатам запроса If-None-Match , контент действительно совпадает.
Почти все кэши используют время из Last-Modified как валидатор; ETag также становятся распространенными.
Большинство современных веб-серверов могут автоматически генерировать и ETag , и Last-Modified заголовки, чтобы использовать в качестве валидаторов для статического контента (т.е. файлов); вам не придется ничего делать. Однако, они не знают достаточно о динамическом контенте (таком, как CGI, ASP, базы данных сайтов), чтобы их генерировать (см. раздел “Написание скриптов, дружественных кэшу”).
Советы по построению дружественных кэшу сайтов
Помимо использования свежести и валидации, существует ряд других мер, которые вы можете выполнять, чтобы сделать ваш сайт более дружественным кэшу.
- Используйте URL-адреса последовательно — это золотое правило кэширования. Если вы храните некоторый контент на разных страницах, для разных пользователей или на разных сайтах, он должен использовать один и тот же URL-адрес. Это самый простой и самый эффективный способ сделать ваш сайт дружественным кэшу. Например, если вы однажды используете “/index.html” в качестве эталона, используйте его таким образом всегда.
- Используйте общую библиотеку изображений и других элементов и обращайтесь к ним из разных мест.
- Храните в кэше изображения и страницы, которые редко изменяются, с помощью указания заголовка Cache-Control: max-age с большими значениями max-age.
- Дайте кэшу возможность распознавать страницы, которые обновляются регулярно, указав соответствующий max-age или время истечения (expiration time).
- Если ресурс (особенно скачиваемый файл) изменяется, измените его имя. Таким образом, вы можете установить время его истечения далеко в будущем и при этом гарантировать, что все еще храните актуальную версию; страница, которая хранит ссылку на него, — единственное, что потребует короткого времени истечения.
- Не изменяйте файлы без необходимости. Если вы так поступаете, всё будет иметь обманчиво недавнюю дату в Last-Modified .
- Используйте куки только когда необходимо — куки сложно кэшировать и они, в большинстве ситуаций, не нужны. Если вам необходимо их использовать, ограничьте сферу их применения динамическими страницами.
- Минимизируйте использование SSL — потому как шифрованные страницы не хранятся в общем кэше; пользуйтесь ими только при необходимости и бережно относитесь к взаимодействию через SSL с изображениями.
- Проверьте свои страницы с помощью REDbot — он поможет вам применить многие идеи из этого учебного пособия.
Написание скриптов, дружественных кэшу
По-умолчанию, большинство сценариев (прим. переводчика — в рамках данного текста слово “сценарий” имеет тот же смысл, что и слово “скрипт”) не будет возвращать валидатор ( Last-Modified или ETag в заголовоке ответа) или информацию о свежести ( Expires или Cache-Control ). В то время как некоторые скрипты являются по-настоящему динамическими (это означает, что они возвращают разные ответы на каждый запрос), многие (такие, как поисковые движки или сайты, взаимодействующие с базами данных) могут извлечь пользу из взаимодействия с кэшем.
Вообще говоря, если скрипт производит некий вывод, который является воспроизводимым тем же запросом позднее (неважно, через минуты или дни), он должен быть закэширован. Если контент на выходе сценария изменяется только в зависимости от того, что в URL-адресе, он кэшируется; если вывод зависит от куков, информации об аутентификации или от какого-то другого внешнего фактора, вероятно кэширования не происходит.
- Лучший способ сделать скрипт дружественным кэшу — это выгружать его содержимое в простой файл всякий раз, когда оно меняется. Веб-сервер может затем обрабатывать её как любую другую страницу, создавая и используя валидаторы, которые сделают вашу жизнь проще. Не забудьте только записывать файлы, которые были изменены, так вы сохраните время Last-Modified .
- Другой способ сделать скрипт кэшируемым в ограниченной форме — установить заголовки, связанные с возрастом, настолько далекими в будущем, насколько это практично. Хотя это может быть исполнено с помощью Expires , вероятно проще всего это сделать с помощью Cache-Control: max-age , который сделает запрос свежим на всем протяжении этого периода.
- Если вы не можете этого сделать, вам придется создать валидатор, а затем ответить на If-Modified-Since и/или If-None-Match запросы. Это может быть сделано путем парсинга HTTP заголовков и последующим ответом с 304 Not Modified при необходимости. К сожалению, это не тривиальная задача.
- Не используйте POST, если это не является целесообразным. Ответы POST-методом не хранятся большинством кэшей; если вы отправляете информацию в строке запроса (через GET), кэш может хранить эту информацию на будущее.
- Не вставляйте информацию, специфичную для пользователя, в URL-адрес, если сгенерированный контент не является полностью уникальным для этого пользователя.
- Не рассчитывайте на все запросы пользователя, приходящие с одного хоста, потому что кэши часто работают вместе.
- Генерируйте Content-Length заголовки ответа. Это легко сделать и позволит ответу от вашего скрипта использоваться в постоянном соединении (persistent connection). Это позволяет клиентам запрашивать несколько экземпляров контента через одно TCP/IP соединение, вместе установки соединения на каждый запрос. Это делает ваш сайт, кажется, намного быстрее.
Обратитесь к «Заметкам о реализации» для более конкретной информации (прим. переводчика — будет в следующей части).
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») у мобильных и десктопных клиентов, закешированный мобильный контент не будет по ошибке отсылаться пользователям десктопов и наоборот.
Где хранятся ответы get запросов к серверу
![]()
![]()
Site design / logo © 2022 Stack Exchange Inc; user contributions licensed under cc by-sa. rev 2022.6.10.42345



Рисунок 1: Элементарный HTTP-запрос

Рисунок 2: Перечень методов, поддерживаемых протоколом HTTP/1.1
URL:

Рисунок 3: Переменная HOST представляет собой сетевой адрес веб-сервера, куда клиент отсылает HTTP-запрос

Рисунок 4: Наиболее распространенные заголовки HTTP-запроса

Рисунок 5: Элементарный HTTP-ответ

Рисунок 6: Перечень кодов статуса

Рисунок 7: Наиболее распространенные HTTP-заголовки ответов

Рисунок 8: Пример использования метода GET

Рисунок 9: Ответ на запрос с использованием метода GET

Рисунок 10: Пример отправки информации методом POST

Рисунок 11: Запрос с использованием метода TRACE

Рисунок 12: Ответ на запрос с методом TRACE

Рисунок 13: Создание простейшей страницы при помощи метода PUT

Рисунок 14: Ответ с кодом об успешном создании страницы

Рисунок 15: Пример использования кода шелла в теле запроса

Рисунок 15: Запрос на удаление ресурса при помощи метода DELETE

Рисунок 16: Ответ на запрос об успешном удалении ресурса

Рисунок 17: Запрос методов, доступных на сервере, при помощи метода OPTIONS

Рисунок 18: Ответ с перечнем методов, доступных на сервере
Где хранятся ответы get запросов к серверу
Вторая часть довольно подробного и интересного изложения материала, касающегося кэша и его использования. Часть 1.
Автор, Mark Nottingham, — признанный эксперт в области HTTP-протокола и веб-кэширования. Является председателем IETF HTTPbis Working Group. Принимал участие в редактировании HTTP/1.1, part. 6: Caching. В настоящий момент участвует в разработке HTTP/2.0.
От переводчика: об опечатках и неточностях просьба сообщать в личку. Спасибо.
Как (и как не) управлять кэшем
Существует несколько инструментов, которые веб-дизайнеры и веб-мастера могут использовать для тонкой настройки того, как кэш будет работать с их сайтами. Это может потребовать от вас небольшого ознакомления с конфигурацией сервера, но оно того стоит. Для получения более детальной информации о том, как использовать эти инструменты для работы с вашим сервером, обратитесь к разделам “Реализация” (прим. переводчика — будет в следующей части) ниже.
Мета-теги HTML и HTTP-заголовки
Специалисты, работающие с HTML, могут поместить определенные теги в раздел HTML-документа, в котором описываются такого рода атрибуты. Мета-теги часто используются в надежде, что они помогут пометить документ как некэшируемый или устаревающий в определенное время.
Мета-теги просты в использовании, однако не очень эффективны. Это происходит потому, что они удостаиваются внимания только некоторых браузерных кэшей, но не прокси (которые почти никогда не читают HTML-документ). Хотя это может быть заманчивым — поместить мета-тег Pragma: no-cache в HTML-код страницы, но этот шаг не обязательно сохранит страницу свежей.
С другой стороны, верные HTTP-заголовки дают вам серьезный контроль над тем, как и кэш браузера, и прокси обрабатывают ваш контент. Они не могу быть рассмотрены в HTML и, обычно, автоматически генерируются на стороне веб-сервера. Однако, вы можете управлять ими в некоторой степени, в зависимости от используемого сервера. В следующих разделах вы увидите, какие HTTP-заголовки представляют интерес, и как их применить на своем сайте.
HTTP-заголовки отправляются сервером прежде HTML и рассматриваются только браузером и любым промежуточным кэшем. Заголовки типичного HTTP/1.1 ответа могут выглядеть следующим образом:
HTTP/1.1 200 OK
Date: Fri, 30 Oct 1998 13:19:41 GMT
Server: Apache/1.3.3 (Unix)
Cache-Control: max-age=3600, must-revalidate
Expires: Fri, 30 Oct 1998 14:19:41 GMT
Last-Modified: Mon, 29 Jun 1998 02:28:12 GMT
ETag: «3e86-410-3596fbbc»
Content-Length: 1040
Content-Type: text/html
HTML будет следовать за этими заголовками, отделенный пустой строкой. Смотрите разделы “Реализация” (прим. переводчика — будет в следующей части), чтобы узнать о том, как установить HTTP-заголовки.
Примечание
Если ваш сайт размещен у интернет-провайдера (ISP) или на хостинг-ферме и они не позволяют вам устанавливать произвольные HTTP-заголовки (такие как Expires и Cache-Control ), упорнее выражайте недовольство; это инструменты, необходимые для вашей работы.
HTTP-заголовки Pragma (и почему они не работают)
Многие специалисты считают, что употребление HTTP-заголовока Pragma: no-cache делает контент некэшируемым. Это не всегда верно; спецификация HTTP не описывает никаких указаний для заголовков Pragma в ответе; но Pragma заголовки запроса (заголовки, которые браузер посылает серверу) были упомянуты. Хотя некоторые кэши могут учитывать эти заголовки, большинство — не будет, и их употребление не принесет никакого эффекта. Вместо этого используйте заголовки, указанные ниже.
Контроль свежести HTTP-заголовком Expires
HTTP-заголовок Expires — основной способ управления кэшем; он сообщает всем кэшам, как долго контент трактуется как свежий. По истечении этого времени, кэш всегда будет опрашивать исходный сервер, чтобы узнать, изменился ли контент. Заголовки Expires поддерживаются практически любым кэшем.
Большинство веб-серверов позволяют установить заголовки ответов Expires с помощью ряда методов. Как правило, они позволяют установить абсолютно точное время истечения; время, на основе последнего раза, когда клиент запрашивал контент (last access time); или время, основанное на дате последнего изменения документа на сервере (last modification time).
Заголовки Expires особенно хороши для кэширования статических изображений (таких как навигация и кнопки). Потому что они не изменяются часто и вы можете устанавливать им чрезвычайно долгое время истечения, делая ваш сайт более отзывчивым к пользователям. Они также полезны для управления кэшированием страниц, которые изменяются регулярно. Если вы обновляете страницу с новостями единоразово около шести часов утра, вы можете установить время истечения для контента на этот час — так кэш будет знать о том, когда необходимо получить свежую копию, без необходимости ручного обновления пользователями с помощью нажатия “Обновить”.
Отметим, что только HTTP-дата является валидным значением HTTP-заголовка Expires . Что-то помимо этого наиболее вероятно будет интерпретировано как уже истекшее, так что контент будет некэшируемым. Также, стоит напомнить, что время в HTTP-дате трактуется по Гринвичу (GMT), не в соответствии с местным часовым поясом.
Например:
Expires: Fri, 30 Oct 1998 14:19:41 GMT
Хотя заголовок Expires полезен, он имеет некоторые ограничения. Во-первых, часы веб-сервера и кэша должны быть синхронизированы, так как в расчетах присутствует время; если у них разное представление о текущих дате/времени, ожидаемый эффект не будет достигнут и кэш может ошибочно трактовать устаревший контент как свежий.
Другая проблема с Expires заключается в том, что довольно легко забыть, что вы установили некоторому контенту определенное время истечения. Если вы не обновите время в Expires , прежде чем оно придет, каждый запрос будет обращаться к серверу, увеличивая нагрузку и время ожидания.
Примечание
Очень важно быть уверенным в том, что часы вашего веб-сервера работают правильно, если вы используете заголовок Expires . Один из способов удостовериться в этом — использование сетевого протокола для синхронизации внутренних часов компьютера (Network Time Protocol, NTP); поговорите об этом с вашим системным администратором.
HTTP-заголовки Cache-Control
HTTP/1.1 ввел новый класс заголовков, заголовки ответа Cache-Control , чтобы дать веб-мастерам больший контроль над контентом и решить ограничения, связанные с Expires .
- max-age=[секунды] — описывает максимальный период времени, в течение которого контент остается свежим. Аналогично Expires , эта директива указывает время, относительное моменту запроса, а не абсолютную величину. [секунды] — количество секунд от момента запроса, в течение которых вы хотите, чтобы контент трактовался как свежий.
- s-maxage=[секунды] — подобен max-age, отличаясь тем, что применяется только к общему кэша (т.е. прокси).
- public — помечает авторизованные запросы, как кэшируемые; это нормально, если требуется HTTP-аутентификация, ответы автоматически становятся приватными.
- private — позволяет кэшу, который действует для определенного пользователя (т.е. в браузере) хранить ответ; общему кэшу (т.е. прокси) — нет.
- no-cache — принуждает кэш отправлять запрос на исходный сервер каждый раз для валидации, прежде чем выдать кэшированную копию. Это полезно, когда необходимо гарантировать, что аутентификация принята во внимание (в сочетании с public) или для поддержания жесткой свежести без потери преимуществ кэширования.
- no-store — указывает кэшу не сохранять копию контента, ни при каких условиях.
- must-revalidate — сообщает кэшу, что он должен подчиниться любой свежей информации, что вы ему предоставляете о контенте. HTTP позволяет кэшу хранить устаревший контент при определенных условиях; упомянув этот заголовок, вы сообщаете кэшу, что вы хотите, чтобы он строго следовал вашим правилам.
- proxy-revalidate — подобен must-revalidate, кроме того, что применяется только к прокси.
Когда оба, и Cache-Control , и Expires — присутствуют, больший приоритет имеет Cache-Control . Если вы планируете использовать Cache-Control , вам следует ознакомиться с документацией по HTTP/1.1.
Валидаторы и валидация
В секции “Как работает кэш” мы сказали, что валидация используется серверами и кэшами для взаимодействия, когда контент был изменен. Используя её, кэш избегает необходимости скачивания контента целиком, когда он уже имеет локальную копию, но не уверен в том, что она все еще свежая.
Валидаторы очень важны; если нет ни одного и не доступна любая информация о свежести ( Expires или Cache-Control ), кэш не будет хранить контент вообще.
Наиболее распространенный валидатор это время, когда документ был в последний раз именен, о чем сообщено в заголовке Last-Modified . Когда кэш хранит контент, который содержит заголовок Last-Modified , он (кэш) может использовать его для того, чтобы опросить сервер, чтобы узнать был ли изменен контент со времени его последнего просмотра, с помощью If-Modified-Since запроса.
HTTP/1.1 ввел новый вид валидатора, названный ETag . ETag — это уникальные идентификаторы, которые генерируются сервером и изменяются каждый раз, когда запрашивается контент. Поскольку сервер управляет тем, как сгенерированы ETag , кэш может быть уверен в том, что, если ETag совпадают по результатам запроса If-None-Match , контент действительно совпадает.
Почти все кэши используют время из Last-Modified как валидатор; ETag также становятся распространенными.
Большинство современных веб-серверов могут автоматически генерировать и ETag , и Last-Modified заголовки, чтобы использовать в качестве валидаторов для статического контента (т.е. файлов); вам не придется ничего делать. Однако, они не знают достаточно о динамическом контенте (таком, как CGI, ASP, базы данных сайтов), чтобы их генерировать (см. раздел “Написание скриптов, дружественных кэшу”).
Советы по построению дружественных кэшу сайтов
Помимо использования свежести и валидации, существует ряд других мер, которые вы можете выполнять, чтобы сделать ваш сайт более дружественным кэшу.
- Используйте URL-адреса последовательно — это золотое правило кэширования. Если вы храните некоторый контент на разных страницах, для разных пользователей или на разных сайтах, он должен использовать один и тот же URL-адрес. Это самый простой и самый эффективный способ сделать ваш сайт дружественным кэшу. Например, если вы однажды используете “/index.html” в качестве эталона, используйте его таким образом всегда.
- Используйте общую библиотеку изображений и других элементов и обращайтесь к ним из разных мест.
- Храните в кэше изображения и страницы, которые редко изменяются, с помощью указания заголовка Cache-Control: max-age с большими значениями max-age.
- Дайте кэшу возможность распознавать страницы, которые обновляются регулярно, указав соответствующий max-age или время истечения (expiration time).
- Если ресурс (особенно скачиваемый файл) изменяется, измените его имя. Таким образом, вы можете установить время его истечения далеко в будущем и при этом гарантировать, что все еще храните актуальную версию; страница, которая хранит ссылку на него, — единственное, что потребует короткого времени истечения.
- Не изменяйте файлы без необходимости. Если вы так поступаете, всё будет иметь обманчиво недавнюю дату в Last-Modified .
- Используйте куки только когда необходимо — куки сложно кэшировать и они, в большинстве ситуаций, не нужны. Если вам необходимо их использовать, ограничьте сферу их применения динамическими страницами.
- Минимизируйте использование SSL — потому как шифрованные страницы не хранятся в общем кэше; пользуйтесь ими только при необходимости и бережно относитесь к взаимодействию через SSL с изображениями.
- Проверьте свои страницы с помощью REDbot — он поможет вам применить многие идеи из этого учебного пособия.
Написание скриптов, дружественных кэшу
По-умолчанию, большинство сценариев (прим. переводчика — в рамках данного текста слово “сценарий” имеет тот же смысл, что и слово “скрипт”) не будет возвращать валидатор ( Last-Modified или ETag в заголовоке ответа) или информацию о свежести ( Expires или Cache-Control ). В то время как некоторые скрипты являются по-настоящему динамическими (это означает, что они возвращают разные ответы на каждый запрос), многие (такие, как поисковые движки или сайты, взаимодействующие с базами данных) могут извлечь пользу из взаимодействия с кэшем.
Вообще говоря, если скрипт производит некий вывод, который является воспроизводимым тем же запросом позднее (неважно, через минуты или дни), он должен быть закэширован. Если контент на выходе сценария изменяется только в зависимости от того, что в URL-адресе, он кэшируется; если вывод зависит от куков, информации об аутентификации или от какого-то другого внешнего фактора, вероятно кэширования не происходит.
- Лучший способ сделать скрипт дружественным кэшу — это выгружать его содержимое в простой файл всякий раз, когда оно меняется. Веб-сервер может затем обрабатывать её как любую другую страницу, создавая и используя валидаторы, которые сделают вашу жизнь проще. Не забудьте только записывать файлы, которые были изменены, так вы сохраните время Last-Modified .
- Другой способ сделать скрипт кэшируемым в ограниченной форме — установить заголовки, связанные с возрастом, настолько далекими в будущем, насколько это практично. Хотя это может быть исполнено с помощью Expires , вероятно проще всего это сделать с помощью Cache-Control: max-age , который сделает запрос свежим на всем протяжении этого периода.
- Если вы не можете этого сделать, вам придется создать валидатор, а затем ответить на If-Modified-Since и/или If-None-Match запросы. Это может быть сделано путем парсинга HTTP заголовков и последующим ответом с 304 Not Modified при необходимости. К сожалению, это не тривиальная задача.
- Не используйте POST, если это не является целесообразным. Ответы POST-методом не хранятся большинством кэшей; если вы отправляете информацию в строке запроса (через GET), кэш может хранить эту информацию на будущее.
- Не вставляйте информацию, специфичную для пользователя, в URL-адрес, если сгенерированный контент не является полностью уникальным для этого пользователя.
- Не рассчитывайте на все запросы пользователя, приходящие с одного хоста, потому что кэши часто работают вместе.
- Генерируйте Content-Length заголовки ответа. Это легко сделать и позволит ответу от вашего скрипта использоваться в постоянном соединении (persistent connection). Это позволяет клиентам запрашивать несколько экземпляров контента через одно TCP/IP соединение, вместе установки соединения на каждый запрос. Это делает ваш сайт, кажется, намного быстрее.
Обратитесь к «Заметкам о реализации» для более конкретной информации (прим. переводчика — будет в следующей части).
Схема функционирования HTTP-сообщений и возможные риски
В этой статье мы рассмотрим структуру HTTP-сообщений и проанализируем, что происходит за кадром при просмотре веб-страниц.
Автор: Ryan Brown
Протокол HTTP используется в мировой паутине для обеспечения информационных транзакций между клиентом и сервером. На данный момент широко распространена версия HTTP/1.1, однако мировая индустрия вскоре начнет переходить на версию 2.0. Веб-сайты и веб-приложения являются тем, на чем построена всемирная паутина, однако между этими двумя сущностями есть ключевое различие, которое заключается в том, что веб-приложения принимают данные от пользователей. Веб-сайт может быть статическим, а веб-приложение обязательно должно быть динамическим. Веб-сайт хранит содержимое на веб-сервере, и ресурсы веб-сервера могут быть получены клиентами, однако сами ресурсы остаются неизменными. С другой стороны, веб приложение принимает данные от пользователя и динамически генерирует выходные сведения на базе того, что введено. В самом начале мировая паутина состояла только из веб-сайтов. Постепенно сайтов становилось все меньше, веб-приложения стали занимать более доминантную позицию.
Коммуникационная транзакция между клиентом и сервером через протокол HTTP состоит из того, что мы называем HTTP-сообщениями. HTTP-сообщение состоит из HTTP-запроса, посылаемого клиентом серверу, и HTTP-ответа, возвращаемого сервером клиенту на основе полученного запроса. Ключевой момент при тестировании безопасности веб-приложения связан с пониманием логики этих коммуникаций. В этой статье мы рассмотрим структуру HTTP-сообщений и проанализируем, что происходит за кадром при просмотре веб-страниц.
HTTP-запросы
Простой HTTP-запрос:

Рисунок 1: Элементарный HTTP-запрос
HTTP/1.1 – Версия протокола HTTP
HTTP-методы:
Протокол HTTP поддерживает несколько методов. В самой первой версии HTTP/1.0 было три метода: GET, POST и HEAD. В HTTP/1.1 появилось несколько новых методов (см. RFC 2616): OPTIONS, CONNECT, TRACE, PUT и DELETE. В RFC 5789, появившегося в 2010 году, добавился метод PATCH.

Рисунок 2: Перечень методов, поддерживаемых протоколом HTTP/1.1
GET используется для простого запроса ресурсов с веб-сервера. Параметры для этого метода передаются через URL.
POST используется для отсылки данных на веб-сервер через тело HTTP-запроса.
HEAD схож с методом GET, но выводит только заголовки HTTP-ответа, который возвращает сервер.
OPTIONS используется для получения списка методов, принимаемых веб-сервером, которые хранятся в заголовке ‘Allow’ в HTTP-ответе.
PUT предназначен для замены существующего или создания нового ресурса на веб-сервере.
TRACE используется при тестировании и отсылает полное сообщение, полученное веб-сервером, обратно клиенту, что позволяет увидеть конкретное содержимое, полученное веб-сервером.
CONNECT используется редко и совместно с прокси-сервером, который может динамически переключаться в режим туннеля.
PATCH схож с методом PUT. Отличие заключается в этом, что PATCH поддерживает частичную модификацию, в то время как метод PUT поддерживает только полную замену ресурса.
Примечание:
В большинстве приложений используются в основном GET и POST, поскольку HTML поддерживает только эти два метода. Если предполагается использование методов OPTIONS, PUT, DELETE, PATCH, TRACE или CONNECT, необходимо все тщательно продумать и оценить риски в отношении приложения или веб-сервера.
URL:
URL или Uniform Resource Locator (унифицированный указатель ресурсов) является подмножеством унифицированных идентификаторов ресурсов (Uniform Resource Identifiers; URI). Типичная структура URL выглядит так:
По сути, это способ обозначения местонахождения сетевого ресурса и метод передачи данных целевому ресурсу.

Рисунок 3: Переменная HOST представляет собой сетевой адрес веб-сервера, куда клиент отсылает HTTP-запрос

Рисунок 4: Наиболее распространенные заголовки HTTP-запроса
User-Agent содержит информацию о браузере.
Accept определяет тип содержимого, который может принять клиент.
Accept-Encoding определяет тип кодировки, которую может принять клиент.
Content-Length определяет длину тела запроса в октетах. Это значение не имеет особой важности, но некоторые HTTP-методы (например, PUT) требуют этот заголовок. Если необходимо, в значение этого заголовка можно установить 0.
Referer содержит URL-источник запроса.
Cookie предназначен для отправки cookies на сервер для управления сессией.
Connection используется для того, чтобы сообщить серверу о том, что нужно закрыть соединение или оставить открытым для последующих запросов.
Authorization содержит информацию, имеющую отношение к аутентификации на конкретной платформе.
HTTP-ответы
Пример простого HTTP-ответа:

Рисунок 5: Элементарный HTTP-ответ
В примере выше обратим особое внимание на первую строчку, где указана используемая HTTP-версия и код статуса, возвращенный сервером. Код статуса – важная часть HTTP-сообщения, поскольку свидетельствует о том, как приложение обработало запрос.

Рисунок 6: Перечень кодов статуса
Код ответа «100 Continue» редко когда-либо используется. Наиболее распространенный код «200 OK», который сигнализирует о том, что запрос корректен, ресурс существует, сервер обработал запрос и вернул ресурс в теле ответа. Код 201 означает, что ресурс, запрашиваемый в запросе, был успешно создан. Этот код обычно является результатом запроса с использованием метода PUT. Коды статуса 3xx возвращается приложениями со ссылкой на ресурс, куда нужно перенаправить клиента. Ссылка находится либо в тебе ответа, либо в заголовке «location». Коды статуса 4xx отсылаются в случае, если запрашиваемого ресурса на сервере не существует или пользователь не авторизован для получения содержимого или при возникновении ошибки в запросе, отправленном серверу. Коды статуса 5xx возвращаются в случае, когда сервере возникает ошибка, и нет возможности обработать запрос.

Рисунок 7: Наиболее распространенные HTTP-заголовки ответов
Date содержит дату, когда получен запрос.
Server содержит информацию о веб-сервере (например, IIS/Apache).
X-Powered-By содержит информацию, касающуюся технологии, используемой в скриптах, и текущую версию (например, PHP или Asp.net).
Content-Length схож с аналогичным заголовком в запросе. Содержит длину тела ответа в октетах.
Set-Cookie содержит cookie, используемые клиентом при формировании запроса с целью управления сессией.
Expires содержит, время по истечению которого сервер не будет рассматривать ответ как корректный.
Cache-Control указывает клиенту, нужно ли кэшировать возвращаемые запросы.
Пример HTTP-запроса с использованием метода GET:

Рисунок 8: Пример использования метода GET
Как видно на рисунке выше, в первой строчке указан метод, путь к ресурсу с параметрами и используемая версия HTTP. Во второй строчке указан хост, которому посылается запрос. В третьей строке содержится информация о браузере клиента. В четвертной строчке указан тип данных, который может принимать клиент. В пятой строчке указан язык, используемый клиентом. В шестой – cookie, полученные с сервера для поддержания сессии. Седьмая строчка указывает, нужно ли закрывать сессию или оставить открытой. Последняя строчка нужна при использовании метода GET и является пустой.
Пример ответа на запрос с использованием метода GET:

Рисунок 9: Ответ на запрос с использованием метода GET
В ответе на запрос содержится несколько HTTP-заголовков, которые уже обсуждались ранее.
Пример использования метода POST:

Рисунок 10: Пример отправки информации методом POST
HTTP-запрос с использованием метода POST во многом схож с запросом с методом GET, который мы только что рассматривали. Основное отличие заключается в том, что параметры передаются не через URL, а через тело запроса. Этот метод более безопасен и пригоден для передачи конфиденциальной информации, поскольку передаваемые сведения нельзя получить из кэша на стороне клиента. Если планируется передавать параметры, имеющие отношение к аутентификации, следует использовать протокол HTTPS вместе с TLS с целью включения функции шифрования передаваемой информации. Заголовок Content-Length содержит длину тела запроса в октетах. И, наконец, заголовок Referer указывает серверу место возникновения запроса.
Пример HTTP-запроса с методом TRACE:

Рисунок 11: Запрос с использованием метода TRACE
Пример ответа на запрос с методом TRACE:

Рисунок 12: Ответ на запрос с методом TRACE
Как видно из рисунка выше, при использовании метода TRACE сервер возвращает в теле ответа все, что было отправлено в запросе. Эта функция может быть полезна в том случае, если клиенту нужно точно знать, что получено сервером. Метод TRACK выполняет ту же самую функцию, но используется при работе с сервером Microsoft IIS. Уязвимости, которые могут возникать при использовании метода TRACE, связаны с межсайтовой трассировкой (Cross-Site Tracing; XST). Этот класс брешей злоумышленник может использовать для кражи cookie или другой конфиденциальной информации (например, учетных записей), хранимых в заголовке Authorization при помощи межсайтового скриптинга (XSS).
Пример HTTP-запроса с методом PUT:

Рисунок 13: Создание простейшей страницы при помощи метода PUT
Метод PUT требует использования в запросе заголовка Content-Length. Значение этого заголовка особого значение не имеет и может быть установлено нулевым без каких-либо непредсказуемых последствий. Если директория, указанная в URL, уже существует на сервере, соответствующий ресурс будет полностью заменен. Если ресурса, указанного в URL, не существует, то будет создан новый ресурс (предполагается, что соответствующий метод реализован на сервере). Содержимое ресурса, который нужно создать, указывается в теле запроса. В примере выше создается простая html-страница.
Пример ответа на запрос с методом PUT:

Рисунок 14: Ответ с кодом об успешном создании страницы
В идеале от сервера должен прийти ответ с кодом статуса «201 Created», свидетельствующий о том, что ресурс создан. Кроме того, в HTTP-запрос можно было бы вставить заголовок «Expect: 100-continue», чтобы удостовериться, что сервер готов к обработке и не закроет сокет до того, как получит содержимое, которое вы указали в теле запроса. При использовании в запросе заголовка «Expect» в ответ может прийти один из двух кодов статуса: «100 Continue» или «417 Expectation Failed».
После ознакомления с вышеуказанной информации становится понятно, почему этот метод может стать причиной потенциальных проблем и привести к тому, что злоумышленник завладеет вашим сервером. В реальных условиях использование метода PUT разрешено нечасто, и для того, чтобы сервер принял содержимое тело запроса, необходимо наличие заголовка «Authorization».
Пример атаки показан на рисунке ниже. В этом примере в тело HTTP-запроса вставляется код шелла, который, в случае принятия запроса сервером, позволяет злоумышленнику выполнять команды.
Пример HTTP-запроса с методом PUT и вставкой кода шелла:

Рисунок 15: Пример использования кода шелла в теле запроса
Пример HTTP-запроса с методом DELETE:

Рисунок 15: Запрос на удаление ресурса при помощи метода DELETE
Пример ответа на запрос с методом DELETE:

Рисунок 16: Ответ на запрос об успешном удалении ресурса
После успешного удаления ресурса, указанного в URL, сервер должен вернуть код статуса 200 OK.
Пример HTTP-запроса с методом OPTIONS:

Рисунок 17: Запрос методов, доступных на сервере, при помощи метода OPTIONS
Пример ответа на запрос с методом OPTIONS:

Рисунок 18: Ответ с перечнем методов, доступных на сервере
Цель метода OPTIONS – узнать, какие методы разрешены на сервере. При помощи этого метода нельзя нанести какой-либо ущерб, а только собрать нужную информацию. Важно отметить, что содержимое заголовка «Allow» в заголовке ответа часто содержит методы, разрешенные не на сервере, а на прокси-сервере в случае, если трафик проходит через туннель.
В данной статье была представлена информация относительно использования HTTP-сообщений и методов, реализованных в протоколе HTTP/1.1. Кроме того, затрагивалась информация о возможных сценариях атак против веб-приложений при помощи этих методов. Эти сведения могут помочь вам в разработке сценариев пентестов. Надеемся, что после ознакомления с этой заметкой, вы стали лучше понимать механизмы функционирования и потоков данных протокола HTTP.
HTTP протокол: основные правила Интернета, которые должен знать каждый веб-разработчик. Как браузер взаимодействует с сервером.
Тема 13: Кэширование в HTTP: механизмы клиентского и серверного кэша в HTTP
- 11.06.2016
- HTTP протокол, Сервера и протоколы
- 3 комментария
Привет, читатель блога ZametkiNaPolyah.ru! Продолжим знакомиться с протоколом HTTP в рубрике Серверы и протоколы и ее разделе HTTP протокол. Эта запись будет о кэширование в HTTP протоколе. Мы с тобой поговорим о том для чего вообще нужно кэширование в HTTP, о правилах кэширования, о том, какие могут быть ошибки при кэшировании, а так же о том какие есть механизмы о клиента и сервера для управления процессом кэширования в HTTP и о различных моделях при кэширование.

Кэширование в HTTP
Для чего нужно кэширование в HTTP протоколе
HTTP протокол работает в сети интернет, пожалуй, это основной протокол седьмого уровня модели OSI, используемый для связи между узлами. Причем узлы могут находить на разных континентах и их может разделять огромная цепочка узлов и станций. Поэтому разработчики стандарта HTTP протокола были очень озабочены его эффективностью. Одним из механизмов увеличения эффективности HTTP протокола является кэширование. Кэширование в HTTP протоколе позволяет снизить нагрузку на конечный сервер и транзитные сервера не только на седьмом уровне модели OSI, но и на четвертом и ниже.
Сейчас мы не будем вдаваться в технические подробности реализации кэширования HTTP сервера, но общее представление о том, как происходит кэширование в HTTP получим. Хочу отметить, что кэширование в HTTP очень упрощает жизнь клиентам и серверам и значительно снижает нагрузку на сеть, зачастую пользователь даже не догадывается, что содержимое (обсуждение содержимого в HTTP), которое он видит в окне своего браузера – это результат работы механизма кэширования в HTTP.
Кэширование в HTTP реализовывалось для того, чтобы избавиться от необходимости отправлять лишние HTTP запросы от клиента, а так же для того, чтобы сервер не отправлял HTTP ответы целиком так, как будто механизма кэширования нет вовсе. Кэширование в протоколе HTTP управляется, как со стороны сервера, так и со стороны клиента, это необходимо для более точного и удобного восприятия содержимого конечным пользователем.
Так же хочу обратить ваше внимание на то, что механизмы кэширования HTTP сервера (например, веб-сервер Apache) и HTTP клиента (любой браузер) зависят непосредственно от разработчика, сейчас мы рассматриваем общие принципы кэширования протокола HTTP без привязки к какому-либо приложению.
Правильность кэширования в HTTP
Первое правило кэширования в HTTP заключается в том, что сервер должен посылать кэшированные HTTP ответы с самой последней версией содержимого. При этом суть заключается в том, что клиент может получать данные не с конечного HTTP сервера, а с ближайшего сервера, у которого в кэше есть информация о том, что содержится в URI (URI в HTTP), который был указан в HTTP запросе, поэтому должны выполняться следующие правила:
- Проверка достоверности. Сервер, который дает ответ на запрос должен всегда быть в курсе о содержимом конечного сервера, к которому идет обращение.
- Новизна кэша. Новизна кэша определяется конечным сервером.
- Предупреждение. Транзитный сервер должен предупреждать клиента о том, что его информация из кэша не самая «свежая».
Если все эти правила будут соблюдены, можно спокойно утверждать о том, что клиент получит самую достоверную и новую информацию от транзитного сервера, при этом, не обращаясь к конечному.
Предупреждения при кэшировании в HTTP
Предупреждения при кэшировании отправляет сервер в поле заголовка Warning HTTP ответа в том случае, когда информация из кэша теряет свою актуальность. При этом клиент, получив такое HTTP сообщение может обратиться не к кэшу транзитного сервера, а к первоначальному серверу или предпринять какие-либо другие действия.
Механизмы управления кэшированием в HTTP. Поле заголовка Cache-Control в HTTP
В версии протокола HTTP 1.1 в качестве механизмов управления кэшированием используется поле заголовка Cache-Control. Заголовок Cache-Control позволяет управлять HTTP кэшем, как клиенту, так и HTTP серверу при помощи директив, которые они передают вместе с ответами и запросами. При этом директивы, передаваемые в поле заголовка Cache-Control, отменяют значения по умолчанию для кэширования в HTTP. Необходимо отметить тот момент, что директивам заголовка Cache-Control должны повиноваться все участники цепочки обмена данными между конечным сервером и клиентом.
Для управления кэшированием HTTP клиенты могут использовать директивы, которые указаны в таблице ниже. Директивы поля заголовка Cache-Control для клиента
Мы рассмотрели механизм управления кэшированием HTTP клиентом, теперь давайте разберемся с механизмами управления кэшированием со стороны HTTP сервера. Другими словами: как HTTP ответы могут управлять кэшированием. Управление кэширование со стороны сервера происходит аналогичным образом – при помощи заголовка Cache-Control, но директивы заголовка Cache-Control у сервера свои и, соответственно, свои механизмы управления кэшированием при HTTP соединение. Давайте сведем серверные директивы заголовка Cache-Control в одну таблицу.
Общий синтаксис механизмов кэширования в HTTP вы сможете найти ниже.
Cache-Control = «Cache-Control» «:» 1#cache-directive cache-directive = cache-request-directive | cache-response-directive cache-request-directive = «no-cache» [ «=» <«> 1#field-name <«> ] | «no-store» | «max-age» «=» delta-seconds | «max-stale» [ «=» delta-seconds ] | «min-fresh» «=» delta-seconds | «only-if-cached» | cache-extension cache-response-directive = «public» | «private» [ «=» <«> 1#field-name <«> ] | «no-cache» [ «=» <«> 1#field-name <«> ] | «no-store» | «no-transform» | «must-revalidate» | «proxy-revalidate» | «max-age» «=» delta-seconds | cache-extension cache-extension = token [ » crayon-main» style=» max-width: 700px;»>
Мы рассмотрели механизмы управления кэшированием в HTTP, давайте теперь посмотрим, как приложения определяют, что кэш устарел и какими правилами руководствуются.
Модель устаревания кэша в HTTP
Модель устаревания HTTP кэша была разработана для того, чтобы клиенты не делали постоянные запросы к изначальному серверу, а получали актуальные HTTP ответы от промежуточных узлов. Выше мы рассматривали директивы поля Cache-Control, не трудно догадаться, какие из директив лежат в основе модели устаревания HTTP кэша.
Мы не будем вдаваться в механизмы работы модели устаревания, так как они интересны в большей степени разработчикам серверов и клиентов. Нам важно знать и понимать, что модель устаревания кэша в HTTP есть, она действует и управляется директивами, которые мы перечислили выше.
Модель сравнения кэша в HTTP
HTTP протокол имеет модель сравнения кэша. Сравнение кэша в HTTP используется для того, чтобы клиент получал всегда актуальную информацию. Когда сервер содержит информацию длительное время в кэше и хочет использовать его как ответ на запрос клиента, то он сначала должен свериться с исходным сервером, чтобы понять: действительно ли информация, хранимая в его кэше, будет актуальной для клиента. Для проверки актуальности кэша могут быть использованы условные HTTP методы и условные поля HTTP заголовков, а так же специальные поля, о которых мы поговорим ниже.
Например, клиент делает повторный запрос к HTTP серверу к тому же самому URI, при этом контент по данному URI никак не изменился, сервер проанализировал всю эту информацию и вместо того, чтобы отправлять ответ с телом сообщения (HTTP объектом), в котором может быть огромный HTML документ с таблицами стилей и скриптами, сервер просто дает ответ с кодом состояния 304 (Not Modified), после чего браузер подтягивает страничку из кэша, а конечный пользователь думает, что ему эта страница загрузилась с сервера из Австралии.
Помимо условных запросов для модели сравнения HTTP кэша используется поле заголовка Last-Modified, этот заголовок используется сервером и в него записывается дата последнего изменения документа (дата и время в HTTP) и если дата последнего известного изменения совпадает с той датой, что указана в заголовке Last-Modified, то контент, запрашиваемый пользователем, отдается из кэша, если эта дата не совпадает, то ответ дается новый, при этом происходит кэширование и перезапись поля заголовка Last-Modified.
Помимо всего прочего, модель сравнения кэша в HTTP может использовать для проверки актуальности кэша тэг HTTP объекта, который записывается в поле заголовка ETag и который уникален для каждого объекта. Например, в нашем HTML документе был текст Hello, world. а мы его изменили на Hello, World…, так вот значение поля ETag для таких страниц будет разным. Такой подход сравнения кэша в 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 и позволяет начать его практическое применение в кратчайшие сроки.