1. 1
  2. 2
  3. 3
  4. 4

# теория · шаг 1 из 4

Ставим nginx и открываем первую страницу

Коротко

Для практики нужен рабочий nginx: в контейнере Docker или пакетом в отдельной Linux-машине.

  • Свой сайт кладётся ОТДЕЛЬНЫМ файлом в conf.d, чужие файлы не правятся.
  • Ежедневный набор: nginx -t (проверить), nginx -s reload (применить), nginx -T (посмотреть собранный конфиг целиком).
  • Проверять результат надёжнее curl -i, а не браузером: браузер кеширует и подставляет своё.
  • Две ловушки первого дня: duplicate default server из-за дефолтного файла Debian и 403 из-за прав на КАТАЛОГИ пути, а не на сам файл.

Если nginx уже стоит и nginx -t знаком - листай до «Теперь сам».

Конфиг положил, а страница старая

Типичная первая минута: человек кладёт свой server в /etc/nginx/conf.d/Разбирается в разделе 1, глава «Конфиг: где лежит и как не уронить прод»: Где живёт конфиг - каталог, куда nginx сам заглядывает за чужими кусками конфига, - обновляет браузер и видит стандартную страницу «Welcome to nginx».

Такую страницу дают четыре разные причины: конфиг не перечитан, файл лежит не там, где nginx его ищет, ответил другой server-блок, или браузер показывает свою копию страницы. Разберём проверки этих причин.

Способ первый: контейнер

Для этого способа нужен установленный Docker и свободный порт 8080. Docker - программа, которая запускает готовый образ в отдельной песочнице: nginx в системе не появится, а удалить всё можно одной командой. Проверить, стоит ли он: docker --version. Если команды нет - либо поставь его по инструкции с docs.docker.com, либо иди вторым способом, дальше по уроку они равноценны.

$  команда
mkdir -p ~/nginx-lab/conf.d ~/nginx-lab/www
echo '<h1>привет из nginx</h1>' > ~/nginx-lab/www/index.html

Перед запуском создай файл ~/nginx-lab/conf.d/lab.conf:

~/nginx-lab/conf.d/lab.confhttp
server {
    listen 80 default_server;
    server_name _;

    root /var/www/site;
    index index.html;
}

Теперь запускай контейнер:

$  команда
docker run -d --rm --name lab -p 8080:80 \
  -v ~/nginx-lab/conf.d:/etc/nginx/conf.d:ro \
  -v ~/nginx-lab/www:/var/www/site:ro \
  nginx:1.31-alpine

Строка читается так. -d - запустить в фоне и вернуть терминал; без него консоль будет занята выводом nginx, и следующую команду набрать станет некуда. --rm удалит контейнер после остановки; --name lab даст ему имя lab. Команда docker rm -f lab принудительно остановит и удалит его раньше. -p 8080:80 - слева порт на твоей машине, справа порт внутри контейнера. -v путь_снаружи:путь_внутри:ro подставляет твой каталог внутрь контейнера, ro - только для чтения. nginx:1.31-alpine - имя образа и версия; если его нет локально, Docker скачает сам. Логи смотреть docker logs -f lab: флаг -f оставляет просмотр открытым и показывает новые строки. Выйди через Ctrl+C; nginx продолжит работать. Удалить учебный контейнер можно командой docker rm -f lab.

Наш каталог conf.d заменяет каталог из образа целиком. Поэтому стандартного default.conf внутри нет: nginx прочитает созданный нами lab.conf. Если запустить контейнер с пустым каталогом, он не будет слушать HTTP-порт.

