Сайт на VPS с nginx и Let's Encrypt: от домена до HTTPS

Как поднять сайт или API на VPS: настроить DNS, открыть порты 80 и 443, проксировать приложение через nginx и получить бесплатный сертификат Let's Encrypt с автопродлением.

Команда EGN 8 мин чтения
Сайт на VPS с nginx и Let's Encrypt: от домена до HTTPS
Содержание 19
  1. Коротко
  2. Что нужно до начала
  3. Шаги
  4. Фаервол в кабинете EGN
  5. Что должно быть открыто наружу
  6. Примеры
  7. Симптом → причина → решение
  8. Пример: WebSocket через nginx
  9. Как проверить настройку
  10. Что дальше
  11. Частые ошибки
  12. Итог
  13. Чек-лист после выпуска сертификата
  14. Пример: один адрес вместо двух
  15. Вопросы и ответы
  16. Нужен ли платный сертификат?
  17. Можно ли обойтись без nginx?
  18. Почему certbot пишет, что домен не прошёл проверку?
  19. Что делать, если нужен только IPv4?

Сайт на VPS нужен не только владельцам сайтов. Страница статистики сервера, панель бота, форма заявок и API для модов — всё это веб-сервисы, которым нужны домен и HTTPS. Схема одна и та же: приложение слушает внутри машины, nginx принимает запросы из интернета и передаёт их дальше, а Let’s Encrypt выдаёт бесплатный сертификат.

Коротко

Направьте A-запись домена на IP вашего VPS, поставьте nginx и certbot, настройте proxy_pass на приложение, которое слушает 127.0.0.1:8000, и выпустите сертификат командой sudo certbot --nginx -d example.com. Снаружи открыты только порты 22, 80 и 443, а приложение недоступно напрямую. Продление certbot делает автоматически. На ноябрь 2026 Let’s Encrypt выдаёт сертификаты на 90 дней.

Что нужно до начала

Нужен VPS с Ubuntu 24.04 и домен, которым вы управляете. Сначала создайте у регистратора или DNS-провайдера запись типа A: имя домена указывает на IPv4 вашего сервера. Let’s Encrypt проверяет владение доменом, обращаясь на порт 80, поэтому порт должен быть доступен. Пока DNS не обновился, сертификат не выпустится.

Если у сервера есть IPv6, добавьте и запись AAAA, но только если nginx слушает IPv6. Иначе часть пользователей получит ошибку подключения.

Шаги

  1. Установите nginx и certbot. Certbot рекомендует установку через snap, но пакет из репозитория Ubuntu тоже работает с плагином nginx:
Терминал
sudo apt update
sudo apt install -y nginx certbot python3-certbot-nginx
  1. Откройте порты. Это сразу ограничивает поверхность атаки: SSH, HTTP и HTTPS, всё остальное закрыто.
Терминал
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status

Внимание: сначала разрешите SSH, потом включайте ufw, иначе потеряете доступ к серверу.

  1. Запустите приложение на локальном порту. Пример — Python-сервис на порту 8000, который слушает только 127.0.0.1. Так приложение не видно снаружи.

  2. Создайте конфиг сайта /etc/nginx/sites-available/example.com:

nginx
server {
    listen 80;
    server_name example.com www.example.com;

    location / {
        proxy_pass http://127.0.0.1:8000;
        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;
    }
}
  1. Включите сайт и проверьте конфиг:
Терминал
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
  1. Выпустите сертификат. Certbot сам добавит блок listen 443 ssl, пути к сертификату и редирект с HTTP на HTTPS:
Терминал
sudo certbot --nginx -d example.com -d www.example.com
  1. Проверьте продление. Certbot ставит таймер или cron-задание, которое обновляет сертификат, когда до конца срока остаётся меньше 30 дней. Проверка в тестовом режиме:
Терминал
sudo certbot renew --dry-run
Путь запроса: интернет, nginx на 80 и 443, приложение на localhost
Путь запроса: интернет, nginx на 80 и 443, приложение на localhost

Фаервол в кабинете EGN

На вкладке «Фаервол» карточки VPS есть два режима: «Всё открыто» (входящие подключения на любые порты, ограничивать их нужно внутри системы) и «Свои правила» (разрешено только то, что в списке, остальное блокируется ещё до сервера). Для сайта в режиме «Свои правила» есть быстрые пресеты SSH, HTTP и HTTPS, то есть порты 22, 80 и 443. Если вы убрали порт 22, консоль в панели продолжит работать, и доступ можно вернуть. Либо пользуйтесь одним ufw внутри системы, либо облачным фаерволом, но понимайте, что правила действуют на двух уровнях и должны совпадать.

Что должно быть открыто наружу

Правильная схема выглядит так: снаружи доступны только 22, 80 и 443. Приложение на 8000 и база данных на 5432 закрыты, даже если они запущены на том же сервере. Таблица ниже показывает, что видит интернет:

Открытые и закрытые порты для сайта на VPS
Открытые и закрытые порты для сайта на VPS

Порт 80 оставляйте открытым и после выпуска сертификата. Через него Let’s Encrypt продлевает сертификат, а nginx перенаправляет пользователей на HTTPS.

Примеры

Сайт на WordPress или статическая страница. Схема та же, только proxy_pass не нужен: корень сайта указывается через root и index. Certbot всё равно выпускает сертификат тем же способом.

API для мода или бота. Если бот работает через webhook, как описано в статье Telegram-бот на VPS: long polling или webhook, адрес webhook должен быть HTTPS на одном из портов 443, 80, 88 или 8443. Удобнее всего 443 через nginx.

Несколько доменов на одном VPS. Для каждого домена создаётся свой блок server с server_name. Certbot принимает несколько -d в одной команде или выпускает отдельные сертификаты.

