Статический контент сайта: какие файлы относятся к статике и как их организовать | AdminWiki

Статический контент сайта: какие файлы относятся к статике и как их организовать

22 сентября 2026 12 мин. чтения
Содержание статьи

Что такое статический контент сайта и что к нему относится

Статический контент - это файлы, которые сервер отдаёт клиенту без генерации на каждый запрос. Он читает их с диска или из кэша, добавляет HTTP-заголовки и отправляет браузеру. Запрос к /static/css/main.css возвращает одинаковые байты до тех пор, пока файл не изменился. Отсюда два практических следствия: статику просто кэшировать, а её отдача почти не расходует CPU сервера.

В состав статики на типовом сайте входят восемь групп файлов:

  • готовые HTML-страницы;
  • CSS-файлы, исходные и минифицированные;
  • JavaScript-файлы и собранные бандлы;
  • изображения: PNG, JPEG, WebP, AVIF, SVG, ICO;
  • медиа: MP4, WebM, MP3, OGG, WAV;
  • документы: PDF, DOCX, XLSX, CSV;
  • шрифты: WOFF, WOFF2, TTF, OTF, EOT;
  • служебные файлы: robots.txt, sitemap.xml, favicon.ico, manifest.json, .htaccess, web.config, humans.txt, security.txt.

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

HTML, CSS и JavaScript: базовый набор статики

Готовый HTML-файл - статика. Файл index.html лежит на диске и отправляется как есть. Шаблон index.twig, index.blade.php или index.jinja2 к статике не относится: его обрабатывает шаблонизатор при каждом запросе, подставляя переменные и выполняя условия.

Тот же принцип для стилей и скриптов. Файл static/css/style.css, который уходит браузеру, - статика. Исходник src/scss/style.scss - нет: его компилируют в CSS на этапе сборки. Минифицированные версии вроде style.min.css тоже относятся к статике, если сборщик подготовил их заранее и сервер отдаёт готовый файл.

Когда страницы собирают заранее (подход SSG), HTML попадает в ту же категорию: сервер отдаёт подготовленный файл, а не собирает страницу из шаблона. Как выбирать между предварительной сборкой, серверным и клиентским рендерингом, разобрано в руководстве по стратегиям рендеринга.

Изображения, медиа, документы и шрифты

Эти файлы хранятся на диске целиком и отдаются как есть, без преобразования на лету:

  • изображения: PNG, JPEG, WebP, AVIF, ICO, SVG;
  • видео: MP4, WebM;
  • аудио: MP3, OGG, WAV;
  • документы: PDF, DOCX, XLSX, CSV;
  • шрифты: WOFF, WOFF2, TTF, OTF, EOT.

Современные форматы сжатия уменьшают вес изображения: по данным сравнений, WebP и AVIF весят на 25–50% меньше JPG при том же качестве, а AVIF может сжимать картинку на 20–40% эффективнее WebP при равном качестве. Поэтому для одной картинки часто держат несколько форматов и подключают их через тег picture: браузер сам выбирает первый поддерживаемый формат, обычно сначала предлагают AVIF, затем WebP, а JPG оставляют как универсальный запасной вариант. Автоматический выбор формата по заголовку Accept в проверенных материалах не подтверждён, поэтому на него лучше не опираться как на основной механизм.

SVG занимает пограничное положение: как отдельный файл icon.svg он статика, а инлайн-вставка того же SVG в разметку становится частью HTML-документа и кэшируется вместе с ним.

Служебные файлы: robots.txt, sitemap.xml, favicon и другие

Служебные файлы обычно лежат в корне сайта и тоже отдаются статикой: robots.txt, sitemap.xml, favicon.ico, manifest.json, humans.txt, security.txt. Конфигурации .htaccess и web.config попадают в эту же группу, хотя их читает веб-сервер, а не браузер.

sitemap.xml бывает обоих типов. На статическом сайте это подготовленный файл, а в CMS его часто генерирует модуль по расписанию. Если карта сайта собирается на каждый запрос, к статике она не относится.

Как отделить статику от шаблонов и пользовательских загрузок

Три категории файлов живут на одном сервере, но требуют разного обращения. Путаница между ними приводит к потере данных при деплое и к неверному кэшированию.

КатегорияТипичные путиКто меняет содержимоеКэширование
Статикаpublic/static/, public/assets/Разработчик, через релизДолгое, с версией в имени файла
Шаблоныtemplates/, views/, themes/Разработчик, через релизНе кэшируется, обрабатывается на сервере
Пользовательские загрузкиuploads/, media/, /mnt/data/uploads/Пользователи, API, админкаОсторожное, отдельный бэкап

Шаблоны: почему они не статика

