Как установить веб-сервер Caddy с автоматическим HTTPS
Установка Caddy на Ubuntu из официального репозитория, Caddyfile для статики и обратного прокси, автоматические сертификаты, systemd, логи и типичные ошибки при переходе с Nginx.

Caddy — веб-сервер, который по умолчанию сам получает и продлевает сертификаты TLS. Для небольшого VPS с одним-двумя доменами это убирает целый слой таймеров Certbot и фрагментов Nginx. Язык конфигурации (Caddyfile) короткий. Обратный прокси, gzip и HTTP/2 — обычные возможности, а не выходные с модулями.
Nginx по-прежнему уместен, если конфиги уже обкатаны или нужен редкий модуль. Этот гайд — про другой частый случай: HTTPS на свежем VPS Hiddence без заучивания include. Поставим Caddy из официального apt, разберём, как он говорит с Let's Encrypt, отдадим статику, проксируем локальное приложение, повесим несколько доменов, посмотрим логи и типичную драку за порт 80 с Apache или старым Nginx.
Когда Caddy хорошо заходит
Автоматический HTTPS — заголовок, а в быту выигрыш в меньшем числе движущихся частей. Caddy слушает 80 и 443, переводит HTTP на HTTPS и хранит сертификаты в своём каталоге данных. Домен на VPS всё равно нужен. ACME всё равно можно сломать (закрытый 80/tcp, чужой процесс на :80). Wildcard-сертификат требует модуля DNS-провайдера — это длиннее, чем сертификат на один хост.
- Сами HTTP→HTTPS и продление сертификата
- Читаемый Caddyfile вместо длинных блоков server
- Обратный прокси для Node, Python, PHP-FPM (с доп. настройкой) или контейнеров Docker
- HTTP/2 и нормальные настройки TLS без таблицы шифров
- Готовый unit systemd из официального пакета
Что нужно
Домен должен резолвиться на этот VPS до того, как Caddy докажет ACME HTTP-01. Пока DNS разъезжается, выпуск падает и повторяется — кажется, что «Caddy сломан», хотя виноват только DNS. Apache или Nginx остановите, если они держат 80/443.
- Ubuntu 22.04 или 24.04
- A-запись домена на IP VPS (и AAAA, если используете IPv6)
- Порты 80 и 443 свободны и разрешены в брандмауэре
- Root или sudo
Шаг 1. Установка из официального репозитория
Не берите случайно старый Caddy из universe Ubuntu, если нужны актуальные TLS и ACME. У проекта Caddy есть apt-источник. После установки появляется пользователь caddy, служба включается.
ssh root@YOUR_VPS_IP
apt update && apt -y install debian-keyring debian-archive-keyring apt-transport-https curl gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list
apt update
apt -y install caddy
caddy version
systemctl status caddy --no-pagerШаг 2. Первый Caddyfile — статический сайт
Файл по умолчанию — /etc/caddy/Caddyfile. Подставьте свой домен. Caddy попытается взять сертификат сразу после загрузки конфига, если имя не localhost. Файлы сайта положите в каталог, который может читать пользователь caddy.
mkdir -p /var/www/example
echo '<h1>It works</h1>' > /var/www/example/index.html
chown -R caddy:caddy /var/www/example
cat >/etc/caddy/Caddyfile <<'EOF'
example.com {
root * /var/www/example
file_server
encode gzip
}
EOF
caddy validate --config /etc/caddy/Caddyfile
systemctl reload caddy
# Выпуск сертификата:
journalctl -u caddy -fШаг 3. Обратный прокси к локальному приложению
Если Gunicorn, Node или Docker слушают 127.0.0.1:8000, Caddy должен быть единственным публичным процессом. Директива reverse_proxy нормально передаёт Host и X-Forwarded-* для большинства приложений. Если приложение генерирует абсолютные HTTP-ссылки, включите доверие к прокси в самом приложении (SECURE_PROXY_SSL_HEADER в Django, trust proxy в Express и т.д.).
app.example.com {
encode gzip
reverse_proxy 127.0.0.1:8000
}
# Несколько хостов в одном файле — обычное дело:
# blog.example.com {
# root * /var/www/blog
# file_server
# }Шаг 4. Брандмауэр и systemd
Разрешите 80 и 443. Служба — caddy.service; после правок Caddyfile достаточно reload, если процесс жив. Если меняете переменные для DNS-плагина, понятнее полный restart. Сертификаты по умолчанию лежат под /var/lib/caddy/.local/share/caddy/ — этот путь стоит копировать, если часто переустанавливаете систему и не хотите упираться в лимиты Let's Encrypt.
ufw allow OpenSSH
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable
systemctl enable --now caddy
systemctl reload caddy
# Каталог данных (точный путь чуть зависит от версии):
ls -la /var/lib/caddy/Шаг 5. Логи, сжатие и заголовки
Журнал доступа помогает, когда бот долбит путь или вы ловите 404. Логи можно включить на сайт. Заголовки безопасности для браузерного приложения добавляйте осмысленно: длинный HSTS браузеры запоминают.
example.com {
root * /var/www/example
file_server
encode gzip
log {
output file /var/log/caddy/example.log
}
header {
X-Content-Type-Options nosniff
Referrer-Policy no-referrer-when-downgrade
-Server
}
}
mkdir -p /var/log/caddy
chown caddy:caddy /var/log/caddy
systemctl reload caddyШаг 6. PHP и прочие дополнения
Caddy умеет php_fastcgi к PHP-FPM. Для части WordPress и Laravel этого хватает, но FPM всё равно нужно поставить и указать верный сокет. Если у вас уже идеальный PHP-конфиг Nginx, переезжать за один вечер не обязательно — Caddy сильнее на связке «статика + прокси + автоматический TLS».
example.com {
root * /var/www/example
php_fastcgi unix//run/php/php8.3-fpm.sock
file_server
}
# FPM должен быть запущен:
systemctl status php8.3-fpmПереезд с Nginx без сюрпризов
Остановите Nginx до запуска Caddy, иначе они дерутся за 80/443. За день до смены IP уменьшите TTL DNS. Проверяйте curl --resolve, чтобы бить в новый VPS до смены A-записи. Конфиги Nginx подержите в git неделю на случай отката.
- systemctl stop nginx && systemctl disable nginx
- Поставьте Caddy, проверьте Caddyfile, запустите службу
- curl -I --resolve example.com:443:NEW_IP https://example.com
- Только потом меняйте DNS, если IP новый
- Выпуск сертификата автоматический; Certbot на тот же хост параллельно не гоняйте
Если что-то не работает
Сбои ACME почти всегда DNS, брандмауэр или чужая служба на 80. В логах Caddy это tls.obtain. Сайт открывается по HTTP, но не по HTTPS — выпуск не завершился. Слишком много сертификатов — лимит Let's Encrypt; на тестах используйте staging CA, не боевой.
- validate не проходит: синтаксис Caddyfile, пропущенная скобка
- permission denied на root: chown caddy для каталога сайта
- bind: address already in use — ss -tulpn | grep -E ':80|:443'
- Таймаут сертификата: dig +short домена с VPS, ufw allow 80
- 502 у reverse_proxy: бэкенд мёртв или localhost указан не из той сети (контейнеры)
Замечания по защите
Настройки TLS у Caddy умеренные. Ваша зона — приложение и SSH. Админ-API Caddy на публичный интерфейс не выставляйте: по умолчанию он локальный, так и оставьте. Caddyfile из интернета читайте целиком: фрагмент, который проксирует /* на внутренний IP, может стать открытым прокси.
- Админ-API снаружи не открывать
- Пакет обновлять
- HSTS — только когда HTTPS точно работает на всех именах
- Тестовые и боевые имена разведите, чтобы не сжечь лимит ACME
Советы
- caddy fmt --overwrite /etc/caddy/Caddyfile делает файл читаемым
- import-фрагменты, когда сайтов много и они похожи
- В Docker часто образ caddy:alpine и примонтированный Caddyfile
- Wildcard нужен DNS-модуль и токен API — токен берегите
- Сначала journalctl -u caddy, потом переписывание всего файла
Caddy на VPS — официальный пакет, короткий Caddyfile, открытые 80/443, DNS уже смотрит на сервер, reload systemd после правок. Статика или обратный прокси перед Gunicorn, Node или Docker. Автоматический HTTPS работает, когда ACME отвечает на порту 80; если выпуск падает, чините DNS и конфликт портов, а не «сам Caddy». Каталог /var/lib/caddy копируйте, если часто переустанавливаете систему.