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

Илья Григорик
Илья Григорик
Джефф Посник
Джефф Посник

Получение ресурсов по сети является медленным и дорогостоящим процессом:

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

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

В этом руководстве показаны основы эффективной реализации HTTP-кэширования.

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

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

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

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

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

Более подробный концептуальный обзор можно найти в статье HTTP-кэширования MDN.

Заголовки запросов: придерживайтесь значений по умолчанию (обычно).

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

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

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

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

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

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

Чтобы сэкономить вам время на поиски, вот инструкции по настройке нескольких популярных веб-серверов:

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

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

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

Долговременное кэширование для версионных URL-адресов.

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

Предположим, ваш сервер инструктирует браузеры кэшировать CSS-файл на 1 год ( 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 . https://example.com/index.34def12.html . Итак, что вы можете сделать для этих URL-адресов?

Это один из сценариев, в котором вам нужно признать поражение. 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 , ознакомьтесь с рекомендациями Джейка Арчибальда по кэшированию и руководством по ошибкам максимального возраста .

См. раздел «Любите свой кэш» , чтобы узнать, как оптимизировать использование кэша для повторных посетителей.

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

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

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

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

Блок-схема

Приложение: примеры Cache-Control

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