Шаблон содержит переменные, циклы и условия, а обрабатывает его шаблонизатор (Twig, Blade, Jinja2, Handlebars) на стороне сервера. Результат обработки - HTML, который уходит в ответ. Сам файл шаблона браузер не получает никогда.

Практический критерий простой: если файл лежит в templates/ или views/ и содержит управляющие конструкции, он не статика. Некоторые CMS кэшируют результат рендеринга в HTML-файл, и такая копия ведёт себя как статика: отдаётся напрямую, пока не истечёт срок жизни кэша.

Пользовательские загрузки: почему их нельзя смешивать со статикой

Загрузки приходят через формы, API и админку: аватары, вложения, изображения товаров. Их содержимое меняется без участия разработчика, и перезаписывать их при релизе нельзя.

Каталог static/uploads/ опасен: при деплое, который заменяет весь static/, файлы пользователей исчезнут. Безопаснее хранить их отдельно, например в var/uploads/, /mnt/data/uploads/ или в объектном хранилище. Разделение даёт три эффекта: деплой не затрагивает данные, бэкап загрузок идёт по своему расписанию, а права на запись выдаются только каталогу загрузок.

Типовая структура каталогов статики сайта

Рабочая схема отвечает на один вопрос: что видит веб-сервер, а что остаётся в репозитории. Корень сайта (обычно public/) содержит только то, что должно быть доступно по HTTP. Исходники, конфигурации сборки и пользовательские загрузки лежат вне него.

Базовый вариант: public/static с подкаталогами

Минимальная структура, которую можно применить сразу:

public/
  index.html
  robots.txt
  sitemap.xml
  favicon.ico
  static/
    css/
    js/
    img/
    fonts/
    media/
    docs/
    vendor/

Каталог vendor/ отведён под сторонние библиотеки: bootstrap, jquery, шрифтовые иконочные наборы. Свои файлы держат рядом, но отдельно, иначе обновление библиотеки затирает собственную вёрстку. Каталог docs/ хранит PDF и другие документы, media/ - видео и аудио. Такая схема подходит статическим сайтам и небольшим проектам без сборщика.

Вариант для сборки: src/ и dist/

Когда есть сборщик, исходники и результат разводят по разным каталогам:

src/
  scss/style.scss
  js/app.js
  img/hero.png

dist/
  css/style.min.css
  js/app.min.js
  img/hero.webp

Веб-сервер отдаёт только dist/, а src/ хранится в git и на продакшен не копируется. Каталог dist/ пересоздают при каждом деплое, поэтому руками его не правят: правки, внесённые прямо в него, потеряются при следующей сборке. Каталог dist/ удобно держать в .gitignore, чтобы не смешивать артефакты с исходниками.

Вариант для CMS: отделение статики от шаблонов и загрузок

В WordPress стили и скрипты темы обычно лежат в wp-content/themes/имя_темы/assets/, файлы шаблонов - в wp-content/themes/имя_темы/templates/, загрузки - в wp-content/uploads/. Плагины приносят собственную статику в своих каталогах.

В Drupal статика темы и модулей лежит внутри их каталогов, а загрузки - в sites/default/files/. Битрикс хранит статику шаблона в local/templates/ или bitrix/templates/, а загрузки - в upload/.

Частая ошибка в CMS: изображения из редактора складывают в assets/ темы, рядом с файлами вёрстки. Тогда очистка assets/ перед релизом уносит пользовательский контент, который восстановить из репозитория не получится.

Правила именования и версионирования статических файлов

Имена файлов читаются в логах, в конфигурациях nginx, в скриптах деплоя и в коде. Предсказуемые имена экономят время при разборе инцидентов.

Правила именования: регистр, разделители, запрещённые символы

Рабочее правило для команды: латиница, цифры, дефис и подчёркивание, все буквы в нижнем регистре. Пробелы заменяют дефисом. Кириллица в именах создаёт проблемы при переносе между файловыми системами и при кодировании в URL.

Хорошие имена: main-style.css, hero-image.webp, icon-search.svg, guide-2026-09-22.pdf. Плохие: file1.css, new.css, final.css, final-final2.css, Стили.css, MainStyle.CSS. Отдельно стоит отказаться от дат вида 12.09.2026 в имени: точки и порядок частей ломают сортировку. Формат ГГГГ-ММ-ДД сортируется как обычная строка.

Cache busting: зачем добавлять хеш в имя файла

Браузер кэширует файл по URL. Если URL не меняется, браузер отдаёт сохранённую копию, и пользователь видит старую вёрстку после обновления сайта. Решение - менять URL вместе с содержимым.

