Как предотвратить ненужные сетевые запросы с помощью кеша HTTP

Получение ресурсов по сети происходит медленно и дорого:

  • Для передачи больших ответов требуется много циклов обмена данными между браузером и сервером.
  • Страница не загрузится, пока не будут полностью скачаны все ее критические ресурсы.
  • Если у пользователя ограниченный тариф на мобильный интернет, каждый лишний сетевой запрос будет стоить ему денег.

Как избежать ненужных сетевых запросов? HTTP-кеш браузера – это первая линия защиты. Это не самый мощный и гибкий подход, и у вас ограниченный контроль над временем жизни кэшированных ответов, но он эффективен, поддерживается во всех браузерах и не требует большой работы.

В этом руководстве описаны основы эффективного кеширования HTTP.

Совместимость браузеров

На самом деле не существует единого API, называемого HTTP Cache. Это общее название набора API веб-платформы. Эти API поддерживаются во всех браузерах:

Cache-Control

Browser Support

  • Chrome: 1.
  • Edge: 12.
  • Firefox: 1.
  • Safari: 1.

Source

ETag

Browser Support

  • Chrome: 1.
  • Edge: 12.
  • Firefox: 1.
  • Safari: 1.

Source

Last-Modified

Browser Support

  • Chrome: 1.
  • Edge: 12.
  • Firefox: 1.
  • Safari: 1.

Source

Как работает HTTP-кеш

Все HTTP-запросы, которые делает браузер, сначала направляются в кеш браузера, чтобы проверить, есть ли там действительный кешированный ответ, который можно использовать для выполнения запроса. Если совпадение найдено, ответ считывается из кеша, что позволяет избежать задержки в сети и расходов на передачу данных.

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

Более подробную информацию вы найдете в статье Кеширование HTTP в MDN.

Заголовки запросов: обычно используются значения по умолчанию

Есть ряд важных заголовков, которые должны быть включены в исходящие запросы вашего веб-приложения, но браузер почти всегда заботится о том, чтобы установить их от вашего имени, когда он делает запросы. Заголовки запросов, которые влияют на проверку актуальности, например If-None-Match и If-Modified-Since, появляются в зависимости от того, как браузер интерпретирует текущие значения в кеше HTTP.

Это хорошая новость. Это означает, что вы можете продолжать включать в HTML-код такие теги, как <img src="my-image.png">, а браузер автоматически позаботится о кешировании HTTP без дополнительных усилий.

Заголовки ответов: настройка веб-сервера

Самая важная часть настройки кеширования HTTP – это заголовки, которые веб-сервер добавляет к каждому исходящему ответу. На эффективность кеширования влияют следующие заголовки:

  • Cache-Control. Сервер может возвращать директиву Cache-Control, чтобы указать, как и в течение какого времени браузер и другие промежуточные кеши должны кешировать отдельный ответ.
  • ETag. Когда браузер находит ответ с истекшим сроком действия, он может отправить на сервер небольшой токен (обычно хеш содержимого файла), чтобы проверить, изменился ли файл. Если сервер возвращает тот же токен, значит файл не изменился и скачивать его заново не нужно.
  • Last-Modified. Этот заголовок служит той же цели, что и ETag, но использует стратегию на основе времени, чтобы определить, изменился ли ресурс, в отличие от стратегии на основе контента ETag.

Некоторые веб-серверы по умолчанию поддерживают настройку этих заголовков, а другие полностью исключают их, если вы не настроите их явным образом. Способ настройки заголовков зависит от используемого веб-сервера. Подробные инструкции можно найти в документации к нему.

Ниже приведены инструкции по настройке некоторых популярных веб-серверов.

Если вы не добавите заголовок ответа Cache-Control, кеширование HTTP не будет отключено. Вместо этого браузеры пытаются угадать, какой тип кеширования наиболее подходит для того или иного контента. Скорее всего, вам потребуется больше контроля, поэтому уделите время настройке заголовков ответов.

Какие значения заголовка ответа следует использовать?

