В прошлых двух уроках мы полностью развернули filmsite: сервер, домен, Git, Django, Gunicorn, Nginx. Сайт уже работает по http://filmsite.ru. Осталось решить последнюю задачу — перевести сайт на защищённый протокол HTTPS.
После этого разберём три важные вещи, которые пригодятся вам не только сейчас, а на протяжении всей дальнейшей работы с проектом:
- расширенный чек-лист типичных ошибок деплоя — что делать, когда что-то не работает;
- как вносить правки в код на уже работающем сервере, не «ломая» сайт;
- итоговый план-шпаргалка по всему деплою — чтобы не пересматривать три урока каждый раз.
Если сайт открывается по адресу вида http://filmsite.ru, браузер помечает его как небезопасный, часть функционала браузеры могут блокировать, поисковые системы понижают такой сайт в выдаче, а авторизация и передача данных происходят без шифрования.
SSL-сертификат:
- шифрует данные между браузером пользователя и сервером;
- подтверждает, что домен действительно принадлежит вам;
- позволяет использовать протокол
https://.
Мы используем бесплатный и полностью легальный сертификат от Let's Encrypt — он действует 90 дней и автоматически продлевается.
Certbot сам получает сертификат, автоматически настраивает Nginx и умеет продлевать сертификаты без нашего участия.
sudo apt install certbot python3-certbot-nginx -ycertbot— основная утилита;python3-certbot-nginx— модуль, который автоматически интегрируется с Nginx и правит его конфигурацию.
Вместо filmsite.ru укажите свой домен:
sudo certbot --nginx -d filmsite.ru -d www.filmsite.ruВ процессе Certbot:
- попросит указать email — на него могут приходить уведомления о проблемах с сертификатом;
- попросит подтвердить условия использования — вводим
y; - спросит про перенаправление HTTP → HTTPS — соглашаемся, выбираем автоматический редирект.
Если всё прошло успешно — вы увидите сообщение о том, что сертификат выдан.
https://filmsite.ru
Ожидаем: сайт открывается по HTTPS, в адресной строке — замок, предупреждений нет. Также http://filmsite.ru должен автоматически перенаправлять на HTTPS.
Сертификаты Let's Encrypt действуют 90 дней, но продлевать их вручную не нужно — Certbot устанавливает системный таймер:
sudo systemctl status certbot.timerСтатус active — сертификат будет продлеваться автоматически.
Проверить, что продление точно сработает, можно через симуляцию (сертификат реально не обновляется):
sudo certbot renew --dry-runЕсли команда завершилась без ошибок — при реальном истечении срока сертификат продлится сам.
Ниже — самые частые проблемы именно на этапе первого деплоя, в формате «симптом → причина → как проверить → как исправить». Возвращайтесь к этому разделу каждый раз, когда что-то идёт не так.
Симптом: Nginx отдаёт страницу с ошибкой 502 вместо сайта.
Причина: Nginx работает и принимает запрос, но не может получить ответ от Gunicorn — либо Gunicorn не запущен, либо путь к .sock-файлу в конфиге Nginx не совпадает с реальным.
Как проверить:
sudo systemctl status gunicorn
ls -la /var/www/filmsite/filmsite.sockКак исправить:
- если статус не
active (running)— смотрим причину падения:sudo journalctl -u gunicorn -n 50; - если сокет отсутствует или лежит в другом месте — сверьте путь
--bind unix:...вgunicorn.serviceс путёмproxy_pass http://unix:...в конфиге Nginx, они должны совпадать буква в букву; - после любого исправления:
sudo systemctl restart gunicorn.
Симптом: до подключения SSL все формы (вход, добавление фильма, рецензии) работали, а после перехода на HTTPS — при отправке любой формы Django возвращает 403 с текстом «CSRF verification failed».
Причина: Django, начиная с версии 4, дополнительно проверяет заголовок Origin запроса для защищённых (HTTPS) соединений за прокси. Если домен не указан в CSRF_TRUSTED_ORIGINS — проверка не проходит.
Как исправить: добавьте в settings.py:
CSRF_TRUSTED_ORIGINS = [
'https://filmsite.ru',
'https://www.filmsite.ru',
]Обязательно с https:// в начале — именно так, как обращается к сайту браузер после редиректа. После изменения — sudo systemctl restart gunicorn.
Симптом: сайт возвращает 400-ю ошибку, в логах Gunicorn — DisallowedHost.
Причина: домен, IP или поддомен, по которому пришёл запрос, не указан в ALLOWED_HOSTS.
Как проверить:
sudo journalctl -u gunicorn -n 30 | grep DisallowedHostВ сообщении будет указано, какой именно хост Django отклонил.
Как исправить: добавьте недостающее значение в ALLOWED_HOSTS в settings.py (это тот же список, который мы настраивали в уроке 34 — просто нужно свериться, не изменился ли домен/IP), затем sudo systemctl restart gunicorn.
Симптом: сайт открывается, но без стилей, либо не показываются постеры/аватары.
Возможные причины, по частоте:
- Забыли выполнить
collectstaticпосле последних изменений — самая частая причина. Проверка:ls /var/www/filmsite/static/— если папка пустая или устарела, выполнитеpython manage.py collectstaticзаново. - Путь в конфиге Nginx не совпадает с
STATIC_ROOT/MEDIA_ROOT. Откройте/etc/nginx/sites-available/filmsiteи сверьте значенияrootв блокахlocation /static/иlocation /media/с реальными путями изsettings.py. - Права доступа. Проверка и исправление:
sudo chmod 755 /var/www/filmsite/static sudo chmod 755 /var/www/filmsite/media
Проверяйте причины именно в этом порядке — права доступа на практике оказываются виноваты реже, чем забытый collectstatic или несовпадение путей, но именно к правам почему-то тянутся в первую очередь.
Симптом: вы сделали git push локально, git pull на сервере прошёл без ошибок, но в браузере видна старая версия сайта.
Причина: Gunicorn держит код Python-приложения загруженным в память своих worker-процессов. git pull обновляет файлы на диске, но уже запущенные процессы Gunicorn продолжают работать со старой версией кода до перезапуска.
Как исправить: после каждого git pull, если менялся Python-код (models, views, forms, urls) — обязательно:
sudo systemctl restart gunicornЕсли менялись только модели — не забудьте про миграции перед перезапуском:
python manage.py migrate
sudo systemctl restart gunicornЕсли менялась статика (CSS/JS/шаблоны, влияющие на собранные файлы) — потребуется collectstatic:
python manage.py collectstaticСимптом: django.db.utils.OperationalError: could not connect to server или password authentication failed for user.
Возможные причины:
- Неверный пароль/имя пользователя/базы в
DATABASESвsettings.py— сверьте с тем, что вы указывали приCREATE USER/CREATE DATABASEв уроке 33. - PostgreSQL не запущен:
sudo systemctl status postgresql
- Неверный
HOST. Если PostgreSQL слушает через Unix-сокет, а не TCP — иногда помогает'HOST': ''(пустая строка) вместо'localhost', поскольку это заставляет драйвер использовать сокет напрямую. Это скорее нюанс конкретной конфигурации сервера, чем правило — если'localhost'не работает, стоит попробовать пустое значение.
Симптом: после sudo systemctl restart nginx сайт вообще перестал открываться — не только через HTTPS, но и полностью.
Причина: синтаксическая ошибка в конфигурационном файле — например, забытая точка с запятой или незакрытая скобка в блоке server {}.
Как избежать: всегда проверяйте синтаксис перед перезапуском:
sudo nginx -tЕсли видите syntax is ok и test is successful — можно перезапускать. Если нет — Nginx покажет строку с ошибкой прямо в выводе команды.
Частый вопрос новичков: «Мне нужно поправить файл через nano, а сервер запущен — придётся его останавливать?»
Нет. Открыть nano (или vim) и отредактировать любой файл проекта можно в любой момент, независимо от того, работает сайт или нет. Текстовый редактор и запущенные процессы Gunicorn/Nginx — это совершенно независимые друг от друга программы, они не мешают друг другу обращаться к одним и тем же файлам на диске.
Важно другое — что именно нужно сделать после сохранения правки, чтобы изменения реально применились на сайте. Это зависит от типа файла:
| Что вы изменили | Нужно ли что-то после сохранения |
|---|---|
Python-код: views.py, models.py, forms.py, urls.py, settings.py |
Да — sudo systemctl restart gunicorn. Gunicorn держит код в памяти worker-процессов и не видит изменений на диске сам по себе. |
| Модели (добавили поле, новую таблицу) | Сначала python manage.py migrate, затем sudo systemctl restart gunicorn. |
HTML-шаблоны (.html в templates/) |
Обычно нет — Django по умолчанию читает шаблоны заново при каждом запросе. Перезапуск не помешает, но чаще всего не обязателен. |
| Статика (CSS/JS в исходных папках приложений) | Да — python manage.py collectstatic, чтобы файлы попали в STATIC_ROOT, откуда их раздаёт Nginx. Gunicorn перезапускать не нужно. |
Конфиг Nginx (/etc/nginx/sites-available/filmsite) |
Да, но не restart, а reload: сначала sudo nginx -t, затем sudo systemctl reload nginx. |
Для Gunicorn мы всегда используем restart — сервис останавливается и запускается заново с новым кодом в памяти. Это занимает доли секунды и обычно проходит незаметно для пользователей благодаря механизму systemd, но кратковременно оборвёт активные запросы, если они как раз попали в этот момент.
Для Nginx предпочтительнее reload, а не restart:
sudo systemctl reload nginxreload перечитывает конфигурацию без разрыва уже установленных соединений — Nginx продолжает обслуживать текущие запросы старой конфигурацией, пока не завершит их, и только новые запросы попадают под обновлённые правила. restart полностью останавливает и поднимает процесс заново, что для веб-сервера, который может обслуживать не только ваш проект, — более грубый и рискованный способ.
- Подключаемся по SSH.
- Открываем нужный файл:
sudo nano путь/к/файлу(для системных файлов вроде конфига Nginx — обязательно черезsudo, для файлов проекта в/var/www/filmsite— обычно можно безsudo, если вы работаете отroot). - Вносим правку, сохраняем (
Ctrl+O,Enter,Ctrl+Xв nano). - В зависимости от типа файла — смотрим таблицу выше и выполняем нужную команду (
restart gunicorn,collectstatic,nginx -t+reload). - Проверяем результат в браузере.
Важная оговорка на будущее: постоянно редактировать код прямо на сервере через
nano— рабочий способ для быстрой правки в экстренной ситуации, но не для повседневной разработки. Стандартный процесс — вносить изменения локально, коммитить, пушить на GitHub и подтягивать черезgit pullна сервере (как мы делали в уроке 33). Прямые правки на сервере имеют свойство «потеряться» — если вы в следующий раз сделаетеgit pull, а локальная версия файла отличается от той, что вы поправили вручную на сервере,git pullможет конфликтовать или просто перезаписать вашу правку.
Компактная версия всего пройденного в модуле 9 — для быстрого обращения в будущем, без необходимости пересматривать все три урока целиком.
- Арендовать VPS (Ubuntu 24.04) и зарегистрировать домен — Урок 33, Шаги 2–3.
- Привязать домен к IP сервера через A-запись, дождаться распространения DNS — Урок 33, Шаг 4.
- Подключиться к серверу по SSH — Урок 33, Шаг 5.
- Обновить систему и установить пакеты: Python, venv, Nginx, при необходимости PostgreSQL — Урок 33, Шаг 6.
- [PostgreSQL] Создать базу данных и пользователя — Урок 33, Шаг 7.
- Подготовить
requirements.txtлокально и перенести код через Git (git cloneна сервере) — Урок 33, Шаг 8. - Создать виртуальное окружение и установить зависимости — Урок 33, Шаг 9.
- Настроить
settings.py:ALLOWED_HOSTS,DATABASES,STATIC_ROOT,MEDIA_ROOT— Урок 34, Шаги 1–3. - Выполнить миграции и
collectstatic— Урок 34, Шаг 4. - Проверить сайт через
runserver, затем черезgunicornнапрямую — Урок 34, Шаги 5, 8. - Переключить
DEBUG = False— Урок 34, Шаг 7. - Настроить Gunicorn как systemd-сервис — Урок 34, Шаг 9.
- Настроить и проверить конфиг Nginx (
nginx -tперед перезапуском!) — Урок 34, Шаг 10. - Открыть нужные порты через firewall — Урок 34, Шаг 11.
- Вынести секреты (
SECRET_KEY, пароль от БД) в.env— Урок 34, Бонус. - Получить SSL-сертификат через Certbot — Урок 35, начало.
- [После HTTPS] Добавить
CSRF_TRUSTED_ORIGINS, если формы начали отдавать 403 — Урок 35, чек-лист ошибок. - При каждом обновлении кода:
git pull→ (если менялись модели)migrate→ (если менялась статика)collectstatic→restart gunicorn. - При правке конфига Nginx:
nginx -t→reload nginx(неrestart).
Этот список можно дополнять собственными пометками и ссылками — он и задумывался как рабочий чек-лист, а не как исчерпывающая документация: подробности при необходимости всегда можно найти в соответствующем уроке.