Цели занятия
На этом занятии Nginx используется не только для отдачи статических файлов, но и как reverse proxy. Это нужно, когда сайт или API запускается отдельным приложением на локальном порту.
После занятия студент должен уметь:
- объяснить, что такое reverse proxy;
- объяснить назначение
proxy_pass; - запустить тестовое приложение на локальном порту;
- создать systemd-службу для приложения;
- настроить Nginx для проксирования запросов;
- передать приложению корректные заголовки;
- закрыть внутренний порт от внешнего доступа;
- найти причину ошибки
502 Bad Gateway.
1. Схема работы reverse proxy
Reverse proxy — это сервер, который принимает запрос от пользователя и передаёт его другому серверу или приложению.
Браузер пользователя
↓
https://example.com
↓
Nginx
↓ proxy_pass
http://127.0.0.1:3000
↓
Backend-приложение
Зачем использовать Nginx перед приложением
- Nginx принимает HTTP/HTTPS-запросы на стандартных портах
80и443; - приложение может работать на внутреннем порту, например
3000; - сертификаты HTTPS настраиваются в Nginx;
- внутренний порт приложения не нужно открывать в интернет;
- логи веб-доступа остаются в Nginx.
2. Тестовое приложение
Для практики можно использовать простой сервер на Python. Он будет слушать локальный порт 3000.
Создание директории
sudo mkdir -p /opt/student-app
sudo chown -R $USER:$USER /opt/student-app
cd /opt/student-app
Создание файла приложения
nano app.py
Содержимое файла app.py
from http.server import BaseHTTPRequestHandler, HTTPServer
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header("Content-type", "text/html; charset=utf-8")
self.end_headers()
self.wfile.write("<h1>Приложение работает через Nginx</h1>".encode("utf-8"))
server = HTTPServer(("127.0.0.1", 3000), Handler)
server.serve_forever()
Проверка запуска вручную
python3 app.py
В другом терминале нужно проверить ответ:
curl http://127.0.0.1:3000
127.0.0.1, поэтому оно доступно только внутри сервера. Это безопаснее, чем слушать 0.0.0.0 без необходимости.
3. Создание systemd-службы для приложения
Приложение не должно запускаться вручную после каждого перезапуска сервера. Для этого создаётся systemd-служба.
Файл службы
sudo nano /etc/systemd/system/student-app.service
Содержимое файла
[Unit]
Description=Student test web application
After=network.target
[Service]
WorkingDirectory=/opt/student-app
ExecStart=/usr/bin/python3 /opt/student-app/app.py
Restart=always
User=www-data
Group=www-data
[Install]
WantedBy=multi-user.target
Права на директорию приложения
sudo chown -R www-data:www-data /opt/student-app
Запуск службы
sudo systemctl daemon-reload
sudo systemctl enable student-app
sudo systemctl start student-app
sudo systemctl status student-app
Проверка приложения
curl http://127.0.0.1:3000
sudo systemctl status student-app, затем логи через journalctl.
4. Настройка Nginx для проксирования
Теперь нужно настроить Nginx так, чтобы он принимал запросы на домен и передавал их приложению на 127.0.0.1:3000.
Создание конфига сайта
sudo nano /etc/nginx/sites-available/student-app
Конфигурация reverse proxy
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Включение сайта
sudo ln -s /etc/nginx/sites-available/student-app /etc/nginx/sites-enabled/
Проверка и применение
sudo nginx -t
sudo systemctl reload nginx
Проверка через Nginx
curl http://example.com
5. Proxy-заголовки
Когда Nginx передаёт запрос приложению, он может добавить служебные заголовки. Они помогают приложению понимать, какой домен запрашивал пользователь, какой был IP клиента и какой протокол использовался.
| Заголовок | Назначение |
|---|---|
Host |
Передаёт домен, на который пришёл запрос. |
X-Real-IP |
Передаёт реальный IP клиента. |
X-Forwarded-For |
Передаёт цепочку IP-адресов клиента и прокси. |
X-Forwarded-Proto |
Передаёт исходный протокол: HTTP или HTTPS. |
Базовый набор заголовков
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
6. Firewall при reverse proxy
При правильной схеме наружу открываются только порты Nginx. Внутренний порт приложения не открывается в UFW.
Правильный набор правил
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw status
Что не нужно открывать
sudo ufw allow 3000/tcp
3000 не нужно открывать наружу, если к приложению обращается Nginx через 127.0.0.1.
Проверка слушающего адреса
sudo ss -tulpn | grep ':3000'
Безопасный вариант:
127.0.0.1:3000
Более рискованный вариант:
0.0.0.0:3000
7. Диагностика reverse proxy
Самая частая ошибка при reverse proxy — 502 Bad Gateway. Она означает, что Nginx получил запрос, но не смог получить корректный ответ от backend-приложения.
| Проблема | Возможная причина | Проверка |
|---|---|---|
502 Bad Gateway |
Приложение не запущено | sudo systemctl status student-app |
502 Bad Gateway |
Неверный порт в proxy_pass |
sudo ss -tulpn | grep ':3000' |
| Nginx не запускается | Ошибка в конфигурации | sudo nginx -t |
| Сайт не открывается снаружи | Закрыт порт 80 или 443 | sudo ufw status |
| Приложение работает локально, но не через домен | Не включён server block или неверный server_name |
ls -l /etc/nginx/sites-enabled/ |
Команды диагностики
sudo nginx -t
sudo systemctl status nginx
sudo systemctl status student-app
sudo journalctl -u student-app -n 50
sudo tail -n 100 /var/log/nginx/error.log
sudo ss -tulpn | grep ':3000'
curl http://127.0.0.1:3000
curl http://example.com
8. Практическая работа
app.py.
sudo mkdir -p /opt/student-app
sudo chown -R $USER:$USER /opt/student-app
cd /opt/student-app
nano app.py
python3 app.py
curl http://127.0.0.1:3000
student-app, запустите её и включите автозагрузку.
sudo systemctl daemon-reload
sudo systemctl enable student-app
sudo systemctl start student-app
sudo systemctl status student-app
http://127.0.0.1:3000.
sudo nano /etc/nginx/sites-available/student-app
sudo ln -s /etc/nginx/sites-available/student-app /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
sudo ufw status
sudo ss -tulpn | grep ':3000'
502 Bad Gateway: остановите приложение и попробуйте открыть сайт через Nginx. Затем найдите причину в логах и запустите приложение обратно.
sudo systemctl stop student-app
curl http://example.com
sudo tail -n 50 /var/log/nginx/error.log
sudo systemctl start student-app
9. Мини-проверка
Вопросы
- Что такое reverse proxy?
- Зачем ставить Nginx перед backend-приложением?
- Что делает директива
proxy_pass? - Почему приложение лучше слушать на
127.0.0.1? - Зачем нужна systemd-служба для приложения?
- Какая команда проверяет конфигурацию Nginx?
- Какие порты нужно открыть наружу для сайта?
- Нужно ли открывать порт
3000в UFW? - Что означает ошибка
502 Bad Gateway? - С какой проверки нужно начинать диагностику 502?
Ответы для самопроверки
- Сервер, который принимает внешний запрос и передаёт его внутреннему приложению.
- Чтобы принимать запросы на стандартных портах, использовать HTTPS и не открывать внутренний порт приложения.
- Передаёт запросы на указанный backend-адрес.
- Так приложение доступно только внутри сервера.
- Чтобы приложение запускалось как служба и автоматически поднималось после перезагрузки.
sudo nginx -t.80/tcpи443/tcp, а также SSH-порт для администрирования.- Нет, если Nginx обращается к приложению локально.
- Nginx не смог получить корректный ответ от backend-приложения.
- С проверки статуса приложения и локального ответа на
127.0.0.1:3000.
10. Итоговый порядок публикации приложения
1. Запустить приложение локально:
curl http://127.0.0.1:3000
2. Создать systemd-службу:
sudo systemctl enable student-app
sudo systemctl start student-app
3. Проверить службу:
sudo systemctl status student-app
4. Настроить Nginx:
proxy_pass http://127.0.0.1:3000;
5. Проверить Nginx:
sudo nginx -t
sudo systemctl reload nginx
6. Проверить firewall:
sudo ufw status
7. Проверить сайт через домен:
curl http://example.com
8. При ошибке смотреть:
sudo journalctl -u student-app -n 50
sudo tail -n 100 /var/log/nginx/error.log