При настройке заголовков ответа веб-сервера необходимо учитывать два важных сценария.

Длительное кеширование для URL с версиями

Как URL с версиями могут помочь вашей стратегии кеширования
URL с версиями – это хорошая практика, поскольку они упрощают аннулирование кешированных ответов.

Предположим, что ваш сервер указывает браузерам кешировать файл CSS в течение одного года (Cache-Control: max-age=31536000), но ваш дизайнер только что внес срочное обновление, которое вам нужно немедленно развернуть. Как сообщить браузерам, что нужно обновить устаревшую кешированную копию файла? Нет, если не изменить URL ресурса.

После того как браузер кеширует ответ, кешированная версия используется до тех пор, пока она не перестанет быть актуальной, что определяется по max-age или expires, или пока она не будет удалена из кеша по какой-либо другой причине, например если пользователь очистит кеш браузера. В результате разные пользователи могут использовать разные версии файла при создании страницы: пользователи, которые только что получили ресурс, используют новую версию, а пользователи, которые кэшировали более раннюю (но все еще действительную) копию, используют более старую версию ответа.

Как получить лучшее из двух миров: клиентское кеширование и быстрые обновления? Вы меняете URL ресурса и заставляете пользователя скачивать новый ответ каждый раз, когда меняется его контент. Обычно для этого в название файла добавляют цифровой отпечаток или номер версии, например style.x234dff.css.

При ответе на запросы URL, которые содержат цифровой отпечаток или информацию об управлении версиями и контент которых не должен меняться, добавляйте в ответы Cache-Control: max-age=31536000.

Это значение сообщает браузеру, что если ему потребуется загрузить тот же URL в течение следующего года (31 536 000 секунд – максимальное допустимое значение), он может сразу использовать значение в кеше HTTP, не отправляя сетевой запрос на ваш веб-сервер. Это здорово – вы сразу же получили надежность и скорость, которые дает отказ от сети!

Инструменты сборки, такие как webpack, могут автоматизировать процесс назначения хеш-отпечатков URL объектов.

Повторная проверка сервером URL без версий

К сожалению, не все URL, которые вы загружаете, имеют версии. Возможно, вы не можете включить этап сборки перед развертыванием веб-приложения, поэтому не можете добавить хеши в URL объектов. И каждому веб-приложению нужны HTML-файлы – эти файлы (почти!) никогда не будут содержать информацию об управлении версиями, поскольку никто не станет использовать ваше веб-приложение, если им нужно будет помнить, что URL для посещения – https://example.com/index.34def12.html. Что можно сделать в этом случае?

Это один из сценариев, в котором вам нужно признать поражение. Одного кеширования HTTP недостаточно, чтобы полностью избежать использования сети. Не волнуйтесь, скоро вы узнаете о сервис-воркерах, которые помогут вам победить в этой битве. Но есть несколько шагов, которые вы можете предпринять, чтобы сетевые запросы выполнялись как можно быстрее и эффективнее.

Следующие значения Cache-Control помогут вам настроить, где и как кешируются URL без версий:

  • no-cache. Это указывает браузеру, что он должен каждый раз выполнять повторную проверку на сервере, прежде чем использовать кешированную версию URL.
  • no-store. Это указывает браузеру и другим промежуточным кешам (например, CDN), что не нужно хранить никакие версии файла.
  • private. Браузеры могут кешировать файл, а промежуточные кеши – нет.
  • public. Ответ может храниться в любом кеше.

Чтобы понять, какие значения Cache-Control использовать, ознакомьтесь с приложениемCache-Control. В Cache-Control также можно указать список директив, разделенных запятыми. См. Приложение. Примеры Cache-Control.

Также можно задать значение ETag или Last-Modified. Как упоминалось в разделе Заголовки ответов, ETag и Last-Modified служат одной цели: определить, нужно ли браузеру повторно скачивать кешированный файл, срок действия которого истек. Мы рекомендуем использовать ETag, поскольку это более точный вариант.

Пример ETag

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

