# теория · шаг 1 из 4
Ставим nginx и открываем первую страницу
Коротко
Рабочий nginx нужен под рукой с самого начала - контейнером или пакетом, пять минут в обе стороны.
- Свой сайт кладётся ОТДЕЛЬНЫМ файлом в
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 - программа, которая
запускает готовый образ в отдельной песочнице: nginx в системе не появится, а
удалить всё можно одной командой. Проверить, стоит ли он: docker --version.
Если команды нет - либо поставь его по инструкции с docs.docker.com, либо иди
вторым способом, дальше по уроку они равноценны.
mkdir -p ~/nginx-lab/conf.d ~/nginx-lab/www
echo '<h1>привет из nginx</h1>' > ~/nginx-lab/www/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, и следующую команду набрать станет некуда.
-p 8080:80 - слева порт на твоей машине, справа порт внутри контейнера.
-v путь_снаружи:путь_внутри:ro подставляет твой каталог внутрь контейнера,
ro - только для чтения. nginx:1.31-alpine - имя образа и версия; если его
нет локально, Docker скачает сам. Логи смотреть docker logs -f lab, убрать
всё - docker rm -f lab.
Открывай http://localhost:8080 - увидишь стандартную страницу nginx.
Теперь добавь свой сайт:
server {
listen 80 default_server;
server_name _;
root /var/www/site;
index index.html;
}
Файл лежит в примонтированной папке, значит внутри контейнера он уже есть.
Про то, как эта запись устроена - почему фигурные скобки, зачем точка с запятой
и значит ли что-нибудь отступ, - есть
отдельный урокРазбирается в разделе 2, глава «Директивы, блоки, контексты»: Весь синтаксис за один урок; сейчас достаточно
скопировать. Две строки внутри всё же назову: default_server помечает блок
как «отвечает, когда имя не совпало ни с чьим», а server_name _ - это
условная заглушка вместо доменного имени, потому что домена у стенда пока нет.
Обе разбираются в главе про сервер по умолчаниюРазбирается в разделе 3, глава «default_server и чужие запросы»: Кто ответит на неизвестное имя.
Осталось сказать nginx перечитать конфиг:
docker exec lab nginx -t && docker exec lab nginx -s reload
Обнови страницу - на ней твой заголовок. Это уже полноценный веб-сервер: тот же бинарник и тот же конфиг, что на проде.
Почему монтируем каталог, а не копируем файл
Правишь файл обычным редактором на своей машине, в контейнер копировать нечего.
Пересоздавать контейнер после каждой правки тоже не надо: reload применяет
конфиг, не роняя сервер.
Способ второй: пакет в системе
Если под рукой сервер или виртуалка - ставится одной строкой.
sudo apt install nginx # Debian, Ubuntu
sudo dnf install nginx # Fedora, RHEL, Rocky
После установки nginx уже запущен и слушает 80-й порт:
systemctl status nginx
curl -I http://localhost
Каталог сайта надо создать - в отличие от способа с контейнером, где он приехал из твоей домашней папки:
sudo mkdir -p /var/www/site
echo '<h1>привет из nginx</h1>' | sudo tee /var/www/site/index.html
Свой сайт кладётся отдельным файлом - и не трогая ничего вокруг:
server {
listen 80 default_server;
server_name _;
root /var/www/site;
index index.html;
}
sudo nginx -t && sudo systemctl reload nginx
Механизм: откуда nginx собирает конфиг
Файл, который читает nginx, ровно один - /etc/nginx/nginx.conf. Всё
остальное попадает туда через include, и понимать это дерево полезно с
первого дня: половина вопросов «почему не применилось» - про то, что файл
лежит вне дерева.
Правило
Свой сайт - отдельным файлом в conf.d, чужие файлы не правим.
Дефолтные конфиги перезаписываются при обновлении пакета, и правка в них
исчезнет молча.
nginx -t перед каждым reload. Битый конфиг nginx не применит, но узнать
об этом лучше до, а не после.
nginx -T при любом «почему не применилось». Он печатает собранный конфиг
целиком, со всеми include, - то есть ровно то, что nginx видит на самом деле.
Разбор: duplicate default server
Ставим nginx из пакета на Debian, кладём свой lab.conf с listen 80
default_server, проверяем:
sudo 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/
/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
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
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. Проверяется одной командой nginx -T | grep свой_домен: если строки
там нет, nginx твой файл не видит.
«Работало на моей машине, на сервере 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/
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 | grep -n server_name - виден ли твой блок в
собранном конфиге. Если его там нет, файл лежит вне дерева включений, и всё
остальное неважно. Вторая: curl -i -H 'Host: shop.local' http://localhost/ -
запрос с нужным именем и без браузерного кеша; если ответ верный, дело было в
браузере. Третья: tail /var/log/nginx/error.log - если ответ всё ещё чужой,
значит запрос попал в другой server-блок, и это уже разговор про выбор
блока, о котором будет отдельная глава. Порядок именно такой: сначала «видит ли
nginx мой конфиг», потом «дошёл ли до него запрос», и только потом «почему
ответил не тот».
Главное
Рабочий nginx под рукой - контейнером или пакетом, свой сайт отдельным файлом
в conf.d. Ежедневный набор: nginx -t перед каждым reload, nginx -T при
любом «почему не применилось». Ответ проверять curl -i. Две ловушки первого
дня - duplicate default server из-за дефолтного файла Debian и 403 из-за
прав на каталоги пути, а не на сам файл.
Комментарии
Пока нет комментариев. Будь первым!
Оставить комментарий