Отдавать статику через Nginx: частые ошибки новичков
Настроим Nginx 1.24+ для раздачи HTML, CSS, JavaScript и изображений с отдельного каталога. Разберём ошибки с root, alias, правами, MIME-типами, кешем и проверкой конфигурации.

За 15–20 минут вы настроите Nginx 1.24+ так, чтобы он напрямую отдавал HTML, CSS, JavaScript и изображения из каталога сайта, а затем проверите конфигурацию без перезапуска вслепую. Основные проблемы новичков возникают не в самой раздаче файлов, а в несовпадении пути на диске и URL, правах доступа и неверном кешировании.
Что подготовить до настройки
- сервер с Ubuntu или Debian и установленным Nginx 1.24 или новее;
- доступ к терминалу с правами пользователя, который может редактировать конфигурацию Nginx;
- каталог сайта, например
/srv/www/static; - домен или тестовая запись в файле
/etc/hosts; - файл
index.html, CSS и изображение для проверки.
MIME-тип — значение, по которому браузер понимает, что именно он получил: HTML, CSS, JavaScript, изображение или другой формат. Например, для CSS нужен тип text/css, а для JavaScript — text/javascript или совместимый с ним тип.
Самая частая ошибка — проверять только факт ответа 200. Файл может успешно отдаваться, но с неправильным содержимым, MIME-типом или сроком кеширования.
Пошаговая настройка
Подготовьте каталог и тестовые файлы
Создайте каталог и положите в него минимальную страницу. Владелец процесса Nginx должен иметь право проходить по каталогам и читать файлы.
sudo mkdir -p /srv/www/static/css sudo mkdir -p /srv/www/static/images sudo sh -c 'printf "%s\n" "<!doctype html>" "<html lang=ru>" "<head>" "<meta charset=utf-8>" "<link rel=stylesheet href=/css/site.css>" "</head>" "<body>Статические файлы работают</body>" "</html>" > /srv/www/static/index.html' sudo sh -c 'printf "%s\n" "body { font-family: sans-serif; }" > /srv/www/static/css/site.css' sudo find /srv/www/static -type d -exec chmod 755 {} \; sudo find /srv/www/static -type f -exec chmod 644 {} \;Проверка: команда
ls -l /srv/www/static/index.htmlпоказывает файл с правами чтения, аls -ld /srv/www/static— каталог, доступный для прохода.Создайте отдельный server-блок
Сохраните конфигурацию в
/etc/nginx/sites-available/static-site. Директиваrootзадаёт базовый каталог: к нему Nginx добавляет путь из URL.server { listen 80; listen [::]:80; server_name example.test; root /srv/www/static; index index.html; include /etc/nginx/mime.types; default_type application/octet-stream; location / { try_files $uri $uri/ =404; } location ~* \.(?:css|js|jpg|jpeg|png|gif|svg|webp|ico|woff2)$ { expires 7d; add_header Cache-Control public; } }Замените
example.testна свой домен. Для сайта с уже работающим HTTPS этот блок не заменяет отдельную TLS-конфигурацию.Проверка: команда
sudo nginx -tдолжна завершиться сообщениемsyntax is okиtest is successful.Включите конфигурацию
На Debian и Ubuntu сайт обычно включают символической ссылкой из
sites-availableвsites-enabled.sudo ln -s /etc/nginx/sites-available/static-site /etc/nginx/sites-enabled/static-site sudo nginx -t sudo systemctl reload nginxПроверка: команда
systemctl is-active nginxвозвращаетactive, а запрос к домену открывает страницу из/srv/www/static/index.html.Проверьте HTML, CSS и заголовки
Проверяйте не только страницу в браузере. Заголовки покажут, какой MIME-тип и кеш получил клиент.
curl -I http://example.test/ curl -I http://example.test/css/site.cssДля HTML ожидайте код
200и тип вродеtext/html, для CSS —text/css. Если файл отсутствует, должен возвращаться404, а не случайная HTML-страница.Проверка: в ответе на CSS есть
200 OKиContent-Type: text/css, а в ответе на несуществующий файл —404 Not Found.
Частые ошибки новичков
Путь в root не совпадает с ожиданием
Для запроса /css/site.css и настройки root /srv/www/static Nginx ищет файл по пути /srv/www/static/css/site.css. Если файл лежит в /var/www/site/css/site.css, ответ будет 404, даже если файл существует на сервере.
Не проверяйте путь только через файловый менеджер. Сопоставьте URL и полный путь на диске вручную, затем посмотрите журнал ошибок: на Debian и Ubuntu это обычно /var/log/nginx/error.log.
root перепутали с alias
root добавляет весь URI к указанному каталогу. alias заменяет совпавшую часть URI. Поэтому эти варианты дают разные пути:
location /assets/ {
root /srv/www;
}
location /assets/ {
alias /srv/www/static/;
}В первом случае запрос /assets/app.js ищется как /srv/www/assets/app.js. Во втором — как /srv/www/static/app.js. Для обычного сайта проще оставить root на уровне server, а alias использовать только при осознанном сопоставлении каталога и URL.
Сломали права доступа
Ошибка 403 Forbidden часто означает, что Nginx не может прочитать файл или пройти хотя бы через один родительский каталог. Права 644 на файл не помогут, если каталог /srv или /srv/www закрыт для прохода.
Проверьте всю цепочку командой namei -l /srv/www/static/index.html. Не выдавайте всему дереву права 777: это маскирует проблему и расширяет доступ к файлам.
Не подключили mime.types
Если CSS приходит как application/octet-stream или JavaScript браузер блокирует из-за неподходящего типа, проверьте наличие строки include /etc/nginx/mime.types. Она должна находиться внутри блока http в основном конфигурационном файле или внутри нужного server-блока.
Если файл действительно отдаётся, но страница выглядит без стилей, откройте вкладку Network в инструментах браузера. Там видны код ответа, URL, MIME-тип и причина блокировки.
Включили слишком агрессивный кеш
Заголовок expires 7d удобен для файлов с версией в имени, например app.4f31c.js. Для файла site.css без изменения имени браузер может хранить старую версию семь дней. После публикации правки не увидит ни пользователь, ни вы.
Для часто меняющихся файлов на этапе разработки уберите expires и Cache-Control либо используйте короткий срок. На продакшене надёжнее добавлять хеш к имени файла и менять ссылку при каждом релизе.
Если не получилось
nginx: [emerg] duplicate default server. Два server-блока объявлены как сервер по умолчанию на одном порту. Уберите лишнийdefault_serverили используйте уникальныйserver_name.403 Forbidden. Проверьте права на все каталоги черезnamei -l, наличиеindex.htmlи журналerror.log. Не заменяйте диагностику правами777.404 Not Found. Сопоставьте URL с путём, который строится изroot. Проверьте регистр букв: в LinuxLogo.pngиlogo.png— разные файлы.- В браузере открывается старая версия CSS. Очистите кеш только для проверки, затем решите проблему версионированием имени файла или параметра в URL. Простая перезагрузка не всегда удаляет кеш.
nginx: [emerg] unknown directive. Проверьте опечатку, контекст директивы и версию Nginx. Сначала запускайтеsudo nginx -t, а затем делайте reload.
Где эта схема ломается
Конфигурация выше подходит для готовой статики: лендинга, документации, собранного фронтенда и файлового каталога. Она не запускает PHP, Python или Node.js — для динамического приложения нужен отдельный upstream и проксирование.
Есть и особый случай с одностраничным приложением. Если клиентский роутер ожидает, что запрос /profile вернёт index.html, настройка try_files $uri $uri/ =404 даст 404. Тогда fallback на index.html нужно добавлять осознанно: он не подходит для обычного сайта, где несуществующий файл должен оставаться 404.
Что делать дальше
После базовой проверки включите HTTPS, проверьте доступность скрытых файлов и добавьте отдельный мониторинг кодов 4xx и 5xx. Для продакшена измеряйте размер ответа, время до первого байта и долю запросов к статике из кеша. Следующим шагом стоит настроить сборку с хешированием имён CSS и JavaScript: это решит конфликт между быстрым кешем и мгновенной доставкой новых версий.
Редактор изображений: Убрать лишнее, заменить фон, добавить объект, расширить кадр — на вашем изображении.
Спросить за 20 ₽Источники: NGINX Documentation: Serving Static Content, NGINX HTTP Core Module
Частые вопросы
Почему Nginx отдаёт 404, если файл точно есть на сервере?
Можно ли использовать alias вместо root?
Что делать, если появляется 403 Forbidden?
Почему после изменения CSS браузер показывает старую версию?
Нужна ли отдельная настройка для HTTPS?
Что делать, если у меня другая версия Nginx?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.