Файл лежит в примонтированной папке, значит внутри контейнера он уже есть. Про то, как эта запись устроена - почему фигурные скобки, зачем точка с запятой и значит ли что-нибудь отступ, - есть отдельный урокРазбирается в разделе 2, глава «Директивы, блоки, контексты»: Весь синтаксис за один урок; сейчас достаточно скопировать. Две строки внутри всё же назову: default_server помечает блок как «отвечает, когда имя не совпало ни с чьим», а server_name _ - это условная заглушка вместо доменного имени, потому что домена у стенда пока нет. Обе разбираются в главе про сервер по умолчаниюРазбирается в разделе 3, глава «default_server и чужие запросы»: Кто ответит на неизвестное имя. После запуска открой http://localhost:8080: увидишь «привет из nginx». При дальнейших изменениях lab.conf проверяй и применяй конфиг. docker exec lab запускает команду внутри уже работающего контейнера. Здесь сначала запускается проверка; && выполнит reload только при её успехе:

$  команда
docker exec lab nginx -t && docker exec lab nginx -s reload

Обнови страницу и проверь заголовок.

Почему монтируем каталог, а не копируем файл

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

Способ второй: пакет в системе

На отдельном учебном сервере или виртуальной машине установи пакет. sudo запускает команду с правами администратора; если он попросит пароль, введи пароль своей учётной записи. При вводе символы не отображаются. Выбери одну команду для своего дистрибутива:

$  команда
sudo apt install nginx        # Debian, Ubuntu
sudo dnf install nginx        # Fedora, RHEL, Rocky

После установки запусти службу: на части дистрибутивов пакет сам её не запускает. start также подходит, если служба уже работает. Проверь порт:

$  команда
sudo systemctl start nginx
systemctl status nginx
curl -I http://localhost

Каталог сайта надо создать - в отличие от способа с контейнером, где он приехал из твоей домашней папки:

$  команда
sudo mkdir -p /var/www/site
echo '<h1>привет из nginx</h1>' | sudo tee /var/www/site/index.html

tee читает текст из команды слева от | и записывает его в файл. Здесь именно tee запускается через sudo, поэтому может писать в /var/www. Обычный > выполнила бы твоя оболочка без этих прав.

Свой сайт кладётся отдельным файлом:

/etc/nginx/conf.d/lab.confhttp
server {
    listen 80 default_server;
    server_name _;

    root /var/www/site;
    index index.html;
}

На свежем учебном Debian или Ubuntu отключи штатную ссылку, если она есть:

$  команда
[ ! -L /etc/nginx/sites-enabled/default ] || \
  sudo unlink /etc/nginx/sites-enabled/default

Иначе два блока объявят себя default_server на одном порту. Это действие относится к новому стенду; на существующем сервере сначала выясни, какой сайт обслуживает штатный блок. Ниже разобрано сообщение о таком конфликте.

$  команда
sudo nginx -t && sudo systemctl reload nginx

Механизм: откуда nginx собирает конфиг

Файл, который читает nginx, ровно один - /etc/nginx/nginx.conf. Всё остальное попадает туда через include, и понимать это дерево полезно с первого дня: файл вне дерева подключений не влияет на работающий сервер.

