После деплоя пользователь видит старый CSS: как настроить версии ассетов
После деплоя разработчик видит новую верстку, а пользователь продолжает видеть старый CSS. В админке всё обновили, файлы на сервере свежие, но у части людей кнопки съехали, стили не применились или JavaScript ждёт другой HTML. Чаще всего причина в кэше браузера, CDN или серверных заголовках.
Проблема решается не просьбой “обновите страницу с Ctrl+F5”, а нормальной версионизацией ассетов.
Почему браузер берёт старый файл
Если CSS подключается одним и тем же URL, браузер имеет право использовать кэшированную копию.
<link rel="stylesheet" href="/css/app.css">
Если на сервере файл изменился, но URL тот же, пользователь может какое-то время видеть старый app.css. Особенно если настроен долгий Cache-Control.
Версия в query string
Простой способ — добавить версию в URL.
<link rel="stylesheet" href="/css/app.css?v=20260618">
После изменения версии браузер воспринимает это как новый ресурс. Такой вариант прост и подходит для многих небольших проектов.
Hash в имени файла
Более надёжный подход — включать hash содержимого в имя файла.
/assets/app.8f3a21.css
/assets/app.8f3a21.js
Когда файл меняется, меняется hash и URL. Старый файл может спокойно кэшироваться долго, потому что новый релиз подключает новый URL.
Cache-Control
Для файлов с hash можно ставить долгий кэш.
location ~* \.(css|js|png|jpg|jpeg|webp|svg|woff2)$ {
expires 30d;
add_header Cache-Control "public";
}
Для файлов без версии слишком долгий кэш опасен. Пользователь может долго получать старый CSS.
CDN
Если перед сайтом стоит CDN, обновление файла на сервере ещё не означает обновление у пользователя. Нужно проверить кэш CDN и правила purge.
- есть ли CDN перед доменом;
- кэширует ли он CSS и JS;
- как очищается кэш после релиза;
- учитывает ли CDN query string;
- не отдаёт ли CDN старую версию по edge cache.
Порядок деплоя
Если HTML уже подключает новый файл, а сам файл ещё не загружен, пользователи получат 404 на CSS. Если файл заменили, а HTML ещё старый, будет обратная проблема. Поэтому порядок деплоя важен.
При hash-файлах обычно сначала загружают новые ассеты, потом переключают HTML или релизный symlink.
Смешивание старого JS и нового HTML
Особенно неприятная ситуация — HTML обновился, а JS остался старым. Тогда скрипт ищет старые селекторы или отправляет старый формат AJAX. Поэтому версионировать нужно не только CSS, но и JS.
<script src="/js/app.js?v=20260618"></script>
Как проверить
В браузере нужно открыть Network и посмотреть фактический URL CSS, HTTP-код, размер, заголовки Cache-Control и Age, если есть CDN.
curl -I https://example.ru/css/app.css
Если в заголовках стоит долгий max-age, а URL не меняется между релизами, проблема закономерна.
Чек-лист
- Проверить фактический URL CSS и JS в HTML.
- Проверить Cache-Control и expires.
- Добавить version query или hash в имя файла.
- Версионировать CSS и JS вместе.
- Проверить CDN и правила purge.
- Не ставить долгий кэш на неверсионированные файлы.
- Проверить порядок деплоя ассетов и HTML.
- После релиза проверить страницу в обычном браузере, не только с отключенным кэшем.
Кэш ассетов должен быть предсказуемым. Пользователь не обязан знать, как сбросить кэш браузера. Если URL файла меняется при изменении содержимого, старый CSS после деплоя перестаёт быть случайной проблемой.