Отдавать статику через Nginx: что проверить до старта
Nginx может отдавать CSS, JavaScript, изображения и HTML без участия приложения. До первой настройки нужно проверить структуру каталогов, права, MIME-типы, маршрутизацию и кеширование, иначе сайт получит 403, 404 или устаревшие файлы.

Статический сайт через Nginx можно поднять за несколько минут, но ошибка в одном из трёх мест — пути, правах или конфигурации — даст 403 вместо страницы, 404 вместо картинки или старый CSS из кеша. Ниже — инструкция для Nginx 1.24 и новее: к концу вы получите отдельный виртуальный хост, который отдаёт HTML, изображения, CSS и JavaScript напрямую с диска.
Что нужно иметь до начала
Инструкция рассчитана на Linux-сервер с установленным Nginx 1.24+. Конкретные пути конфигурации зависят от дистрибутива, поэтому сначала проверьте версию и расположение главного файла:
- доступ к серверу с правами на чтение конфигурации и перезапуск Nginx;
- каталог сайта, например
/srv/www/example.com/public; - домен, который уже указывает DNS-записью на IP сервера, если сайт должен открываться по имени;
- тестовые файлы:
index.html, CSS, JavaScript и изображение; - команда
nginx -tдля проверки конфигурации.
Ниже предполагается Debian-подобная система и пользователь веб-сервера www-data. В Alpine, CentOS и RHEL имя пользователя может быть другим. Узнать его можно в основном конфигурационном файле Nginx по директиве user.
Сноска. MIME-тип — это HTTP-описание формата файла. Например, браузер ожидает для CSS тип text/css, а для JavaScript — один из JavaScript MIME-типов. Если сервер отдаёт тип неправильно, браузер может заблокировать ресурс.
Сначала добейтесь корректного ответа без кеша. Только после этого добавляйте долгий срок хранения для CSS, JavaScript и изображений.
Шаг 1. Подготовьте каталог и тестовые файлы
Создайте каталог, если его ещё нет, и положите в него минимальный набор файлов. В рабочем проекте файлы обычно собирает CI/CD или система деплоя, а не команда в терминале.
sudo mkdir -p /srv/www/example.com/public/assets
sudo tee /srv/www/example.com/public/index.html > /dev/null <<'EOF'
<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Nginx static test</title>
<link rel="stylesheet" href="/assets/site.css">
</head>
<body>
<h1>Статика работает</h1>
<script src="/assets/site.js" defer></script>
</body>
</html>
EOF
sudo tee /srv/www/example.com/public/assets/site.css > /dev/null <<'EOF'
body { font-family: sans-serif; }
EOF
sudo tee /srv/www/example.com/public/assets/site.js > /dev/null <<'EOF'
console.log('static file loaded');
EOF
Проверьте, что файлы существуют и читаются не только вашим пользователем:
sudo find /srv/www/example.com/public -maxdepth 2 -type f -ls
Проверка шага: в выводе должны быть index.html, assets/site.css и assets/site.js. Если каталог собирает отдельный пользователь деплоя, не меняйте владельца вслепую: Nginx достаточно права чтения файлов и прохода по каталогам.
Шаг 2. Проверьте права на путь
Nginx должен пройти через каждый каталог от корня файловой системы до файла. Для каталогов нужен execute-бит, для файлов — read-бит. Посмотрите права на весь путь:
namei -l /srv/www/example.com/public/index.html
Не обязательно делать сайт владельцем www-data. Частая безопасная схема — владелец отвечает за деплой, группа или остальные пользователи имеют только чтение. Для тестового каталога можно проверить доступ так:
sudo -u www-data test -r /srv/www/example.com/public/index.html && echo "file is readable"
sudo -u www-data test -x /srv/www/example.com/public && echo "directory is searchable"
Проверка шага: обе команды должны вывести подтверждение. Если появляется Permission denied, исправляйте права на конкретный каталог или файл, а не выдавайте всему дереву chmod -R 777.
Шаг 3. Создайте виртуальный хост
Положите конфигурацию в файл, который подключается внутри блока http. В Debian и Ubuntu это может быть /etc/nginx/sites-available/example.com с симлинком в sites-enabled; в других системах часто используют /etc/nginx/conf.d/example.com.conf.
server {
listen 80;
server_name example.com www.example.com;
root /srv/www/example.com/public;
index index.html;
location / {
try_files $uri $uri/ =404;
}
location ~* \.(?:css|js|jpg|jpeg|png|gif|svg|webp|ico|woff2?)$ {
try_files $uri =404;
expires 7d;
add_header Cache-Control "public, max-age=604800";
}
location ~ /\.(?!well-known) {
deny all;
}
}
Директива root добавляет путь из URL к указанному каталогу. Например, запрос /assets/site.css ищется как /srv/www/example.com/public/assets/site.css. try_files не передаёт неизвестные адреса в приложение и возвращает 404, если файла нет.
Проверка шага: конфигурация должна лежать в реально подключаемом каталоге. Убедитесь в этом командой nginx -T: в полном выводе должен появиться ваш блок server.
Шаг 4. Проверьте и примените конфигурацию
Сначала проверяйте синтаксис, затем перечитывайте конфигурацию без остановки уже работающих соединений:
sudo nginx -t
sudo systemctl reload nginx
Если на сервере нет systemd, используйте способ управления сервисом, принятый в вашем дистрибутиве. Не делайте перезагрузку после неудачного nginx -t: исправьте ошибку и проверьте снова.
Проверка шага: nginx -t должен закончиться сообщением о том, что синтаксис корректен и проверка успешна. После reload запрос к домену должен вернуть HTML.
Шаг 5. Проверьте ответы и MIME-типы
Проверяйте не только страницу в браузере. Заголовки покажут статус, тип содержимого и кеширование:
curl -I http://example.com/
curl -I http://example.com/assets/site.css
curl -I http://example.com/assets/site.js
curl -I http://example.com/assets/missing.css
Ожидаемые результаты: для главной страницы и существующих ресурсов — 200 OK, для отсутствующего файла — 404 Not Found. CSS не должен приходить как text/html. Если MIME-типы не определяются, проверьте, что в блоке http главного конфига есть подключение таблицы типов:
include /etc/nginx/mime.types;
Путь к mime.types может отличаться. Его нельзя добавлять второй раз без необходимости: сначала найдите уже существующую директиву в выводе nginx -T.
Если не получилось
403 Forbidden
Обычно Nginx не может пройти по одному из каталогов, не может прочитать файл или пытается открыть каталог без доступного index.html. Посмотрите error log и проверьте путь командой namei -l. Не открывайте права на весь сервер — исправьте только ветку сайта.
404 Not Found
Сравните URL и физический путь. Для /assets/site.css при указанном root файл обязан лежать в /srv/www/example.com/public/assets/site.css. Если файл существует, проверьте, не попал ли запрос в другой server с тем же или совпадающим server_name.
nginx: [emerg] "root" directive is not allowed here
Директива помещена не в тот контекст конфигурации. root допустим, в частности, в контекстах http, server и location. Для этой инструкции оставьте её внутри блока server.
CSS или JavaScript не загружается
Откройте вкладку Network в инструментах разработчика и проверьте статус и Content-Type. Статус 404 означает ошибку пути, а блокировка ресурса после ответа с типом HTML часто указывает на неправильную маршрутизацию или отсутствующий файл.
Старый CSS остаётся после деплоя
В конфигурации выше ресурс получает кеш на семь дней. Для файлов с тем же именем изменения могут не появляться сразу. Используйте версионирование имени или параметра файла, например site.2025-01.css, а после изменения сборки обновляйте ссылку в HTML. Инвалидация CDN и кеш браузера — отдельная тема.
Что проверить после первого запуска
Сделайте запросы к существующему файлу, отсутствующему файлу, скрытому файлу и каталогу. Затем посмотрите access log и error log во время теста. В продакшене отдельно настройте HTTPS, перенаправление с HTTP и безопасные заголовки; они не относятся к базовой раздаче статики и требуют собственной проверки.
- убедитесь, что
.env, файлы резервных копий и служебные каталоги не доступны по HTTP; - проверьте, что сборка не оставляет исходники с секретами в публичном каталоге;
- измерьте размер и время ответа крупных изображений;
- после включения кеша проверьте обновление ресурсов на чистом профиле браузера.
Следующий практический шаг — добавить автоматическую проверку после деплоя: запросить главную страницу, один CSS, один JavaScript и заведомо отсутствующий файл. Такой тест быстро ловит неправильный root, сломанные права и случайно удалённую статику.
Генератор изображений: Иллюстрация, обложка, баннер, схема — по текстовому описанию, без исходного фото.
Спросить за 20 ₽Источники: Nginx documentation: ngx_http_core_module, Nginx documentation: ngx_http_gzip_module
Частые вопросы
Нужно ли запускать отдельный сервер для статики?
Почему Nginx возвращает 403, хотя файл существует?
Можно ли указать root внутри location?
Что делать, если у меня другая версия Nginx?
Почему после деплоя браузер показывает старый JavaScript?
Нужно ли добавлять gzip или Brotli сразу?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.