Большая часть страницы сайта состоит из файлов, которые не меняются от визита к визиту: логотип, шрифты, стили CSS, библиотеки JavaScript, фотографии товаров. Если сервер не сообщает браузеру, как долго их хранить, браузер запрашивает их снова или как минимум проверяет, не изменились ли они. При правильных заголовках кеширования повторный визит обходится без обращения к сети за этими файлами: они берутся с диска, и страница открывается заметно быстрее.
Как работает кеш браузера
Сервер в заголовке Cache-Control указывает, сколько времени файл считается свежим: например, Cache-Control: max-age=2592000 означает 30 дней. В течение этого срока браузер файл не запрашивает. Когда срок истекает, браузер отправляет условный запрос с прежним значением ETag в заголовке If-None-Match или с датой в If-Modified-Since. Если файл не изменился, сервер возвращает короткий ответ 304 Not Modified, и файл заново не скачивается.
Старый заголовок Expires делает похожую работу, но указывает конкретную дату. Если присутствуют оба, современные браузеры отдают приоритет Cache-Control. Директива expires в Nginx удобна тем, что выставляет сразу оба заголовка.
Главная проблема: что будет при обновлении файла
Единственный риск долгого кеширования в том, что после изменения CSS браузер пользователя продолжит показывать старую копию до окончания срока. Стандартное решение — версионирование имён файлов. Vite, Webpack и подобные инструменты добавляют в имя хеш содержимого, например app.3f9a2c.css. Изменился файл — изменилось имя, HTML ссылается на новое имя, и браузер скачивает новый файл. Такие файлы можно кешировать на год с пометкой immutable.
Если сборщик не используется, простой способ — параметр в адресе: style.css?v=12. WordPress, например, сам добавляет параметр версии к стилям и скриптам. А вот саму HTML-страницу надолго кешировать нельзя, иначе пользователь не увидит новые имена файлов.
Настройка Nginx
Следующие блоки добавляются в секцию server конфигурации сайта и задают разный срок кеширования в зависимости от расширения:
# Версионированные CSS и JS: год, неизменяемые
location ~* \.(?:css|js)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# Изображения и шрифты: 30 дней
location ~* \.(?:png|jpe?g|gif|webp|avif|svg|ico|woff2?)$ {
expires 30d;
add_header Cache-Control "public";
access_log off;
}
# HTML: проверять каждый раз
location ~* \.html$ {
expires -1;
}
expires 1y добавляет дату Expires и Cache-Control: max-age=31536000. add_header добавляет ещё одну строку Cache-Control, и браузеры объединяют обе. expires -1 заставляет проверять HTML при каждом запросе. Отключение журнала доступа для статики (access_log off) уменьшает запись на диск и делает логи читабельнее.
Ловушка с add_header
В Nginx есть важная тонкость: если во вложенном блоке location указан хотя бы один add_header, все add_header верхнего уровня (из блока server или http) для этого блока не наследуются. Например, заголовки безопасности (X-Content-Type-Options, Strict-Transport-Security), заданные на уровне сервера, пропадут из ответов на картинки и CSS. В таком случае нужные заголовки повторяют в каждом location или выносят в отдельный файл и подключают через include.
Собственный кеш сервера: open_file_cache
Помимо кеша браузера, Nginx умеет держать в памяти сведения о часто запрашиваемых файлах (дескрипторы, размер, время изменения). На сайтах с большим количеством статики это сокращает обращения к диску. Настройка добавляется в блок http:
open_file_cache max=10000 inactive=60s;
open_file_cache_valid 120s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
Эта настройка кеширует не содержимое файлов, а только их метаданные. За быструю отдачу содержимого отвечают страничный кеш операционной системы и директива sendfile on;, которые обычно включены по умолчанию.
Пример: сайт частной клиники в Намангане
На сайте частной клиники в Намангане есть фотографии врачей, список услуг, форма записи на приём и карта. Пациенты возвращаются на сайт часто: проверить время приёма, посмотреть цены или найти адрес. Без заголовков кеширования при каждом визите изображения и скрипты загружаются заново. После того как для изображений задано 30 дней, а для версионированных CSS и JavaScript год, при повторном визите браузер берёт с сервера только HTML, а всё остальное открывается из кеша. Обновляя стили, администратор увеличивает номер версии, и пациенты сразу видят новый дизайн.
На виртуальном хостинге: через .htaccess
Если сайт размещён на виртуальном хостинге с Apache и доступа к настройкам сервера нет, того же результата можно добиться модулем mod_expires. Правила записываются в файл .htaccess в корне сайта:
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType text/css "access plus 1 year"
ExpiresByType application/javascript "access plus 1 year"
ExpiresByType image/webp "access plus 30 days"
ExpiresByType image/jpeg "access plus 30 days"
ExpiresByType image/png "access plus 30 days"
ExpiresByType font/woff2 "access plus 30 days"
ExpiresByType text/html "access plus 0 seconds"
</IfModule>
На многих серверах виртуального хостинга статику отдаёт напрямую Nginx, а Apache обрабатывает только PHP-запросы. В такой схеме правила .htaccess могут не действовать на статические файлы. Поэтому обязательно проверьте результат способом ниже, а при необходимости уточните в технической поддержке sayt.uz, как настроено кеширование на вашем тарифе.
Проверка результата
Самый надёжный способ — посмотреть заголовки ответа через curl:
curl -sI https://example.uz/assets/app.3f9a2c.css | grep -iE "cache-control|expires|etag"
curl -sI https://example.uz/ | grep -i cache-control
Первая команда должна показать долгий max-age, вторая — короткий или нулевой срок для HTML. В инструментах разработчика на вкладке Network при повторной загрузке файлы из кеша помечаются как «disk cache» или «memory cache». Если используется CDN вроде Cloudflare, помните, что у него свои правила кеширования и он может менять заголовки.