Заголовок Last-modified содержит дату и время внесения последних изменений в отправляемую страницу, а заголовок Expires говорит о том, сколько времени страница будет доступна. Они оба играют важную роль при кэшировании страницы.
Заголовок Location вставляется сервером для информирования клиента о том, что стоит попробовать осуществить свой запрос повторно по другому URL. Такая ситуация может возникать при «переезде» страницы или тогда, когда несколько URL ссылаются на одну и ту же страницу (возможно, на зеркало страницы, расположенное на другом сервере). Этот трюк также нередко применяется теми компаниями, главная веб-страница которых прописана в домене com, однако клиенты перенаправляются с нее на национальные или региональные страницы, основываясь на IP-адресе клиента или выбранном клиентом языке.
Если страница очень велика по размеру, клиент может не захотеть принимать ее сразу целиком. Некоторые серверы могут принимать запросы, ограничивающие размеры страниц, отсылаемых за один раз. Если страница оказывается слишком большой, она будет разбита на более мелкие единицы и выслана в несколько приемов. Заголовок Accept-Ranges сообщает о том, что сервер готов поддерживать такие запросы частей страниц.
Теперь перейдем к заголовкам, которые могут быть использованы в обоих направлениях. Заголовок Date может применяться как в запросах, так и в ответах. Он содержит время и дату отправки сообщения, а заголовок Range сообщает размер страницы в байтах и высылает его в качестве ответа.
Заголовок ETag выдает короткий тег, который служит в качестве имени контента страницы. Заголовок Cache-Control выдает другие четкие инструкции о том, как кэшировать (или, чаще, как не кэшировать) страницы.
Заголовок Upgrade используется для перехода на новый протокол коммуникации, такой как следующий протокол HTTP, или на защищенный способ передачи данных. Он позволяет клиенту сообщить, какие варианты он может поддерживать, а серверу заявить, каким вариантом пользуется он.
Кэширование
Мы часто возвращаемся на страницы, которые уже просматривали ранее, а на связанных веб-страницах часто размещаются одни и те же ресурсы. В качестве примера можно привести изображения, которые используются для навигации по сайту, а также стандартные таблицы стилей и скрипты. Было бы крайне неэкономично получать все эти ресурсы страниц каждый раз, когда они отображаются, так как у браузера уже есть их копии.
Сохранение полученных страниц для дальнейшего использования называется кэшированием (caching). Преимущество этого метода состоит в том, что страница, сохраненная в кэше, может быть повторно использована, при этом не обязательно повторять передачу. У HTTP есть встроенная поддержка кэширования, чтобы помочь клиентам узнать, могут ли они безопасно использовать страницы повторно. Эта поддержка повышает производительность, сокращая и трафик и время ожидания. Все это достается ценой того, что теперь браузер должен хранить страницы, что практически всегда оправдано, так как локальное хранение информации не требует серьезных затрат ресурсов. Обычно страницы хранятся на диске, так что они могут быть использованы, когда браузер будет запущен в следующий раз.
Спорным вопросом в кэшировании HTTP является то, как определить, что сохраненная копия страницы соответствует тому, как страница выглядит при следующем вызове. Такой вывод не может быть сделан только на основании совпадения URL. Например, URL может выдавать страницу, на которой отображаются последние новости. Контент этой страницы будет часто обновляться, хотя URL останется тем же. С другой стороны, на странице может содержаться список богов греческой и римской мифологии. Эта страница будет обновляться не так часто.
HTTP использует две стратегии решения этой проблемы. Они отбражены на рис. 7.17 как формы обработки между запросом (шаг 1) и ответом (шаг 5). Первая стратегия — это проверка действительности страницы (шаг 2). Запрашивается кэш, и если в нем есть свежая (то есть действительная) копия страницы заданного URL, получать новую копию с сервера нет необходимости. Вместо этого сохраненная в кэше страница может быть возвращена напрямую. Когда сохраненная в кэше страница была первоначально получена с сервера, вместе с ней возвращался заголовок Expires. Его содержание вместе с текущей датой и временем могут использоваться, чтобы сделать заключение о действительности страницы.
Рис. 7.17. Кэширование HTTP
Однако не все страницы приходят с удобным заголовком Expires, который указывает на то, когда страница должна быть получена снова. В конце концов, строить догадки тяжело, особенно догадки на будущее. В этом случае браузер может использовать эвристические правила. Например, если страница не была изменена за последний год (как сказано в заголовке Last-Modified ), довольно логично предположить, что она не изменится за ближайший час. Тем не менее нет гарантии того, что эта ставка не оправдается. Например, биржа может закрыться на ночь, и ее страница не будет обновляться несколько часов, но, как только начнется следующая торговая сессия, изменения будут вноситься постоянно. Таким образом, оправданность кэширования может серьезно варьироваться в зависимости от периода времени. По этой причине эвристические правила должны использоваться осторожно, хотя они зачастую работают очень хорошо.
Поиск страниц, которые не были изменены за определенный промежуток времени, — наиболее полезный способ использования кэширования, так как в этом случае не нужно связываться с сервером. К сожалению, он не всегда работает. Сервер должен использовать заголовок Expires осторожно, так как то, когда на страницу будут внесены изменения, может быть неизвестно. Таким образом, копии, сохраненные в кэше, могут быть свежими, но клиент этого не знает.
В этом случае используется вторая стратегия. Она заключается в запрашивании с сервера информации о действительности сохраненной копии страницы. Этот запрос обозначается как conditional GET (GET с условием). Он показан на рис. 7.17 как шаг 3. Если сервер знает, что сохраненная в кэше копия все еще действительна, он может от-
править короткий ответ, подтверждая это (шаг 4а). В ином случае он должен отослать полный ответ (шаг 4б).
Есть еще несколько полей заголовков, которые используются для того, чтобы сервер мог проверить, действительна ли копия на момент запроса. Клиент знает, когда на страницу были внесены изменения в последний раз благодаря заголовку Last-Modified. Он может выслать это время серверу, используя заголовок If-Modified-Since, чтобы запросить страницу только в том случае, если она была изменена после последнего кэширования. В ином случае сервер может вернуть вместе со страницей заголовок ETag. Он высылает тег, являющийся кратким именем контента страницы, похожим на контрольную сумму, только лучше. (Это может быть криптографический хэш, который мы опишем в главе 8.) Клиент может выяснить, действительна ли сохраненная копии, выслав на сервер заголовок If-None-Match, перечисляющий теги сохраненной копии. Если любой тег совпадает с контентом, который сервер должен переслать в ответ, может быть использована соответствующая копия, сохраненная в кэше. Этот метод может быть использован, когда определять, свежая ли страница, неудобно или бессмысленно. Например, сервер может вернуть различный контент для одного и того же URL, в зависимости от того, какие языки и типы MIME являются предпочтительными. В этом случае только дата модификации не поможет серверу определить, свежая ли копия страницы хранится в кэше. Наконец, обратите внимание на то, что обе эти стратегии кэширования сводятся на нет директивами заголовка Cache-Control. Они могут использоваться, чтобы ограничить кэширование (например, не кэшировать) при отсутствии необходимости. В качестве примера можно привести динамические страницы, которые изменяются при каждом получении. Страницы, на которых требуется авторизация, также не кэшируются.