Симптом → причина → решение

Симптом Частая причина Что сделать
Connection refused от nginx Приложение не запущено или слушает другой порт Проверьте ss -tlnp и порт в proxy_pass
502 Bad Gateway Приложение упало или не отвечает Смотрите журнал приложения и /var/log/nginx/error.log
413 Request Entity Too Large Загрузка больше лимита nginx Добавьте client_max_body_size 20m; в блок server
Ошибка WebSocket Не переданы заголовки Upgrade Добавьте заголовки из примера ниже
Сертификат есть, но браузер пишет «небезопасно» Смешанный контент или старый кеш Проверьте адреса ресурсов на https:// и очистите кеш
Certbot не продлевает сертификат Порт 80 закрыт или таймер отключён Проверьте ufw status и systemctl list-timers

Пример: WebSocket через nginx

Если приложение работает с WebSocket, nginx нужно передавать заголовки обновления соединения. Добавьте в блок location такие строки:

nginx
location /ws/ {
    proxy_pass http://127.0.0.1:8000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_read_timeout 300s;
}

Без proxy_http_version 1.1 и заголовка Upgrade соединение обрывается сразу после рукопожатия. Таймаут proxy_read_timeout не даёт nginx закрыть долгое соединение через минуту простоя.

Как проверить настройку

После изменений выполните три проверки. Первая — синтаксис конфига, вторая — ответ сервера, третья — срок сертификата:

Терминал
sudo nginx -t
curl -I https://example.com
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -enddate

Команда openssl покажет дату окончания сертификата. Если она близка, а certbot renew --dry-run проходит, проблема в таймере, а не в конфиге.

Что дальше

Срок сертификатов будет сокращаться (см. вопрос про платный сертификат ниже), поэтому автоматическое продление нужно проверять регулярно: sudo certbot renew --dry-run раз в месяц и systemctl list-timers | grep certbot после обновлений системы.

Когда сайт заработал, полезно подумать о резервных копиях конфигов и сертификатов. Копии /etc/nginx и /etc/letsencrypt занимают мало места, их стоит включать в общий бэкап VPS, как описано в статье Бэкапы VPS: снапшоты, S3 и restic.

Частые ошибки

  • certbot не может проверить домен. Проверьте A-запись командой dig +short example.com: адрес должен совпадать с IP сервера. Проверьте, что порт 80 открыт, и что другой веб-сервер не занимает порт.
  • Ошибка nginx: [emerg] bind() to 0.0.0.0:80 failed. На порту 80 уже работает другой процесс. Найдите его командой sudo ss -tlnp | grep ':80'.
  • 502 Bad Gateway. nginx не может достучаться до приложения. Проверьте, что приложение запущено и слушает порт из proxy_pass.
  • Приложение видно на порту 8000 из интернета. Оно слушает 0.0.0.0, а не 127.0.0.1. Поправьте адрес в настройках приложения.
  • Сертификат истёк. Проверьте таймер systemctl list-timers | grep certbot и результат certbot renew --dry-run. Если они не работают, ищите причину в журнале /var/log/letsencrypt/.
  • Приложение не видит IP пользователя. Без заголовка X-Forwarded-For в приложении все запросы идут с адреса 127.0.0.1. Передавайте заголовки, как в примере.

Итог

Сайт на VPS за nginx — это простая и надёжная схема: приложение закрыто, nginx принимает HTTPS, certbot продлевает сертификат сам. Главное — не открывать порт приложения и порт базы наружу, а порт 80 оставить для проверки и продления. Если сайту нужна база данных, переходите к статье База данных на VPS: MySQL или PostgreSQL без утечек в интернет, а если сервисов несколько — к Docker Compose на VPS.

Чек-лист после выпуска сертификата

После команды certbot --nginx проверьте сайт по пунктам:

  1. Открывается https://example.com без предупреждений браузера.
  2. http://example.com перенаправляет на HTTPS.
  3. www и основной домен ведут на один и тот же сайт, либо один из них перенаправляет на другой.
  4. В sudo nginx -t нет предупреждений.
  5. sudo certbot renew --dry-run проходит без ошибок.
  6. Порт 80 открыт в ufw и не занят другим процессом.

Пример: один адрес вместо двух

Чтобы www.example.com всегда вёл на example.com, добавьте отдельный блок server, который делает постоянный редирект:

nginx
server {
    listen 443 ssl;
    server_name www.example.com;
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    return 301 https://example.com$request_uri;
}

Сертификат для www должен быть в том же запросе certbot, иначе браузер покажет ошибку имени. Поэтому выпускайте сертификат сразу для обоих имён через -d example.com -d www.example.com.

Вопросы и ответы

Нужен ли платный сертификат?

Нет. Let’s Encrypt выдаёт бесплатные сертификаты, certbot продлевает их автоматически. Сейчас срок 90 дней, с 10 февраля 2027 года по умолчанию будет 64 дня, а с 16 февраля 2028 года 45 дней. Если вы пользуетесь certbot с таймером, ничего менять не нужно; свои скрипты с жёстким «продлевать раз в 60 дней» пересмотрите.

Можно ли обойтись без nginx?

Можно, если приложение само умеет HTTPS, но nginx проще для нескольких сервисов и даёт удобную настройку заголовков и редиректов.

Почему certbot пишет, что домен не прошёл проверку?

Чаще всего A-запись ещё не обновилась или порт 80 закрыт фаерволом. Проверьте оба пункта до повторного запуска.

Что делать, если нужен только IPv4?

Ничего, запись AAAA не обязательна. Главное, чтобы A-запись вела на правильный адрес.

Сервер под игры за пару минут — egnetwork.ru

Читать далее

Вся рубрика →