Дисклеймер
Эта статья подготовлена по мотивам постов в закрытом телеграм канале ALEXPROIT (автор: Мясоед Александр).
Примеры кода, использованные в статье, основаны на открытом репозитории GitHub и распространяются по лицензии MIT, допускающей свободное использование с указанием авторства.
📩 Контакт для связи в Telegram: @Aleksandr_Myasoed
Что вы узнаете из статьи:
- Как подготовить VPS-сервер на Cloud.ru и открыть к нему доступ по IP;
- Как установить и настроить Gunicorn как WSGI-сервер для Django;
- Как настроить Nginx как обратный прокси;
- Как зарегистрировать и подключить домен;
- Как выпустить бесплатный SSL-сертификат Let's Encrypt и настроить автоматическое продление;
- Как обеспечить безопасный доступ к вашему Django-приложению по протоколу HTTPS.
Почему Cloud.ru?
Это не реклама — просто рациональный выбор для разработчика:
- VPS с внешним IP;
- Операционная система: Ubuntu Linux (LTS);
- 4 ГБ RAM, 30 ГБ SSD-диск;
- 100 ГБ в S3-совместимом бакете для храния статики и медиафайлов;
- И всё это — всего за 150 ₽/месяц.
Cloud.ru — это российский аналог DigitalOcean или Hetzner, но дешевле и с локальной поддержкой. Отличный выбор, если вы хотите протестировать или разместить Django-проект в продакшне.
Веб сервер Nginx
Чтобы ваше веб-приложение было доступно пользователям по всему миру, его нужно развернуть на веб-сервере. Веб-сервер — это программа, которая принимает HTTP-запросы от браузеров и отдает им HTML, CSS, JS и другие файлы. Без веб-сервера ваше приложение останется доступным только локально, на вашей машине.
В нашем проекте мы будем использовать Nginx в роли веб-сервера.
Вот почему:
- Скорость и лёгкость: Nginx отлично справляется с большим количеством одновременных подключений и быстро обслуживает статические файлы.
- Обратный прокси: он будет перенаправлять запросы к backend-серверу (например, Gunicorn) — это позволяет отделить логику приложения от сетевого взаимодействия.
- SSL и безопасность: Nginx легко настраивается для работы по HTTPS с бесплатными сертификатами Let's Encrypt.
- Балансировка нагрузки: если приложение масштабируется на несколько серверов — Nginx поможет распределить нагрузку.
Nginx не может напрямую работать с Django:
Nginx — это веб-сервер, написанный на C и заточенный под:
- выдачу статических файлов (HTML, CSS, JS, изображения),
- проксирование запросов,
- балансировку нагрузки и SSL.
Nginx не умеет запускать Python-код, не понимает WSGI-приложения и не может интерпретировать Django-логику сам по себе. То есть:
Nginx не знает, что делать с views.py, моделями, роутами и базой данных.
Что такое WSGI сервер?
WSGI (Web Server Gateway Interface) — это спецификация взаимодействия между Python-приложением и веб-сервером.
Она определяет, как сервер должен вызывать приложение и как приложение должно возвращать ответ — всё строго по протоколу.
Иными словами, WSGI — это мост между HTTP-сервером (например, Nginx) и Python-кодом (например, Django).
🔧 Зачем нужен WSGI-сервер
- Python-приложения (Django, Flask) не работают напрямую с HTTP и сокетами — они просто принимают environ и возвращают response.
- Python-приложению нужен WSGI-сервер, который:
слушает сокет или порт, принимает HTTP-запросы, преобразует их в WSGI-содержимое (environ, start_response), вызывает application(environ, start_response) из Python, получает ответ и отправляет его обратно клиенту.
Примеры WSGI-серверов
Gunicorn - легкий, простой, стабилен. Хорошо работает с Nginx.
uWSGI - мощный, с кучей настроек, но сложнее в конфигурации.
Daphne - используется для ASGI-приложений (WebSocket, Async).
Waitress - кроссплатформенный, часто используется под Windows.
WSGI и ASGI - в чем разница?
Что такое WSGI?
WSGI — это синхронный интерфейс:
сервер вызывает Python-функцию application(environ, start_response), которая сразу возвращает ответ. Это просто, стабильно, но неэффективно под высоконагруженные или долгоиграющие соединения (например, WebSocket или SSE).
⛔ Пример проблемы: WSGI не умеет ждать асинхронные события — он блокируется на каждом запросе.
Что такое ASGI?
ASGI — это асинхронная эволюция WSGI, спроектированная для современного веба.
Поддерживает:
- асинхронные фреймворки (FastAPI, Starlette, Django Channels);
- двустороннюю коммуникацию (WebSocket, long-polling, SSE);
- параллельную обработку большого количества соединений без блокировок.
Главное: ASGI умеет одновременно обрабатывать много лёгких и долгоживущих соединений — идеально подходит для real-time приложений (чат, стрим, телеметрия, игры).
Когда использовать WSGI, а когда ASGI?
В этой статье мы ограничимся рассмотрением WSGI и Gunicorn.
Как с помощью Gunicorn передать запрос от Nginx в Django?
Django сам по себе не может обрабатывать реальные HTTP-запросы из интернета — он не умеет слушать порты и работать с сетью на низком уровне.
Для этого ему нужен посредник — WSGI-сервер. Вот тут и появляется Gunicorn.
Gunicorn (сокращение от Green Unicorn) — это WSGI-сервер для запуска Python‑веб-приложений, таких как Django или Flask, в продакшене.
Gunicorn выполняет 3 ключевые задачи:
- Запускает Django как WSGI-приложение (по стандарту Python Web Server Gateway Interface).
- Создаёт несколько worker-процессов, чтобы обрабатывать запросы параллельно.
- Слушает сокет или порт и отдаёт результат запроса обратно через Nginx.
Как работает связка: Nginx → Gunicorn → Django?
Браузер → Nginx → Gunicorn → Django → Gunicorn → Nginx → Браузер
- Nginx отвечает за соединение с клиентом, SSL, отдачу статики.
- Gunicorn запускает Django, принимает запросы и возвращает ответы.
Почему Gunicorn?
- Прост в установке и настройке.
- Поддерживает многопроцессную модель — стабильно работает под нагрузкой.
- Легко интегрируется с Nginx.
- Соответствует стандарту WSGI — совместим с большинством Python-фреймворков.
Пример запуска gunicorn:
gunicorn myproject.wsgi:application
gunicorn - CLI-утилита WSGI-сервера Gunicorn, установленная в твоём виртуальном окружении (или глобально). Запускает главный мастер-процесс Gunicorn.
myproject.wsgi - путь к Python-модулю.
- myproject — имя Django-проекта (папка, где лежит settings.py).
- wsgi — файл wsgi.py, создаваемый django-admin startproject.
Gunicorn импортирует модуль myproject.wsgi.
:application - после двоеточия идёт имя WSGI-callable (переменная) внутри модуля wsgi.py. По умолчанию Django создаёт именно application. Gunicorn получает ссылку на функцию/объект application, через который передаются все HTTP-запросы в Django.
Что происходит после запуска команды?
- Gunicorn импортирует модуль myproject.wsgi.
- Находит в нём объект application (это и есть WSGI-интерфейс Django).
- Создаёт мастер-процесс и, если не указаны параметры --workers, порождает 1 worker-процесс.
- По умолчанию слушает 127.0.0.1:8000.
- Каждый HTTP-запрос, пришедший на этот сокет, попадает в worker, который вызывает application(environ, start_response); далее управление переходит во внутренности Django: URL-маршруты, views и т.д.
- Gunicorn собирает ответ и отдаёт его Nginx.
Полезные опции Gunicorn, которые обычно добавляют в команду
gunicorn myproject.wsgi:application --bind unix:/path/to/gunicorn.sock --workers 3 --threads 2 --timeout 30 --access-logfile -
⚡ Итог
gunicorn myproject.wsgi:application - эта лаконичная команда «склеивает» Gunicorn и Django: Gunicorn знает, какой модуль загрузить, где искать WSGI-callable и начинает принимать запросы, которые далее можно проксировать через Nginx.
Установка Gunicorn в проект Django 5
1. Активируйте виртуальное окружение:
source venv/bin/activate
Убедитесь, что вы находитесь в корне Django-проекта, рядом с manage.py.
2. Установите Gunicorn:
pip install gunicorn
Проверьте установку:
gunicorn --version
3. Добавьте Gunicorn в зависимости проекта
pip freeze > requirements.txt
4. Запустите Gunicorn локально (проверка):
gunicorn myproject.wsgi:application
Замените myproject на фактическое имя Django-проекта (где лежит wsgi.py).
По умолчанию сервер слушает 127.0.0.1:8000. Если всё настроено — вы увидите HTML главной страницы веб приложения.
5. Настройте рабочие параметры
Пример команды:
- --workers — количество воркеров (обычно CPU * 2 + 1)
- --threads — дополнительная многопоточность
- --bind — сокет-файл или IP:PORT
- --timeout — максимальное время выполнения одного запроса
6. Создайте systemd юнит (для автозапуска)
Файл: /etc/systemd/system/gunicorn.service
7. Перезапустите и проверьте статус
sudo systemctl daemon-reexec
sudo systemctl daemon-reload
sudo systemctl start gunicorn
sudo systemctl enable gunicorn
sudo systemctl status gunicorn
8. Безопасность
Убедитесь, что:
- права на папку с сокетом (gunicorn.sock) позволяют читать Nginx (www-data);
- Gunicorn не запускается от root.
Итог
Теперь наш Django-проект работает на продакшн-ready WSGI-сервере Gunicorn. Следующий шаг — подключить его к Nginx для работы с HTTPS и доменом.
Чтобы gunicorn автоматически запускался при перезагрузке операционной системы, необходимо воспользоваться утилитой systemd.
Что такое systemd?
systemd — это современная система инициализации (init system) и менеджер системных служб в большинстве современных Linux-дистрибутивов (включая Ubuntu, Debian, CentOS, Fedora).
Она управляет:
- запуском и остановкой служб,
- их автозагрузкой при старте системы,
- перезапуском при сбоях,
- логированием через journalctl,
- зависимостями между сервисами (например, Nginx зависит от Gunicorn и сети).
Это "дирижёр", который управляет тем, как и когда стартуют все компоненты на сервере.
Зачем systemd для Gunicorn
Gunicorn сам по себе можно запускать вручную, но это:
- неудобно (нужно вручную перезапускать после ребута сервера),
- нестабильно (если упадёт — не восстановится),
- сложно логировать.
Поэтому мы регистрируем Gunicorn как systemd-сервис, чтобы:
✅ он запускался автоматически при старте сервера;
✅ он перезапускался при сбоях;
✅ он логировался в journalctl;
✅ им можно было управлять через стандартные команды systemctl.
Пример: gunicorn.service
Файл создаётся по пути:
sudo vim /etc/systemd/system/gunicorn.service
Команды для управления Gunicorn через systemd
sudo systemctl start gunicorn # Запустить Gunicorn вручную
sudo systemctl stop gunicorn # Остановить службу
sudo systemctl restart gunicorn # Перезапустить Gunicorn
sudo systemctl status gunicorn # Проверить статус, ошибки
sudo systemctl enable gunicorn # Автозапуск при старте сервера
sudo systemctl disable gunicorn # Убрать из автозагрузки
journalctl -u gunicorn # Посмотреть логи Gunicorn
Полезные советы
После изменений всегда выполняй:
sudo systemctl daemon-reload
- Если видишь ошибку типа Permission denied, проверь владельца и права доступа к директории и сокету.
- Используй Restart=on-failure, чтобы Gunicorn перезапускался автоматически при падениях (но не при systemctl stop).
Проверка
После настройки запусти:
sudo systemctl start gunicorn
sudo systemctl enable gunicorn
sudo systemctl status gunicorn
И проверь сокет:
ls -la /home/alex/myproject/gunicorn.sock
Вывод
systemd — это ключевой инструмент для управления продакшн-процессами в Linux.
В связке с Gunicorn он позволяет надёжно и удобно запускать Django-приложение как полноценный сервис, работающий 24/7.
Что такое Nginx в связке с Django 5 и Gunicorn?
Что такое Nginx
Nginx (читается как "энджин-икс") — это высокопроизводительный веб-сервер и обратный прокси. Он может:
- обрабатывать HTTP-запросы;
- выступать в роли реверс-прокси (перенаправлять запросы к backend-серверу, например, Gunicorn);
- работать как балансировщик нагрузки;
- раздавать статические и медиа-файлы;
- защищать приложение через SSL, ограничение доступа, лимиты и фильтрацию трафика.
Где находится Nginx в архитектуре Django-проекта
Вот упрощённая архитектура:
Пользователь → Nginx → Gunicorn → Django (WSGI/ASGI) → БД, файлы и пр.
- Nginx принимает все входящие HTTP/HTTPS-запросы от клиентов.
- Он либо:
отдаёт сам статический/медиа-контент;
либо проксирует запросы на Gunicorn. - Gunicorn запускает Django-приложение и обрабатывает Python-код.
Почему Nginx, а не напрямую Gunicorn
Gunicorn — это WSGI-сервер, который умеет запускать Django-приложение, но не предназначен для:
- раздачи статики;
- обработки большого количества одновременных соединений;
- управления HTTPS/SSL;
- защиты от DDoS, ограничения трафика, кэширования и сжатия.
Nginx решает все эти задачи на системном уровне и очень эффективно, снимая нагрузку с Python-приложения.
Преимущества использования Nginx в Django-проекте
Примеры задач, которые Nginx решает лучше Python-приложения
- Ограничение частоты запросов (limit_req_zone).
- Защита от ботов/сканеров (deny, allow, user_agent).
- HTTP → HTTPS редиректы.
- Кэширование GET-запросов с параметрами (proxy_cache).
- Gzip-сжатие (gzip on;).
- Базовая аутентификация (auth_basic).
Ограничения и что не стоит делать через Nginx
Почему Nginx нужен для продакшна Django-проекта
В продакшене Nginx обязателен, потому что:
- без него не будет HTTPS;
- безопасность Django-приложения будет под угрозой;
- нагрузка возрастёт — статика и медиа будут обрабатываться Python-кодом;
- Gunicorn при перегрузке будет падать без graceful error pages.
Nginx выступает как щит, менеджер трафика, ускоритель и гарант стабильности.
Заключение
Nginx — это обязательный компонент продакшн-инфраструктуры Django-проекта. Он даёт защиту, производительность, масштабируемость и удобную маршрутизацию трафика, снимая с Python-приложения всё, что не относится к бизнес-логике.
Установка Nginx
Выполните команду:
sudo apt update
sudo apt install nginx -y
Проверьте статус:
sudo systemctl status nginx
Настройка Nginx как обратного прокси-сервера
Создайте файл конфигурации:
sudo vim /etc/nginx/sites-available/myproject
Пример конфигурации HTTP (без SSL):
Проверка Nginx и перезапуск
После внесения любых изменений:
sudo nginx -t && sudo systemctl reload nginx
Проверьте доступность:
- curl -I http://f666.ru
- curl -I https://f666.ru
Обновление сертификатов Certbot (в фоне)
Certbot сам настраивает автообновление, но можно вручную проверить:
sudo certbot renew --dry-run
Что мы получили?
Рекомендации по доработке:
- Добавить редирект с HTTP на HTTPS.
- Добавить серверный блок listen 443 ssl с указанием сертификатов от Let's Encrypt.
- Улучшить безопасность и производительность (gzip, заголовки).
Обновленная версия
Что нужно сделать перед запуском:
1. Установить Certbot:
sudo apt install certbot python3-certbot-nginx
2. Выпустить SSL-сертификат:
sudo certbot --nginx -d f666.ru -d www.f666.ru
3. Проверить конфигурацию и перезапустить Nginx:
sudo nginx -t && sudo systemctl reload nginx
Как должен работать Nginx в проекте Django + Gunicorn?
Пользователь обращается через браузер к вашему веб приложению на порт 80 если у вас не настроен HTTPS и на порт 443 если HTTPS настроен и работает. Кроме того вы через SSH должны иметь доступ к порту 22, чтобы администрировать сервер.
Сейчас у нас веб приложение Django работает на порт 8000, поэтому Nginx как обратный реверс прокси должен передать запрос с порта 80 или 443 на порт 8000 и порт 8000 извне должен быть закрыт - доступ через браузер на порт 8000 напрямую должен быть заблокирован.
В свою очередь Django получив запрос и обработав его должно вернуть ответ на порт 8000, а Nginx должен передать его на порт 80 или 443, чтобы ответ ушел пользователю в браузер.
Чтобы закрыть все порты кроме 80, 443 и 22 на доступ из вне необходимо испоьзовать файервол ufw.
Что такое файервол и зачем он нужен в продакшн-проекте Django с Nginx и Gunicorn
Файервол (firewall) — это программный или аппаратный барьер между сервером и внешним миром, который контролирует входящий и исходящий сетевой трафик на основе заданных правил безопасности. Он позволяет разрешать или блокировать доступ к определённым портам и IP-адресам, предотвращая несанкционированный доступ к критически важным компонентам системы.
Что такое ufw и почему именно он на Ubuntu
На Ubuntu по умолчанию используется UFW (Uncomplicated Firewall) — это упрощённая обёртка над iptables, созданная для быстрого и понятного управления настройками файервола.
Основные преимущества ufw:
- Простота настройки через CLI: команды интуитивно понятны даже новичку.
- Гибкие политики: можно указать правила для отдельных портов, IP-адресов, интерфейсов и сервисов.
- Логирование подозрительных попыток подключения.
- Отличная интеграция с systemd и Ubuntu Server.
Ограничения:
- ufw не поддерживает сложные правила маршрутизации как iptables напрямую.
- Не самая лучшая масштабируемость при управлении сотнями правил (в таких случаях применяют nftables или firewalld).
Как ufw помогает безопасно организовать работу Django + Gunicorn + Nginx
Веб-приложение на Django, работает на порту 8000 через Gunicorn, а Nginx настроен как обратный прокси (reverse proxy) и принимает HTTP/HTTPS-трафик на портах 80 и 443.
Схема взаимодействия:
- Пользователь обращается к сайту по адресу https://example.com → запрос идёт на порт 443 (HTTPS).
- Nginx принимает запрос и проксирует его на внутренний порт 8000, где работает Gunicorn (Django).
- Django формирует ответ и отправляет его обратно через Gunicorn на Nginx.
- Nginx возвращает ответ пользователю.
При этом порт 8000 должен быть доступен только локально (127.0.0.1). Если порт 8000 открыт извне — это уязвимость: кто угодно может обойти Nginx и напрямую взаимодействовать с Gunicorn, минуя слои защиты (rate-limiting, HTTPS, header sanitization и т.д.).
Настройка фаервола ufw
Чтобы гарантировать, что только порты 22, 80 и 443 доступны извне, а все остальные (включая 8000) закрыты выполни следующие команды:
Проверка установки ufw
sudo apt install ufw
Разрешить доступ из вне к портам:
sudo ufw allow 22/tcp # SSH
sudo ufw allow 80/tcp # HTTP
sudo ufw allow 443/tcp # HTTPS
Запретить доступ из вне ко всем остальным портам:
sudo ufw default deny incoming
sudo ufw default allow outgoing
Перед запуском и активацией ufw (Uncomplicated Firewall) очень важно понять, какие порты сейчас уже открыты и какие сервисы их используют, чтобы случайно не перекрыть себе доступ к серверу (особенно по SSH).
Вот пошаговая инструкция, как проверить открытые порты и понять, какие приложения их слушают:
Проверьте открытые порты и приложения, которые их используют:
sudo lsof -i -P -n | grep LISTEN
Что делает эта команда:
- lsof — показывает список открытых файлов и сокетов;
- -i — ограничивает вывод сетевыми соединениями;
- -P — показывает номера портов (а не имена служб);
- -n — не делает DNS-резолвинг IP-адресов;
- grep LISTEN — отфильтровывает только серверные сокеты.
Пример вывода:
Здесь видно:
- sshd слушает порт 22 на всех интерфейсах (*);
- nginx — 80 и 443;
- gunicorn слушает только на 127.0.0.1:8000, что правильно (не открыт вовне).
Проверить прослушиваемые порты с помощью ss (современная альтернатива netstat)
sudo ss -tuln
Опции:
- -t — TCP;
- -u — UDP;
- -l — только прослушиваемые (LISTEN);
- -n — не резолвить имена (показывать IP и порт).
Пример вывода:
Важно:
- Если вы видите 0.0.0.0:8000, это плохо — Gunicorn слушает на внешнем интерфейсе;
- Если 127.0.0.1:8000 — хорошо, только локально.
Проверить доступность портов извне (с локального ПК)
Чтобы убедиться, что какой-то порт доступен из интернета, можно использовать:
nmap -p 1-10000 <IP_твоего_сервера>
Или точечно:
nmap -p 22,80,443,8000 <IP_твоего_сервера>
Пропустить этап пинга и произвести скан портов:
nmap -Pn -p 22,80,443,8000 <IP_твоего_сервера>
nmap — нужно установить отдельно (sudo apt install nmap).
Заключение:
После анализа этих команд можно с уверенностью активировать ufw, не потеряв доступ к серверу, и грамотно закрыть лишние порты, такие как 8000, если они не предназначены для публичного доступа.
Включить ufw:
sudo ufw enable
Проверить статус:
sudo ufw status verbose
Теперь:
- Порт 8000 будет недоступен из внешней сети — безопасность приложению обеспечена.
- Nginx и SSH будут работать штатно, так как нужные порты открыты.
- Сервер не будет "светиться" в интернете, как только установлен — это минимизирует риск атак типа порт-сканирования, brute force, DoS.
Заключение:
Роль ufw в архитектуре Django + Nginx + Gunicorn
ufw — это первая линия обороны, обеспечивающая сетевую изоляцию вашего веб приложения. В архитектуре с Gunicorn и Nginx это особенно важно:
Без использования ufw или подобного инструмента, вы оставляете сервер уязвимым для атаки напрямую на WSGI-сервер, который не предназначен для публичного взаимодействия.
Используйте ufw как часть production-стандарта по безопасности для всех Django-проектов, развернутых на VPS.
Настройка Nginx
В reg.ru зарегистрировал домен для экспериментов f666.ru. Не реклама, просто мне удобно пользоваться reg.ru.
Будем считать, что вы зарегистрировали домен в reg.ru.
Кликните по имени домена, которое вы зарегистрировали, как показано на рисунке выше.
Результат:
Кликните по полю: DNS-серверы и управление зоной.
Результат:
Нам нужно установить ip адрес удаленного сервера, с которым мы работаем, для каждой A записи.
Минут 30 нужно подождать пока ваш ip адрес прогрузится на DNS-серверах.
В адресной строке браузера Введите имя вашего домена:
http://f666.ru/
Результат:
Вы должны увидеть стандартную заставку Nginx, которую он показывает когда не настроен.
Мы подключили свой домен к серверу. Теперь все готово к тому, чтобы настроить Nginx.
Напомню правило по которому должен работать Nginx:
1. Nginx ожидает HTTP запросы на порт 80 и HTTPS запросы на порт 443. Все остальные порты кроме 22 порт, по котому мы подключаемся по SSH к серверу, должны быть закрыты. Мы это сделали выше с помощью фаервола uwf.
2. После того как Nginx получил HTTP запрос он должен понять что нужно сделать:
- отдать статику (css, картинки, видео, файлы и т.д.)
- передать запрос содержащий скрипт обработчик WSGI серверу Gunicorn на порт 8000, чтобы его обработал Django.
3. После того как Django обработает запрос он передает ответ WSGI серверу Gunicorn на порт 8000. Nginx должен забрать ответ с порта 8000 и передать на порт 80 если HTTP запрос или 443 если HTTPS запрос.
Давайте реализуем эту логику работы в настройках Nginx.
1. Соберем статику нашего веб приложения Django в папку static с помощью команды:
Как настроить HTTPS на поддомене
Установите certbot:
sudo apt install certbot python3-certbot-nginx
Настройте автоматически HTTPS на вашем поддомене:
sudo certbot --nginx -d taski.f666.ru