Как разместить сайт на VPS: Nginx, PHP-FPM и HTTPS за вечер
От пустого сервера до сайта по HTTPS: защита SSH, файрвол, виртуальный хост Nginx, DNS, certbot, заголовки безопасности и что делать после запуска.

Коротко: чтобы разместить сайт на VPS, нужны сервер с Ubuntu или Debian, домен и вечер времени. Порядок такой: заходим по SSH-ключу, создаём отдельного пользователя, включаем ufw, ставим Nginx (и PHP-FPM, если сайт на PHP), кладём файлы в /var/www, направляем A-запись домена на IP, выпускаем бесплатный сертификат Let's Encrypt через certbot. После этого настраиваем бэкапы и обновления, иначе через полгода сервер станет проблемой.
Виртуальный хостинг удобен, пока сайт — это пара страниц на WordPress. Как только нужен свой стек (Node.js, Python, нестандартная версия PHP, очередь задач, cron каждую минуту) или просто хочется понимать, что происходит с сервером, остаётся VPS. Он стоит примерно столько же, сколько хороший хостинг, но вся ответственность за систему теперь на вас: обновления, права, сертификаты и резервные копии никто за вас не сделает.
Ещё один частый сценарий: сайт вам делает подрядчик, а размещать его предлагают на «своём» сервере. Даже если разработку ведёт студия вроде YuSMP Group, заранее договоритесь, на чём собран сайт, какие версии PHP или Node.js ему нужны и кто отвечает за сервер после сдачи. С этим списком VPS настраивается за вечер, без него можно потратить неделю на угадывание. Ниже пошагово: от пустого сервера до сайта по HTTPS, который не стыдно оставить без присмотра.
Что нужно подготовить до начала?
До первой команды проверьте, что у вас есть всё из списка. Половина «не работает» в таких инструкциях оказывается не ошибкой в Nginx, а отсутствующей DNS-записью или закрытым портом у провайдера.
- VPS с Ubuntu 24.04 LTS или Debian 12/13. Для лендинга или блога хватит 1 vCPU, 1–2 ГБ RAM и 20 ГБ SSD.
- Домен и доступ к панели управления DNS: регистратор, Cloudflare или DNS-хостинг провайдера.
- SSH-ключ на вашей машине. Если ключа ещё нет, создайте его по нашему гайду SSH-ключи вместо пароля.
- Файлы сайта: статическая сборка (HTML, CSS, JS), PHP-проект или репозиторий в Git.
- 15 минут на чтение документации вашего провайдера: где лежит консоль VNC на случай, если вы заблокируете себе SSH.
| Тип сайта | Минимальный VPS | Что ставим |
|---|---|---|
| Статика (лендинг, документация, сайт на генераторе) | 1 vCPU / 1 ГБ | Nginx |
| WordPress, небольшой PHP-сайт | 1 vCPU / 2 ГБ | Nginx, PHP-FPM, MariaDB |
| Node.js или Python-приложение | 2 vCPU / 2 ГБ | Nginx как reverse proxy, systemd-сервис |
| Интернет-магазин, несколько сайтов | 2–4 vCPU / 4 ГБ | Nginx, PHP-FPM, MariaDB/PostgreSQL, Redis |
Шаг 1. Первый вход и базовая защита сервера
Провайдер выдаёт IP и доступ под root. Под root работаем ровно столько, сколько нужно, чтобы создать обычного пользователя и закрыть вход паролем.
ssh root@203.0.113.10
apt update && apt upgrade -y
adduser deploy
usermod -aG sudo deploy
mkdir -p /home/deploy/.ssh
cp ~/.ssh/authorized_keys /home/deploy/.ssh/
chown -R deploy:deploy /home/deploy/.ssh
chmod 700 /home/deploy/.ssh && chmod 600 /home/deploy/.ssh/authorized_keys
Откройте второй терминал и проверьте вход: ssh deploy@203.0.113.10. Только убедившись, что он работает, отключайте пароль и root в /etc/ssh/sshd_config:
PermitRootLogin no
PasswordAuthentication no
Затем sudo systemctl restart ssh. Если ошиблись — выручит VNC-консоль провайдера. Подробнее о том, что ещё стоит закрыть в первый день, — в статье Защита Linux-сервера: база.
Файрвол ufw
Оставляем открытыми только SSH, HTTP и HTTPS. Порядок важен: сначала разрешаем SSH, потом включаем файрвол.
sudo apt install ufw -y
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verbose
У некоторых провайдеров есть ещё и сетевой файрвол в панели управления. Если порт 80 закрыт там, никакие правила ufw не помогут — проверьте оба уровня.
fail2ban и автообновления безопасности
Через час после запуска любого VPS в /var/log/auth.log появятся тысячи попыток подобрать пароль. С отключённым паролем они безвредны, но засоряют логи. fail2ban банит такие IP, а unattended-upgrades сам ставит патчи безопасности:
sudo apt install fail2ban unattended-upgrades -y
sudo systemctl enable --now fail2ban
sudo dpkg-reconfigure -plow unattended-upgrades
Шаг 2. Как установить Nginx и проверить, что он отвечает?
Nginx — стандартный выбор для VPS: он мало ест, быстро отдаёт статику и умеет проксировать запросы к PHP, Node.js или Python. Если сомневаетесь между ним и Apache, сравнение есть в статье Nginx vs Apache в 2026.
sudo apt install nginx -y
sudo systemctl enable --now nginx
curl -I http://localhost
В ответе должна быть строка HTTP/1.1 200 OK и заголовок Server: nginx. Теперь откройте в браузере http://IP-сервера: если видите приветственную страницу Nginx, сервер доступен снаружи. Если нет — проблема в файрволе провайдера, а не в Nginx.
Шаг 3. Куда класть файлы сайта и какие нужны права?
Принято держать каждый сайт в отдельном каталоге /var/www/<домен>. Владелец — ваш пользователь деплоя, Nginx нужны только права на чтение.
sudo mkdir -p /var/www/example.ru/public
sudo chown -R deploy:www-data /var/www/example.ru
sudo chmod -R 750 /var/www/example.ru
Загрузить файлы можно тремя способами:
- rsync с вашей машины — самый простой для статики:
rsync -avz --delete ./dist/ deploy@203.0.113.10:/var/www/example.ru/public/. - git clone на сервере — удобно, если сайт собирается из репозитория. Для приватного репозитория заведите на сервере отдельный deploy-ключ только на чтение.
- CI/CD — пайплайн сам собирает и выкатывает сайт после каждого коммита. Это следующий уровень, о нём — в гайде GitLab CI с нуля.
Не делайте chmod -R 777, даже если «так заработало». Это значит, что любой процесс на сервере, включая взломанный плагин, может переписать ваш сайт.
Шаг 4. Настраиваем виртуальный хост Nginx
Виртуальный хост (server block) говорит Nginx, какой домен обслуживать и откуда брать файлы. Создаём /etc/nginx/sites-available/example.ru:
server {
listen 80;
listen [::]:80;
server_name example.ru www.example.ru;
root /var/www/example.ru/public;
index index.html index.php;
access_log /var/log/nginx/example.ru.access.log;
error_log /var/log/nginx/example.ru.error.log;
location / {
try_files $uri $uri/ =404;
}
location ~ /\.(?!well-known) {
deny all;
}
}
Последний блок запрещает отдавать скрытые файлы вроде .git и .env — частая причина утечек паролей от базы. Исключение для .well-known нужно certbot. Включаем сайт и проверяем синтаксис:
sudo ln -s /etc/nginx/sites-available/example.ru /etc/nginx/sites-enabled/
sudo rm /etc/nginx/sites-enabled/default
sudo nginx -t && sudo systemctl reload nginx
Команда nginx -t обязательна перед каждым reload: с ошибкой в конфиге Nginx просто не перечитает его, и вы будете гадать, почему изменения не применились.
Если сайт на PHP: подключаем PHP-FPM
Nginx сам PHP не выполняет, он передаёт запросы процессу PHP-FPM через сокет. В Ubuntu 24.04 в репозитории PHP 8.3:
sudo apt install php8.3-fpm php8.3-mysql php8.3-xml php8.3-mbstring php8.3-curl -y
В server block добавьте обработку .php и замените try_files на вариант с фронт-контроллером, если у вас WordPress или фреймворк:
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
Ошибка 502 Bad Gateway после этого почти всегда означает, что путь к сокету не совпадает с версией PHP. Проверьте его командой ls /run/php/.
Если сайт на Node.js или Python
Приложение слушает локальный порт, например 3000, а Nginx проксирует к нему запросы снаружи. Вместо root и try_files пишем:
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;
}
Само приложение запускайте как systemd-сервис с Restart=always, а не в screen или через nohup: после перезагрузки сервера оно поднимется само. Как написать такой юнит, разобрано в статье systemd без боли.
Шаг 5. Как привязать домен к VPS?
В панели DNS создайте две записи:
| Тип | Имя | Значение | TTL |
|---|---|---|---|
| A | @ | 203.0.113.10 | 300 |
| A | www | 203.0.113.10 | 300 |
| AAAA | @ и www | IPv6 сервера, если он есть | 300 |
Короткий TTL на время переезда позволяет быстро исправить ошибку. Проверить, что запись разошлась, можно с сервера или своей машины:
dig +short example.ru
dig +short www.example.ru @1.1.1.1
Если dig возвращает старый IP, ждите: кэш у резолверов живёт столько, сколько был TTL старой записи. Не выпускайте сертификат, пока оба имени не указывают на новый сервер, — certbot упадёт на проверке.
Если AAAA-запись есть, а Nginx не слушает IPv6 (нет строки listen [::]:80), часть посетителей и сам Let's Encrypt получат ошибку подключения. Либо настраивайте IPv6 полностью, либо не создавайте AAAA.
Шаг 6. Бесплатный HTTPS через Let's Encrypt и certbot
Certbot выпускает сертификат, сам дописывает в конфиг Nginx блок listen 443 ssl и настраивает редирект с HTTP на HTTPS:
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d example.ru -d www.example.ru
Сертификат действует 90 дней. Продление делает systemd-таймер, который устанавливается вместе с пакетом. Проверьте, что он активен, и прогоните пробное продление:
systemctl list-timers | grep certbot
sudo certbot renew --dry-run
Если --dry-run проходит без ошибок, о сертификате можно забыть. Если падает — чаще всего виноват закрытый порт 80: Let's Encrypt проверяет домен именно по HTTP, поэтому порт 80 закрывать нельзя даже после перехода на HTTPS. Подробности о вариантах проверки — в документации certbot.
Выбираем главное зеркало: с www или без
Поисковики видят example.ru и www.example.ru как два разных сайта. Выберите одно имя и отправляйте второе на него 301-редиректом. Для варианта без www добавьте отдельный server block:
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name www.example.ru;
ssl_certificate /etc/letsencrypt/live/example.ru/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.ru/privkey.pem;
return 301 https://example.ru$request_uri;
}
Основной блок тогда оставьте только с server_name example.ru.
Шаг 7. Сжатие, кэш и заголовки безопасности
Сайт уже работает, но несколько строк в конфиге заметно ускорят загрузку и закроют типичные замечания сканеров безопасности. Добавьте в основной server block:
gzip on;
gzip_types text/css application/javascript application/json image/svg+xml;
location ~* \.(css|js|webp|avif|png|jpg|svg|woff2)$ {
expires 30d;
add_header Cache-Control "public";
}
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Strict-Transport-Security "max-age=31536000" always;
Есть ловушка: add_header внутри location отменяет все add_header уровнем выше. Если в блоке для статики указан свой заголовок, заголовки безопасности для картинок и CSS пропадут. Проверяйте результат командой curl -I https://example.ru/style.css. HSTS включайте, только когда HTTPS точно работает на всех поддоменах: браузер запомнит его на год.
Как обновлять сайт и не потерять данные?
Размещение — это половина работы. Вторая половина — чтобы через полгода сервер продолжал работать и был восстановимым.
- Бэкапы. Файлы сайта, базу данных (
mysqldumpилиpg_dumpпо cron) и каталог/etc/nginxкопируйте за пределы сервера. Снимок диска у провайдера — это хорошо, но он лежит у того же провайдера. Схема, которая переживает и ошибку, и потерю дата-центра, — в статье правило 3-2-1 на практике. - Обновления. Патчи безопасности ставит unattended-upgrades. Раз в месяц заходите и делайте
apt full-upgrade, а после обновления ядра — перезагрузку в удобное время. - Деплой. Выкатывайте новую версию в отдельный каталог и переключайте симлинк — тогда откат занимает одну команду. Как обновлять сервис совсем без простоя, мы разбирали в статье про zero-downtime деплой.
- Мониторинг. Минимум — внешняя проверка доступности сайта и срока действия сертификата с уведомлением в мессенджер. Если хочется видеть нагрузку и диск, начните с Prometheus и Grafana.
- Логи.
/var/log/nginx/*.error.log— первое место, куда смотреть при любой ошибке 4xx/5xx. Logrotate в Ubuntu уже настроен на ротацию логов Nginx.
Частые ошибки при размещении сайта на VPS
| Симптом | Причина | Что сделать |
|---|---|---|
| Сайт не открывается, curl на сервере работает | Порт закрыт в ufw или в файрволе провайдера | sudo ufw status, проверить сетевые правила в панели |
| Открывается приветственная страница Nginx | Не удалён default или опечатка в server_name | Удалить симлинк default, nginx -t, reload |
| 403 Forbidden | У Nginx нет прав на каталог или нет index-файла | Проверить владельца www-data и namei -l пути |
| 502 Bad Gateway | PHP-FPM или приложение не запущено, неверный сокет/порт | systemctl status php8.3-fpm, логи приложения |
| certbot: Challenge failed | DNS ещё не обновился, порт 80 закрыт или AAAA без IPv6 | dig +short, открыть 80, убрать AAAA |
| Изменения в конфиге не применились | Ошибка синтаксиса, reload не прошёл | Всегда sudo nginx -t перед reload |
Чек-лист перед тем, как отдать сайт в работу
- Вход только по ключу, root по SSH отключён, ufw включён, fail2ban работает.
nginx -tбез ошибок, сайт поhttps://отдаёт 200.- HTTP и второе зеркало (www или без) отдают 301 на основной адрес.
certbot renew --dry-runпроходит успешно.- Скрытые файлы (
/.git/,/.env) отдают 403 или 404. - Бэкап файлов и базы делается автоматически и хотя бы раз восстановлен на тестовой машине.
- Есть внешний мониторинг доступности и срока действия сертификата.
- Записано, где лежат конфиги, кто отвечает за сервер и как откатить деплой.
Частые вопросы
Сколько времени занимает размещение сайта на VPS?
Для статического сайта или WordPress — от 30 минут до вечера, если делаете это впервые. Дольше всего обычно ждут обновления DNS: от нескольких минут до нескольких часов в зависимости от TTL старой записи.
Какой дистрибутив выбрать для VPS под сайт?
Ubuntu 24.04 LTS или Debian 12/13: у них долгая поддержка, свежие пакеты Nginx и PHP в репозиториях и больше всего инструкций в сети. Подробнее о различиях — в нашем обзоре дистрибутивов.
Нужна ли панель управления вроде ISPmanager или aaPanel?
Не обязательно. Панель упрощает работу, если сайтов много и с ними работают люди без опыта в Linux, но добавляет ещё один сервис, который нужно обновлять и защищать. Для одного-двух сайтов достаточно Nginx и конфигов в Git.
Можно ли разместить несколько сайтов на одном VPS?
Да. Каждому сайту — свой каталог в /var/www, свой server block и свой сертификат. Для PHP-сайтов лучше завести отдельные пулы PHP-FPM под разными пользователями, чтобы взлом одного сайта не затронул остальные.
Нужен ли Docker, чтобы разместить сайт?
Для статического сайта или WordPress — нет, Nginx и PHP-FPM из пакетов проще. Docker полезен, когда у приложения много зависимостей или его нужно одинаково запускать на разных серверах. Начать можно с нашей статьи Docker-контейнеры простыми словами.
Что делать, если сертификат Let's Encrypt не продлился?
Запустите sudo certbot renew --dry-run и прочитайте ошибку. Обычно виноват закрытый порт 80, сменившийся IP в DNS или удалённый server block. После исправления выполните sudo certbot renew и sudo systemctl reload nginx.
Чем VPS лучше обычного хостинга для сайта?
Свободой: любые версии языков, свои демоны, cron, очереди и полный доступ к логам. Платить за это приходится ответственностью: обновления, безопасность и бэкапы теперь ваша задача. Если сайт простой и времени на сервер нет, хороший хостинг остаётся разумным выбором.