Кэширование – фундаментальный метод оптимизации веб-приложений.
Он позволяет снизить нагрузку на сервер, ускорить загрузку страниц и улучшить пользовательский опыт.
Без кэширования каждый запрос к серверу требует полной обработки, что может быть ресурсоемким.
Эффективное кэширование сокращает время ответа и повышает масштабируемость.
Использование HTTP заголовков, таких как Last-Modified и ETag, в сочетании с методами HEAD и кодом ответа 304 Not Modified,
представляет собой мощный и элегантный способ реализации кэширования на стороне клиента и прокси-серверов.
Метод HTTP HEAD: получение метаданных без тела
HTTP HEAD – это метод, который позволяет получить метаданные ресурса (например, дату последнего изменения, тип контента, длину) без загрузки самого тела ресурса.
В отличие от GET запроса, который возвращает и заголовки, и содержимое, HEAD возвращает только заголовки.
Это делает HEAD запрос чрезвычайно полезным для проверки актуальности кэшированной версии ресурса. Клиент может отправить HEAD запрос, чтобы узнать, изменился ли ресурс на сервере с момента последнего кэширования.
Если ресурс не изменился, клиент может использовать кэшированную версию, избегая лишней загрузки данных.
HEAD запросы также полезны для определения характеристик ресурса перед его загрузкой, например, для проверки типа файла или его размера.
Это позволяет клиенту принять решение о необходимости загрузки ресурса и о том, как его обрабатывать.
Важно: сервер должен обрабатывать HEAD запросы аналогично GET запросам, за исключением того, что он не должен возвращать тело ответа.
2.1. Как работает HTTP HEAD запрос
Когда клиент отправляет HTTP HEAD запрос на сервер, он указывает URL ресурса, для которого требуется получить метаданные. Сервер, получив запрос, обрабатывает его так же, как и GET запрос, но вместо отправки тела ресурса, он формирует HTTP ответ, содержащий только заголовки.
Эти заголовки включают в себя информацию о ресурсе, такую как Content-Type (тип контента), Content-Length (размер контента), Last-Modified (дата последнего изменения) и ETag (уникальный идентификатор версии ресурса). Клиент получает этот ответ и анализирует заголовки.
На основе полученных заголовков клиент может определить, изменился ли ресурс с момента последнего кэширования. Если заголовки совпадают с теми, что были получены при предыдущем запросе, клиент может использовать кэшированную версию ресурса. В противном случае, клиент должен запросить ресурс заново с помощью GET запроса.
Пример: Клиент отправляет HEAD запрос на изображение. Сервер отвечает заголовками, указывающими, что изображение имеет тип image/jpeg, размер 10KB и дату последнего изменения 2023-10-27.
2.2. Сравнение HEAD и GET запросов
GET и HEAD – оба метода HTTP, используемые для получения информации с сервера, но они существенно различаются по своему назначению и результату. GET запрос предназначен для получения полного ресурса, включая его тело и заголовки. Он возвращает все данные, необходимые для отображения или использования ресурса.
HEAD запрос, напротив, предназначен для получения только метаданных ресурса, представленных в заголовках ответа. Он не возвращает тело ресурса, что делает его более эффективным для проверки актуальности кэшированной версии или получения информации о ресурсе без его загрузки.
Ключевые отличия: GET возвращает тело ресурса, HEAD – нет. GET используется для получения данных, HEAD – для получения информации о данных. HEAD быстрее, чем GET, так как не требует передачи тела ресурса.
Применение: GET – загрузка веб-страниц, изображений, файлов. HEAD – проверка актуальности кэша, получение размера файла, определение типа контента.
Код ответа 304 Not Modified: механизм проверки кэша
HTTP 304 Not Modified – это код ответа сервера, который указывает клиенту, что запрошенный ресурс не был изменен с момента последнего запроса. Этот код используется в сочетании с HEAD запросами и заголовками Last-Modified и ETag для реализации эффективного кэширования.
Когда клиент отправляет HEAD запрос с заголовками If-Modified-Since (содержащим значение Last-Modified из предыдущего ответа) или If-None-Match (содержащим значение ETag из предыдущего ответа), сервер сравнивает эти значения с текущими значениями ресурса.
Если ресурс не изменился, сервер возвращает ответ 304 Not Modified без тела. Клиент, получив этот ответ, использует кэшированную версию ресурса, что значительно снижает нагрузку на сервер и ускоряет загрузку страницы.
Важно: Ответ 304 должен содержать те же заголовки, что и предыдущий ответ, чтобы клиент мог правильно использовать кэшированную версию.
3.1. Условия отправки 304 ответа
Сервер отправляет код ответа 304 Not Modified только при соблюдении определенных условий. Главное условие – ресурс, запрошенный клиентом, не должен быть изменен с момента последнего запроса, о чем свидетельствуют заголовки If-Modified-Since или If-None-Match.
Если клиент отправляет заголовок If-Modified-Since, сервер сравнивает его значение с текущей датой последнего изменения ресурса (Last-Modified). Если дата не изменилась, сервер отправляет 304. Если клиент отправляет If-None-Match, сервер сравнивает его значение с текущим ETag ресурса. При совпадении – отправляется 304.
Важно: Сервер должен игнорировать любые другие параметры запроса (например, параметры GET) при проверке условия 304. Он должен основываться только на значениях If-Modified-Since или If-None-Match и текущем состоянии ресурса.
Внимание: Если ресурс был изменен, сервер должен отправить GET ответ с новым содержимым и заголовками, даже если клиент отправил If-Modified-Since или If-None-Match.
3.2. Роль заголовков Last-Modified и ETag
Last-Modified – это заголовок HTTP, который указывает дату и время последнего изменения ресурса. Клиент использует его в заголовке If-Modified-Since при последующих запросах, чтобы проверить, был ли ресурс изменен.
ETag (Entity Tag) – это уникальный идентификатор версии ресурса, генерируемый сервером. Он может быть хешем содержимого ресурса или любой другой строкой, однозначно идентифицирующей его версию. Клиент использует его в заголовке If-None-Match.
ETag более надежен, чем Last-Modified, так как он позволяет учитывать изменения, которые не влияют на дату последнего изменения (например, изменения метаданных). Last-Modified может быть неточным из-за проблем с синхронизацией времени.
Рекомендации: Используйте ETag, если это возможно. Если ETag недоступен, используйте Last-Modified. Можно использовать оба заголовка одновременно для повышения надежности кэширования.
Реализация кэширования с использованием HTTP HEAD и 304 в PHP
PHP позволяет легко реализовать кэширование с использованием HTTP HEAD и 304 Not Modified. Основная идея заключается в установке заголовков Last-Modified и ETag при первом запросе ресурса и последующей обработке HEAD запросов с проверкой этих заголовков.
Для этого необходимо проверить, является ли запрос HEAD запросом, и если да, то не возвращать тело ресурса, а только заголовки. При обычном GET запросе необходимо устанавливать заголовки Last-Modified и ETag, чтобы клиент мог использовать их при последующих запросах.
При получении HEAD запроса с заголовками If-Modified-Since или If-None-Match, необходимо сравнить их значения с текущими значениями ресурса и отправить ответ 304 Not Modified, если ресурс не изменился. В противном случае, отправить GET ответ с новым содержимым.
Пример: Использование функций header для установки заголовков и $_SERVER[‘REQUEST_METHOD’] для определения типа запроса.
4.1. Установка заголовков Last-Modified и ETag
В PHP установка заголовков Last-Modified и ETag выполняется с помощью функции header. Для Last-Modified необходимо передать дату последнего изменения ресурса в формате, понятном HTTP (например, ‘Sat, 28 Oct 2023 10:00:00 GMT’). Можно использовать функцию gmdate для форматирования даты.
Для ETag можно использовать любой уникальный идентификатор версии ресурса. Часто используется хеш MD5 или SHA1 от содержимого ресурса. Функции md5_file или sha1_file позволяют легко вычислить хеш файла. Важно: ETag должен быть заключен в двойные кавычки.
Пример:
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', filemtime('путь/к/файлу')) . ' GMT');
header('ETag: "' . md5_file('путь/к/файлу') . '"');
Эти заголовки должны быть установлены до отправки любого содержимого браузеру.
Преимущества и недостатки данного подхода к кэшированию
4.2. Обработка HEAD запросов в PHP скрипте
Для обработки HEAD запросов в PHP скрипте необходимо проверить метод запроса с помощью переменной $_SERVER[‘REQUEST_METHOD’]. Если метод равен ‘HEAD’, то необходимо установить заголовки Last-Modified и ETag (если они еще не установлены) и не отправлять тело ответа.
Если запрос является GET, то необходимо установить заголовки Last-Modified и ETag и отправить тело ответа. При получении HEAD запроса с заголовками If-Modified-Since или If-None-Match, необходимо сравнить их значения с текущими значениями ресурса.
Если ресурс не изменился, необходимо отправить ответ 304 Not Modified с теми же заголовками, что и при предыдущем запросе. В противном случае, отправить GET ответ с новым содержимым и заголовками. Важно: exit; после отправки заголовков 304.
Пример: if ($_SERVER['REQUEST_METHOD'] == 'HEAD') { header('Content-Length: 0'); exit; }