/etc/nginx/nginx.conf conf.d/*.conf sites-enabled/* твой файл lab.conf включается всегда только в Debian и Ubuntu сюда кладём свой server nginx -T печатает всё это дерево уже собранным в один текст
Файл вне дерева включений не читается вовсе, и ошибки при этом не будет никакой - страница просто останется прежней.

Правило

Свой сайт - отдельным файлом в conf.d, чужие файлы не правим. Так проще отличать свои настройки от поставляемых пакетом. При обновлении менеджер пакетов может предложить заменить изменённый штатный конфиг; проверяй такие запросы, а не рассчитывай на одинаковое поведение дистрибутивов.

nginx -t перед каждым reload. Битый конфиг nginx не применит, но узнать об этом лучше до, а не после.

nginx -T при любом «почему не применилось». Он печатает собранный конфиг целиком, со всеми include, - то есть ровно то, что nginx прочтёт с диска при следующем reload. Работающие воркеры живут по конфигу последнего удачного reload, и его -T не показывает.

Разбор: duplicate default server

Ставим nginx из пакета на Debian, кладём свой lab.conf с listen 80 default_server, проверяем:

$  команда
sudo nginx -t
↳  выводnginx -t
nginx: [emerg] a duplicate default server for 0.0.0.0:80 in /etc/nginx/conf.d/lab.conf:2
nginx: configuration file /etc/nginx/nginx.conf test failed

Читаем сообщение буквально: дубликат сервера по умолчанию для пары 0.0.0.0:80. Значит такой уже есть, и это не наш. Ищем:

$  команда
grep -Rn default_server /etc/nginx/

Фрагмент результата: оставлены два конфликтующих объявления.

↳  выводgrep -Rn default_server /etc/nginx/
/etc/nginx/sites-enabled/default:12:      listen 80 default_server;
/etc/nginx/conf.d/lab.conf:2:      listen 80 default_server;

В сборках Debian и Ubuntu рядом лежит sites-enabled/default с тем же признаком. Признак default_server означает «этот блок отвечает, когда имя не совпало ни с одним другим», и такой блок на пару адрес-порт может быть только один.

Лечится удалением ссылки, а не правкой чужого файла:

$  команда
sudo rm /etc/nginx/sites-enabled/default
sudo nginx -t && sudo systemctl reload nginx

Обрати внимание: nginx -t назвал файл и строку. Это общее свойство его диагностики, и оно экономит больше времени, чем любые догадки.

Разбор: 403 и права на путь целиком

Конфиг применился, а вместо страницы 403 Forbidden. Смотрим в лог ошибок - всегда туда, а не в конфиг:

$  команда
sudo tail -n 3 /var/log/nginx/error.log
↳  выводtail -n 3 /var/log/nginx/error.log
2026/09/02 21:14:07 [error] 1240#1240: *3 open() "/home/evgen/site/index.html" failed
(13: Permission denied), client: 127.0.0.1, server: _, request: "GET / HTTP/1.1"

Первая мысль - выдать права на файл. Она не сработает, и вот почему: чтобы открыть файл, процессу нужно право на ВХОД в каждый каталог по пути, от корня до последнего. Смотрим всю цепочку разом:

$  команда
namei -l /home/evgen/site/index.html
↳  выводnamei -l /home/evgen/site/index.html
drwxr-xr-x root  root  /
drwxr-xr-x root  root  home
drwx------ evgen evgen evgen
drwxr-xr-x evgen evgen site
-rw-r--r-- evgen evgen index.html

Домашний каталог закрыт от всех, кроме владельца, а воркеры nginx работают под www-data - это служебный пользователь, заведённый пакетом специально для веб-сервера: без пароля, без домашнего каталога и без права входить в систему. Для файловой системы он такой же «чужой», как случайный человек.

Столбец слева читается тройками: владелец, группа, все остальные. У строки drwx------ первая буква d значит «каталог», дальше rwx владельцу и по три прочерка группе и остальным. Ровно на этой строке www-data и упирается. Файл при этом читается всеми - и это ничего не даёт.

Отсюда два честных выхода: держать сайты в /var/www (каталог для того и существует) либо открыть на вход именно каталоги пути:

$  команда
sudo chmod o+x /home/evgen

o+x читается как «остальным (o) добавить (+) право на вход в каталог (x)». Для каталога x - это не «выполнить», а «пройти насквозь»: содержимое такой каталог не покажет, но открыть файл по известному пути внутри разрешит.

Первый способ лучше: домашний каталог не должен становиться проходным двором ради одной страницы.

Что ломается без этого

«Правлю конфиг, ничего не меняется». Файл вне дерева включений, или забыт reload. Найди путь lab.conf в выводе nginx -T; в контейнерном варианте используй docker exec lab nginx -T. Отсутствие пути означает, что файл не включён в конфигурацию на диске. Наличие пути ещё не доказывает успешный reload.

«Работало на моей машине, на сервере 403». Разные пути и разные владельцы каталогов. namei -l показывает всю цепочку за один раз.

«Браузер показывает старое». Кеш. Поэтому проверяем curl -i, а не браузером: он покажет ровно то, что отдал сервер, и покажет заголовки.

Зачем это в работе

Три команды - nginx -t, nginx -s reload, nginx -T - это весь ежедневный набор администратора, и почти любая правка конфига проходит через них в этом порядке. Привычка запускать -t до reload стоит одной секунды и снимает целый класс инцидентов: битый конфиг просто не применяется.

Второй случай - чужой сервер, который надо разобрать за пять минут. nginx -T даёт весь конфиг одним текстом, независимо от того, в какие пятнадцать файлов его разложил предыдущий администратор.

Три команды и два лога

$  команда
nginx -t          # проверить синтаксис, ничего не применяя
nginx -s reload   # применить конфиг, не обрывая начатые запросы
nginx -T          # напечатать собранный конфиг целиком

В способе с пакетом выше стоит systemctl reload nginx, а здесь - nginx -s reload. Разница в том, кто передаёт сигнал: во втором случае это делает сам nginx, в первом - служба systemd, которая под капотом вызывает то же самое. На системе со службой привычнее systemctl: он заодно запишет событие в журнал. В контейнере systemd нет вовсе, поэтому там остаётся nginx -s reload.

$  команда
tail -f /var/log/nginx/access.log
tail -f /var/log/nginx/error.log

В контейнере логи по умолчанию уходят в вывод самого контейнера, так что там это docker logs -f lab.

Проверять результат надёжнее curl, а не браузером:

$  команда
curl -i http://localhost:8080/

Ответ ниже сокращён: строка Date опущена.

↳  выводcurl -i http://localhost:8080/
HTTP/1.1 200 OK
Server: nginx/1.31.5
Content-Type: text/html
Content-Length: 33
Last-Modified: Wed, 09 Sep 2026 19:02:46 GMT
Connection: keep-alive
ETag: "6aa1ad56-21"
Accept-Ranges: bytes

<h1>привет из nginx</h1>

Флаг -i показывает заголовки вместе с телом - тело идёт после пустой строки, и в нём ровно то, что ты положил в index.html. -I - только заголовки, -H 'Host: shop.local' позволяет притвориться другим доменом. Этими тремя приёмами проверяется примерно всё, что будет дальше в курсе.

Тридцать три байта, а не двадцать три. Строка <h1>привет из nginx</h1> кажется короче, но русские буквы весят по два байта, плюс перевод строки от echo. Это первый случай в курсе, когда число в ответе не сходится с интуицией; сверять его надо командой wc -c, а не на глаз.

Чтобы имя вроде shop.local открывалось и в браузере, а не только в curl, допиши строку 127.0.0.1 shop.local в /etc/hosts - своей машины это касается так же, как сервера.

Теперь сам

После правки конфига nginx -t молчит и говорит syntax is ok, reload проходит, а сайт по-прежнему отдаёт стандартную страницу nginx. Какие три проверки сделать и в каком порядке?

Ответ. Первая проверка: найди свой файл и блок в выводе nginx -T. В контейнере это docker exec lab nginx -T, в пакетной установке - sudo nginx -T. Проверь также результат reload: -T читает файлы с диска.

Вторая: запроси нужное имя без браузерного кеша. Для контейнера:

$  команда
curl -i -H 'Host: shop.local' http://localhost:8080/

Для пакетной установки используй порт 80, без :8080. Если ответ различается с браузером, проверь его кеш и фактический адрес запроса.

Третья: в собранном конфиге сопоставь адрес, порт, server_name и root. Другой блок может успешно отдать страницу и не написать ошибку в error_log. Правила выбора блока разберём отдельно; сейчас нужно установить, какой каталог обслуживает проверяемый адрес.

Главное

Держи конфиг учебного сайта в отдельном файле подключённого каталога. Перед применением выполняй nginx -t, после него проверяй ответ через curl. Если правка не видна, сверяй дерево include командой nginx -T: она показывает файлы на диске, которые будут прочитаны при следующем reload.

Комментарии

Пока нет комментариев. Будь первым!

Оставить комментарий

Комментарий появится после проверки. Email не публикуется. Войти, чтобы не вводить имя каждый раз.