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

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

- Рубрика: Гайды по играм
- Игра: VPS
- Опубликовано: 2026-11-13T17:20:00+03:00
- Обновлено: 2026-11-13T17:20:00+03:00
- Адрес: https://blog.egnetwork.ru/vps-sait-nginx-letsencrypt
- Автор: Команда EGN

Сайт на 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:

```bash
sudo apt update
sudo apt install -y nginx certbot python3-certbot-nginx
```

2. Откройте порты. Это сразу ограничивает поверхность атаки: SSH, HTTP и HTTPS, всё остальное закрыто.

```bash
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status
```

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

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

4. Создайте конфиг сайта `/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;
    }
}
```

5. Включите сайт и проверьте конфиг:

```bash
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
```

6. Выпустите сертификат. Certbot сам добавит блок `listen 443 ssl`, пути к сертификату и редирект с HTTP на HTTPS:

```bash
sudo certbot --nginx -d example.com -d www.example.com
```

7. Проверьте продление. Certbot ставит таймер или cron-задание, которое обновляет сертификат, когда до конца срока остаётся меньше 30 дней. Проверка в тестовом режиме:

```bash
sudo certbot renew --dry-run
```

![Путь запроса: интернет, nginx на 80 и 443, приложение на localhost](https://blog.egnetwork.ru/media/654)

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

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

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

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

![Открытые и закрытые порты для сайта на VPS](https://blog.egnetwork.ru/media/655)

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

## Примеры

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

**API для мода или бота.** Если бот работает через webhook, как описано в статье [Telegram-бот на VPS: long polling или webhook](https://blog.egnetwork.ru/vps-telegram-bot), адрес 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 закрыть долгое соединение через минуту простоя.

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

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

```bash
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](https://blog.egnetwork.ru/vps-bekapy-snapshot-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 без утечек в интернет](https://blog.egnetwork.ru/vps-baza-dannykh-mysql-postgresql), а если сервисов несколько — к [Docker Compose на VPS](https://blog.egnetwork.ru/vps-docker-compose-neskolko-servisov).

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

После команды `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](https://egnetwork.ru/)

## Официальные источники

- [127.0.0.1](http://127.0.0.1:8000/)
- [`](https://`/)
- [example.com](https://example.com/)
- [example.com`](https://example.com`/)
- [example.com`](http://example.com`/)
- [example.com$request_uri](https://example.com$request_uri/)

---

Материал EGN (blog.egnetwork.ru), обновлено 13 ноября 2026; при цитировании указывайте ссылку.