Эту проблему решают ключи доступа, указанные в заголовке ETag. Сервер генерирует и возвращает произвольный токен, который обычно представляет собой хеш или другой отпечаток содержимого файла. Браузеру не нужно знать, как генерируется цифровой отпечаток. Ему нужно только отправить его на сервер при следующем запросе. Если отпечаток не изменился, значит, ресурс остался прежним и браузер может пропустить скачивание.

Если задать заголовок ETag или Last-Modified, то запрос на повторную проверку будет выполняться намного эффективнее, поскольку он сможет активировать заголовки запроса If-Modified-Since или If-None-Match, упомянутые в разделе Заголовки запросов.

Когда правильно настроенный веб-сервер видит эти заголовки входящих запросов, он может подтвердить, соответствует ли версия ресурса, которая уже есть у браузера в HTTP-кеше, последней версии на веб-сервере. Если совпадение есть, сервер может отправить HTTP-ответ 304 Not Modified, который означает, что можно продолжать использовать уже имеющиеся данные. При отправке такого ответа передается очень мало данных, поэтому он обычно обрабатывается намного быстрее, чем запрос на отправку копии ресурса.

Визуализация клиента, запрашивающего ресурс, и сервера, отвечающего заголовком 304.
Браузер запрашивает /file с сервера и включает заголовок If-None-Match, чтобы указать серверу возвращать полный файл, только если ETag файла на сервере не соответствует значению If-None-Match браузера. В этом случае значения совпали, поэтому сервер возвращает ответ 304 Not Modified с инструкциями о том, как долго файл должен храниться в кеше (Cache-Control: max-age=120).

Сводка

Кеш HTTP позволяет повысить скорость загрузки, поскольку сокращает количество ненужных сетевых запросов. Он поддерживается во всех браузерах и не требует сложной настройки.

Рекомендуем начать со следующих конфигураций Cache-Control:

  • Cache-Control: no-cache для ресурсов, которые должны повторно проверяться на сервере перед каждым использованием.
  • Cache-Control: no-store для ресурсов, которые никогда не должны кешироваться.
  • Cache-Control: max-age=31536000 для ресурсов с версиями.

А заголовок ETag или Last-Modified поможет вам более эффективно повторно проверять ресурсы кеша с истекшим сроком действия.

Подробнее…

Если вы хотите узнать больше о заголовке Cache-Control, ознакомьтесь с руководством Джейка Арчибальда (Jake Archibald) по кешированию и использованию директивы max-age.

О том, как оптимизировать использование кеша для вернувшихся посетителей, читайте в статье Love your cache (Полюбите кеш).

Приложение. Дополнительные советы

Если у вас есть больше времени, вот дополнительные способы оптимизации использования HTTP-кеша:

  • Используйте единообразные URL. Если вы разместите один и тот же контент по разным URL, он будет извлечен и сохранен несколько раз.
  • Минимизируйте отток пользователей. Если часть ресурса (например, файл CSS) обновляется часто, а остальная часть (например, код библиотеки) – нет, рекомендуем разделить часто обновляемый код на отдельный файл и использовать стратегию краткосрочного кеширования для часто обновляемого кода и стратегию долгосрочного кеширования для кода, который меняется редко.
  • Если в правилах Cache-Control допускается определенная степень устаревания, ознакомьтесь с новой директивой stale-while-revalidate.

Приложение: блок-схема Cache-Control

Блок-схема
Процесс принятия решений по настройке заголовков Cache-Control.

Приложение. Cache-Control примеров

На сумму Cache-Control Описание
max-age=86400 Ответ может храниться в кеше браузеров и промежуточных кешах до одного дня (60 секунд x 60 минут x 24 часа).
private, max-age=600 Ответ может храниться в кеше браузера (но не в промежуточных кешах) до 10 минут (60 секунд x 10 минут).
public, max-age=31536000 Ответ может храниться в любом кеше в течение одного года.
no-store Ответ не может быть кеширован и должен полностью извлекаться при каждом запросе.