Способов два. Первый: параметр версии, /css/style.css?v=2. Второй: хеш содержимого в имени, style.a1b2c3.css. Хеш надёжнее: он меняется только при реальном изменении файла, а не при каждом релизе, поэтому кэш не сбрасывается зря. Webpack добавляет такой хеш автоматически через подстановку [contenthash]: она формирует уникальный хеш на основе содержимого актива, и при изменении содержимого хеш тоже меняется (документация webpack по кэшированию). Для Vite аналогичное поведение в проверенных материалах не подтверждено, поэтому хеширование имён стоит проверять в конфигурации проекта.

Для файлов с хешем в имени ставят длинный срок жизни и флаг immutable. Расширение immutable в Cache-Control позволяет серверам помечать ресурсы, которые не будут обновляться в течение их срока свежести, чтобы клиент не выполнял условные запросы для проверки изменений (RFC 8246). RFC 8246 описывает паттерн версионированных URL: ссылки на подресурс меняются одновременно с его содержимым, что позволяет использовать очень большой срок свежести без угадывания момента обновления. Клиенты не должны выдавать условный запрос в течение срока свежести ответа с immutable, если пользователь явно не переопределил это, например принудительной перезагрузкой. Конкретные числовые значения max-age в проверенных материалах не зафиксированы, поэтому срок выбирают по политике проекта.

Сборка и деплой статики: от исходников до продакшена

Путь статики на продакшен выглядит так: исходники в репозитории, сборка в CI, готовые файлы на сервере, отдача через nginx или CDN. Каждый шаг автоматизируется, и это стоит сделать: ручная сборка быстро расходится с репозиторием.

Инструменты сборки: webpack, vite, gulp и npm scripts

Выбор зависит от того, что именно нужно собирать.

  • webpack - гибкий сборщик для сложных проектов с разными типами ресурсов; плата за гибкость - объём конфигурации.
  • vite - быстрый вариант для новых проектов: мгновенный старт дев-сервера, хеширование имён из коробки.
  • gulp - потоковый сборщик задач: минификация, сжатие изображений, копирование файлов; для сборки JS-модулей его берут реже.
  • npm scripts - минимальный вариант без лишних зависимостей, когда хватает пары команд.

Практический ориентир: новый проект на современном стеке - vite, унаследованный проект с большим набором лоадеров - webpack, статический сайт без модулей - npm scripts или gulp.

Отдача статики через nginx и CDN

nginx отдаёт файлы напрямую с диска, а динамические запросы проксирует на бэкенд. Базовый блок для статики: location /static/ с директивой alias или root на каталог dist, expires 30d и add_header Cache-Control "public, immutable". Сжатие включают через gzip и brotli. Динамическое сжатие экономит трафик и ускоряет отдачу HTML, CSS, JS и API-ответов, и большинство текстовых форматов выигрывает от сжатия: HTML, JSON, CSS, JS, XML, SVG, TTF/OTF (обзор сжатия в nginx и Apache). Brotli обеспечивает на 15–25% лучшее сжатие по сравнению с GZIP при сопоставимой скорости, и современные браузеры поддерживают его через заголовок (оптимизация nginx). При этом нельзя сжимать всё подряд: многие форматы уже сжаты (WOFF2, JPEG/PNG/WebP/AVIF, ZIP, PDF, видео и аудио), а некоторые сценарии ломаются от компрессии. Для статики рекомендуется предварительное сжатие через gzip_static и brotli_static, чтобы не тратить CPU на сжатие при каждом запросе.

Готовые наборы директив для разных типов файлов собраны в статье про кэширование статических файлов в nginx.

CDN добавляет смысл, когда аудитория распределена географически: файл отдаёт ближайшая точка присутствия, а не единственный сервер. При переносе сайта не забудьте сбросить кэш CDN, иначе пользователи ещё какое-то время будут получать старые версии файлов.

Чек-лист аудита статических ресурсов на существующем проекте

Проверку удобно вести по порядку, от структуры к заголовкам. Десять пунктов ниже закрывают основные проблемы.

  1. Убедитесь, что статика отделена от шаблонов и загрузок: разные каталоги и разные политики бэкапа.
  2. Найдите дубликаты: одинаковые CSS и JS в нескольких каталогах раздувают вес и расходятся по версиям.
  3. Проверьте битые ссылки на статику: 404 по CSS или JS ломает вёрстку и ловится в логах.
  4. Найдите неиспользуемые файлы: браузер их не запрашивает, но они попадают в бэкап и в репозиторий.
  5. Посмотрите общий размер статики и вес отдельных файлов.
  6. Проверьте сжатие изображений: PNG рядом с WebP и AVIF без необходимости - частая находка.
  7. Проверьте права: на запись статика доступна быть не должна.
  8. Проверьте заголовки кэширования и сжатия для каждой группы файлов.
  9. Проверьте версионирование: имена CSS и JS должны меняться при правках.
  10. Проверьте, что деплой не затрагивает каталог загрузок и что статика попадает в бэкап.

