Как кэшировать шрифты через Service Worker без устаревших файлов

Кэшировать шрифты через Service Worker безопасно, если разделить кэши по назначению, использовать версионированные имена и удалять старые записи в событии activate. Для обновления достаточно выпустить новую версию кэша, дождаться активации нового обработчика и проверить, что CSS и файлы WOFF2 загружаются согласованно. Ниже — практическая схема без устаревших ресурсов.
Почему старые шрифты остаются в кэше
Service Worker может сохранять ответы сети в Cache Storage и затем отдавать их без нового запроса. Проблема возникает, когда файл шрифта заменили на сервере, но его имя осталось прежним. Кэш уже содержит старую копию, поэтому стратегия cache first продолжает возвращать ее пользователям. Очистка истории браузера не является надежным способом обновления: она зависит от действий конкретного человека.
У веб-шрифта обычно есть несколько связанных ресурсов: таблица стилей CSS, файлы WOFF2 или WOFF, иногда разные начертания и подмножества символов. Если CSS обновился, а Service Worker отдает старый файл шрифта, браузер может загрузить несовместимое начертание или продолжить использовать прежнюю версию. Поэтому кэшировать нужно не отдельный файл изолированно, а согласованный набор ресурсов.
Базовая архитектура кэширования веб-шрифтов
Для проекта удобно создать отдельный кэш только для шрифтов, например fonts-v3, и не смешивать его с HTML, изображениями или JavaScript. При выпуске новой версии меняется имя кэша: fonts-v4. В обработчике activate Service Worker получает список доступных кэшей и удаляет старые имена. Такой подход делает обновление явным и не требует удаления каждого файла по отдельности.
- Выделите отдельный кэш для шрифтов и таблиц стилей, если они обновляются вместе.
- Используйте стабильные имена файлов с хешем содержимого, например название начертания с суффиксом хеша.
- Ограничьте обработчик шрифтов запросами GET и успешными ответами.
- Удаляйте устаревшие версии кэша в activate, а не во время каждого запроса.
- Проверяйте, что CSS ссылается на ту же версию файлов, которую обслуживает приложение.
Пошаговая настройка Service Worker
Шаг 1. Определите набор ресурсов
Сначала составьте список реально используемых файлов: например, regular, medium и bold в формате WOFF2, а также CSS с правилами @font-face. Не добавляйте в предварительный кэш все файлы из папки fonts. Лишние начертания увеличивают размер хранилища и усложняют обновление. Если сайт поддерживает только латиницу, отдельное кириллическое подмножество не должно загружаться без необходимости.
Шаг 2. Введите версионирование
Самый надежный вариант — менять имя файла при изменении его содержимого: например, regular.a81c2.woff2 вместо постоянного regular.woff2. Сборщик проекта обычно добавляет хеш автоматически. Если такой возможности нет, меняйте имя Cache Storage при каждом релизе и следите, чтобы старый CSS не оставался доступным из другого кэша.
Версия кэша и хеш имени решают разные задачи. Имя кэша позволяет массово удалить старую группу ресурсов, а хеш файла предотвращает подмену содержимого под прежним URL. Для небольшого сайта достаточно версий кэша, но для постоянных релизов лучше сочетать оба механизма.
Шаг 3. Предварительно сохраните критические шрифты
В событии install откройте текущий кэш и добавьте только ресурсы, необходимые для первого экрана: основной файл CSS и одно-два начертания, которые используются в заголовке и основном тексте. Метод предварительного кэширования должен завершаться успешно только после сохранения всех обязательных файлов. Если один критический ресурс недоступен, лучше не считать установку полностью готовой.
Предварительный кэш подходит для небольшого фиксированного набора. Динамические начертания, которые встречаются только на отдельных страницах, можно сохранять при первом обращении пользователя. Это снижает размер первоначальной установки и не заставляет браузер скачивать редкие варианты шрифта заранее.
Шаг 4. Обработайте запросы к шрифтам
В событии fetch отфильтруйте запросы по назначению font или по расширениям woff2 и woff. Сначала проверьте кэш, а при отсутствии записи выполните сетевой запрос. Успешный ответ сохраните в отдельный кэш и верните странице. Нельзя кэшировать POST-запросы, ответы с ошибками и случайные ресурсы, не относящиеся к шрифтам.
Для шрифтов, которые редко меняются и имеют хеш в имени, cache first обычно дает предсказуемый результат: после первого скачивания браузер использует локальную копию. Для файлов с постоянным именем безопаснее применять network first или stale-while-revalidate, чтобы новая версия могла попасть в кэш без ручной очистки.
Шаг 5. Удалите старые версии в activate
В событии activate получите имена кэшей, оставьте текущие fonts-v4 и другие актуальные категории, а все прежние версии удалите через Cache Storage. Очистка должна выполняться асинхронно до завершения активации. Не удаляйте кэши, которыми управляют другие приложения или независимые Service Worker: используйте уникальный префикс проекта.
Шаг 6. Учтите жизненный цикл Service Worker
Новая версия Service Worker сначала устанавливается отдельно от активной. Пока обновление не активировалось, открытая вкладка может продолжать работать со старым обработчиком. Не включайте skipWaiting и clientsClaim без проверки: мгновенная смена обработчика способна смешать старый HTML с новым JavaScript или CSS. Если проекту нужна немедленная активация, предусмотрите согласованный релиз всех ресурсов.
Практический сценарий обновления шрифта
Представим сайт, где основное начертание хранится в файле regular.a81c2.woff2, а текущий кэш называется fonts-v3. После изменения межбуквенного расстояния или набора символов сборка создает regular.f42d7.woff2. Service Worker получает имя fonts-v4, CSS начинает ссылаться на новый файл, а activate удаляет fonts-v3 после установки новой версии.
- Соберите новый файл шрифта и проверьте его открытие в браузере.
- Обновите ссылку в CSS и убедитесь, что имя файла совпадает с результатом сборки.
- Измените версию кэша в Service Worker.
- Опубликуйте Service Worker, CSS и шрифт как согласованный набор.
- Откройте новую вкладку и проверьте активацию новой версии.
- Удалите старую копию только после подтверждения, что новый файл успешно загружен.
Как сочетать Service Worker и HTTP-кэш
Service Worker и HTTP-кэш браузера работают на разных уровнях, поэтому изменение только Cache Storage не всегда объясняет результат проверки. Для файлов с хешем в имени можно использовать длительное кеширование: имя меняется вместе с содержимым. Для постоянного имени задайте короткую проверку актуальности или убедитесь, что Service Worker явно выбирает новую версию.
Не стоит одновременно бесконтрольно продлевать срок жизни HTTP-кэша и использовать cache first для файлов без версионирования. В такой схеме старый ресурс может сохраняться и в HTTP-кэше, и в Cache Storage. Версионированные URL упрощают правила: старый файл остается доступным для уже открытой страницы, а новая страница запрашивает новый адрес.
Кросс-доменные шрифты и заголовки ответа
Если шрифты размещены на CDN или другом поддомене, сервер должен разрешать нужный источник через CORS. Браузер проверяет правила доступа к шрифту отдельно от обычного изображения или скрипта. Service Worker не отменяет ограничения безопасности: сохранение ответа в кэш не исправляет неверный Access-Control-Allow-Origin и не заменяет корректную конфигурацию CDN.
Проверьте MIME-тип, код ответа и отсутствие неожиданных перенаправлений. Для шрифта обычно ожидается успешный ответ с типом, соответствующим формату файла. Если CDN возвращает HTML-страницу ошибки с кодом успеха, Service Worker может сохранить ее как будто это шрифт, а браузер сообщит о невозможности декодировать ресурс.
Частые ошибки при кэшировании шрифтов
- Оставлять неизменное имя кэша. Новые файлы не вытесняют старые автоматически, если стратегия всегда выбирает кэш.
- Кэшировать ответ без проверки статуса. Страница ошибки или пустой ответ могут сохраниться вместо WOFF2.
- Удалять только один известный файл. В старом кэше могут остаться другие начертания и CSS, поэтому надежнее удалять всю версию.
- Использовать разные версии CSS и шрифта. Несогласованный набор приводит к неверным ссылкам и неожиданному fallback.
- Забывать о кросс-доменном доступе. Service Worker не отменяет требования CORS для шрифтов на CDN.
- Принудительно активировать новую версию без плана совместимости. Открытая вкладка может получить смесь ресурсов разных релизов.
- Предварительно кэшировать всю папку fonts. Это увеличивает загрузку установки и сохраняет ненужные начертания.
- Проверять только обычную перезагрузку. Для диагностики нужно отдельно смотреть Cache Storage, сетевые запросы и активный Service Worker.
Чеклист проверки перед публикацией
Проверяйте обновление не только в рабочем браузере разработчика, но и в чистой вкладке. В Chrome и совместимых инструментах DevTools откройте раздел Application, найдите Cache Storage и убедитесь, что в текущем кэше лежат нужные файлы. В Network проверьте, откуда пришел ответ: из ServiceWorker, дискового кэша или сети.
- Проверьте работу сайта по HTTPS или на локальном адресе разработки.
- Убедитесь, что область действия Service Worker включает страницы с подключением шрифтов.
- Проверьте совпадение имен файлов в CSS и в опубликованной папке.
- Откройте страницу без сети и убедитесь, что критический шрифт доступен.
- Измените файл шрифта и выпустите новую версию кэша.
- Проверьте, что старый кэш удалился после activate.
- Посмотрите ответы шрифтов в Network и их HTTP-статусы.
- Проверьте fallback, если файл временно недоступен или отключен.
Часто задаваемые вопросы
Можно ли кэшировать шрифты только через Service Worker?
Да, Service Worker может сохранять ответы шрифтов в Cache Storage и отдавать их при последующих запросах. Однако HTTP-заголовки кэширования, CORS и корректные MIME-типы все равно важны. Service Worker дополняет стандартное кэширование браузера, а не отменяет сетевые правила.
Как обновить шрифт без очистки кэша пользователем?
Измените имя файла через хеш содержимого или увеличьте версию кэша Service Worker. Новый CSS должен ссылаться на новый файл, а обработчик activate — удалить старую группу ресурсов. Пользователю не потребуется вручную очищать данные сайта, если новая версия корректно установилась и активировалась.
Что лучше для шрифтов: cache first или network first?
Для неизменяемых файлов с хешем в имени обычно подходит cache first: ресурс быстро берется локально, а новая версия получает новый адрес. Для файлов с постоянным именем безопаснее network first или stale-while-revalidate. Выбор зависит от того, как часто меняется файл и насколько критична офлайн-доступность.
Нужно ли кэшировать WOFF и WOFF2 одновременно?
Нет, если браузеры и сценарии поддержки проекта позволяют использовать только WOFF2. Два формата нужны лишь тогда, когда есть обоснованная потребность в совместимости. Каждый дополнительный файл увеличивает размер кэша и требует синхронного обновления имени, CSS и правил Service Worker.
Почему шрифт не загружается после добавления Service Worker?
Проверьте область действия Service Worker, путь к файлу, статус ответа, MIME-тип и CORS для другого домена. Затем убедитесь, что обработчик fetch действительно распознает запрос как font и не сохраняет ошибочный ответ. В DevTools полезно временно отключить кэш и очистить только тестовые записи Cache Storage.
Можно ли использовать query-параметр для версии шрифта?
Технически адрес с query-параметром может создать отдельную запись, но такой подход сложнее контролировать: разные варианты URL способны накопиться в кэше. Для долгоживущих ресурсов предпочтительнее менять имя файла и удалять старые версии кэша. Если query-параметры необходимы, заранее определите правила очистки и сопоставления запросов.
Вывод
Надежное кэширование шрифтов через Service Worker строится на трех принципах: версионированные ресурсы, отдельный кэш и очистка старых записей в activate. Добавьте проверку сетевых ответов, корректный CORS для CDN и тестирование жизненного цикла Service Worker — тогда обновление шрифтов будет предсказуемым и для онлайн-, и для офлайн-сценариев.