# теория · шаг 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:
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.
Обычный > выполнила бы твоя оболочка без этих прав.
Свой сайт кладётся отдельным файлом:
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, и понимать это дерево полезно с
первого дня: файл вне дерева подключений не влияет на работающий сервер.
Правило
Свой сайт - отдельным файлом в 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: [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. Найди путь 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 опущена.
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.
Комментарии
Пока нет комментариев. Будь первым!
Оставить комментарий