Поиск дубликатов и неиспользуемых файлов

Дубликаты ищут сравнением хешей: md5sum или sha256sum по всем файлам с последующей сортировкой даёт группы совпадений. Второй признак - одинаковые имена в разных каталогах, например jquery.min.js в static/js/ и static/vendor/.

Неиспользуемые файлы вычисляют через логи: список запрошенных URL из access.log сверяют со списком файлов на диске, разницу дают find и grep. Файл, который не запрашивали ни разу за месяц, стоит проверить и, если он не нужен, удалить из репозитория вместе со сборкой.

Проверка прав доступа и владельцев файлов

Статике нужны права на чтение, а не на запись. Для директорий веб-корня рекомендуют права 755 (rwxr-xr-x), а для статических файлов (HTML, CSS, JS, изображения) - 644 (rw-r--r--) (чек-лист безопасности веб-сервера). В некоторых руководствах для статических файлов встречаются и более строгие варианты вроде 640 или 600, поэтому итоговый набор стоит согласовать с политикой проекта. Владелец - пользователь, от которого работает nginx, или root. Правильные права доступа предотвращают повышение привилегий после первичного проникновения, поэтому здесь действует принцип минимальных привилегий. Если статика доступна на запись процессу веб-сервера, подмена файла становится возможной при первой уязвимости в приложении. Права с лишним битом записи для группы и остальных ищут командой вида find /var/www/site/static -type f -perm /022 -ls.

Диагностику ошибок 403 и 404 из-за прав, симлинков и SELinux разбирает статья про ошибки веб-сервера 403, 404 и 500. Запрет на запись в каталоги со статикой дополняет базовую защиту веб-сервера, о которой речь идёт в материале про HTTPS-заголовки, WAF и защиту от атак.

Проверка кэширования и сжатия

Заголовки проверяют запросом curl -I https://site/static/css/style.css. В ответе смотрят три вещи: Cache-Control с большим max-age, заголовок Expires и Content-Encoding со значением gzip или br. Отсутствие Content-Encoding означает, что сжатие выключено или MIME-тип файла не попал в список сжимаемых.

Если max-age слишком мал, браузер перезапрашивает файлы при каждом переходе. Для CSS, JS, шрифтов и изображений с хешем в имени разумный срок - от месяца до года.

Типичные ошибки при переносе статики и как их избежать

Перенос сайта обнажает то, что годами работало случайно. Семь ошибок ниже встречаются чаще всего.

  1. Забыли служебные файлы: robots.txt, sitemap.xml, favicon.ico остались на старом сервере. Проверяйте корень целиком, а не только каталог со вёрсткой.
  2. Не учли регистр имён: ссылка на Style.css при файле style.css на Linux даёт 404.
  3. Перенесли абсолютные пути, привязанные к домену или подкаталогу, и получили битые ссылки на CSS, JS и изображения.
  4. Не перенесли симлинки: копирование по HTTP или распаковка архива без ключа для ссылок превращает их в пустые файлы или обрывает.
  5. Не проверили права и владельца после распаковки архива, и сайт отдаёт 403 на статику.
  6. Забыли сбросить кэш CDN и браузерный кэш: правки не видны пользователям.
  7. Смешали статику с загрузками и потеряли файлы пользователей при первом же деплое.

Регистр имён файлов: почему это ломает сайт на Linux

Windows и macOS по умолчанию не различают регистр в именах файлов, Linux различает. Проект, собранный на macOS, может ссылаться на Hero.webp при файле hero.webp. На Linux такой запрос вернёт 404, и вёрстка потеряет изображение. Лечится единым правилом: все имена в нижнем регистре плюс проверка ссылок перед релизом.

Абсолютные и относительные пути: что выбрать

Абсолютный путь /static/css/style.css привязан к корню домена. Он предсказуем и не зависит от глубины страницы. Относительный путь ../css/style.css зависит от текущего URL, и на страницах разной вложенности он указывает в разные места.

Для статики практичнее абсолютные пути от корня. При переезде в подкаталог их придётся менять, поэтому базовый URL лучше вынести в одну настройку шаблона или в переменную окружения и подставлять в разметку.

Возьмите чек-лист из раздела про аудит и пройдите его на одном проекте: сначала разделите каталоги статики, шаблонов и загрузок, затем включите версионирование имён и заголовки кэширования. Эти три шага убирают большинство проблем с деплоем, бэкапом и отдачей файлов.

Поделиться:
Сохранить гайд? В закладки браузера