Caddy — веб-сервер на Go, который получил известность благодаря автоматическому HTTPS: сертификаты Let’s Encrypt и ZeroSSL выпускаются и продлеваются без certbot, отдельных cron-задач и ручной правки конфигов. Для небольшого сайта, обратного прокси или домашней лаборатории конфигурация занимает несколько строк вместо десятков директив Nginx. Ниже — установка из официального репозитория, рабочий Caddyfile, проксирование приложения, диагностика и честный список того, где Caddy уступает.
Когда Caddy действительно удобнее
Nginx быстрее на статике и богаче модулями, но Caddy выигрывает в другом: TLS включён по умолчанию, HTTP/2 и HTTP/3 работают без настройки, а типовой сайт описывается пятью строками. Сертификат выпускается при первом запуске, продление и редирект с HTTP на HTTPS происходят сами.
- Нужен быстрый старт с валидным HTTPS без возни с ACME.
- Обратный прокси к контейнерам и внутренним сервисам.
- Несколько доменов на одной машине с минимумом конфигурации.
- Локальная разработка: встроенный CA и команда
caddy trust.
Если же требуется тонкая обработка сотен location-блоков, сложные rewrite-цепочки или модули вроде mod_security, Nginx предсказуемее. Caddy тоже расширяется, но часть возможностей ставится отдельной сборкой через xcaddy.
Установка из официального репозитория
Пакет из репозитория дистрибутива часто отстаёт, поэтому подключаем официальный репозиторий Caddy:
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf https://dl.cloudsmith.io/public/caddy/stable/gpg.key | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy
После установки проверяем версию, службу и порты. Юнит caddy.service работает от пользователя caddy, читает /etc/caddy/Caddyfile и хранит сертификаты в /var/lib/caddy/.local/share/caddy.
caddy version
systemctl status caddy --no-pager
ss -tlnp | grep -E ':80|:443'
journalctl -u caddy -n 30 --no-pager
Порты 80 и 443 должны быть свободны, иначе сертификат не выпустится: остановите и отключите Nginx или Apache до первого запуска.
Минимальный Caddyfile
Конфиг для статического сайта выглядит так. Имя домена одновременно задаёт адрес сайта и имя сертификата:
example.com {
encode zstd gzip
root * /var/www/example
file_server
}
При старте Caddy проверяет DNS: A- и AAAA-записи должны указывать на этот сервер. Затем он открывает порт 80 для ACME HTTP-01 и порт 443 для TLS-ALPN-01, выпускает сертификат и перенаправляет все HTTP-запросы на HTTPS. Отдельный блок для порта 80 писать не нужно.
Для экспериментов используйте staging-окружение Let’s Encrypt, иначе можно упереться в лимиты выпуска: добавьте в глобальный блок директиву acme_ca с адресом staging и уберите строку после проверки.
Обратный прокси к приложению
Проксирование — вторая по частоте задача. Заголовки X-Forwarded-For, X-Forwarded-Proto и Host Caddy подставляет сам, WebSocket работает без отдельных директив:
app.example.com {
encode zstd gzip
reverse_proxy 127.0.0.1:3000
}
Для нескольких экземпляров приложения достаточно перечислить адреса и выбрать политику балансировки. Проверка здоровья опрашивает указанный URI и временно исключает упавший бэкенд:
app.example.com {
reverse_proxy 127.0.0.1:3000 127.0.0.1:3001 {
lb_policy least_conn
health_uri /health
health_interval 10s
health_timeout 3s
}
}
Если приложение принимает загрузки, поднимите лимит тела через request_body с параметром max_size: по умолчанию ограничение отсутствует, но промежуточные прокси режут запрос раньше.
Заголовки, сжатие и журналы
Безопасные заголовки добавляются в том же блоке. Директиву HSTS включайте только после того, как убедились в работоспособности HTTPS: браузер запомнит требование на год вперёд.
example.com {
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains"
X-Content-Type-Options "nosniff"
Referrer-Policy "strict-origin-when-cross-origin"
-Server
}
log {
output file /var/log/caddy/example.log {
roll_size 50MiB
roll_keep 5
}
}
}
Каталог для журналов нужно создать заранее и выдать права пользователю caddy, иначе запись не откроется.
Одностраничные приложения и PHP
Для SPA с клиентским роутингом все несуществующие пути отдаются в index.html. Директива try_files проверяет файл, затем каталог, затем делает внутренний редирект:
spa.example.com {
root * /var/www/spa
try_files {path} {path}/ /index.html
file_server
}
PHP обрабатывается через php_fastcgi: директива сама настраивает корень, индексы и параметры FastCGI. Путь к сокету зависит от версии PHP.
blog.example.com {
root * /var/www/blog
php_fastcgi unix//run/php/php-fpm.sock
file_server
}
Проверьте, что пул PHP-FPM слушает именно этот сокет и у пользователя caddy есть право на чтение файлов сайта. Типичная ошибка — 502 при верно указанном сокете из-за прав на каталог.
Проверка, перезагрузка и откат
Перед применением конфиг проверяется и форматируется. caddy validate возвращает ненулевой код при синтаксической ошибке, поэтому его можно ставить в CI.
caddy validate --config /etc/caddy/Caddyfile
sudo caddy fmt --overwrite /etc/caddy/Caddyfile
systemctl reload caddy
caddy adapt --config /etc/caddy/Caddyfile --pretty
reload перечитывает конфиг через admin API на 127.0.0.1:2019 без обрыва соединений. adapt показывает, в какой JSON разворачивается Caddyfile, — так проще понять, почему правило сработало иначе. Текущую конфигурацию выводит curl -s localhost:2019/config/ | jq.
Переезд с Nginx: что учесть
client_max_body_sizeзаменяется наrequest_body, а таймауты чтения задаются вtransport http.- Регулярные выражения
locationзаменяются матчерамиpathиpath_regexp, а ветвление — блокамиhandle. - Ограничение частоты запросов и DNS-01 для wildcard-сертификатов требуют плагинов и сборки через
xcaddy. - Файлы
.htaccessне поддерживаются, а каталог сертификатов стоит включить в резервное копирование.
Диагностика типовых сбоев
Ошибка 502 означает, что Caddy не достучался до бэкенда: проверьте адрес, порт и ss -tlnp. Ошибка выпуска сертификата почти всегда связана с DNS, закрытым портом или неверным системным временем. Быстрый набор проверок:
curl -sSI https://example.com | head -5
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -dates
dig +short example.com
timedatectl
Для HTTP/3 на файрволе открывается UDP 443. Если провайдер блокирует входящие порты, автоматический HTTPS без DNS-01 не заработает — это ограничение сети, а не конфигурации.
Чек-лист перед продакшеном
- DNS указывает на сервер, TTL снижен на время переезда.
- Порты 80/tcp, 443/tcp и 443/udp открыты.
- Конфиг проверен через
caddy validate, форматирование применено. - Журналы пишутся и ротируются, диск не переполняется.
- Снята резервная копия Caddyfile и каталога сертификатов.
Итог
Caddy закрывает типовые задачи небольшого сайта и обратного прокси с минимальным конфигом: автоматический HTTPS, HTTP/3, сжатие и заголовки задаются в одном файле. Плата за простоту — меньше готовых модулей и непривычная логика матчеров. Для нового проекта или домашней лаборатории это разумный выбор, а для сложного legacy-контура с сотнями правил Nginx по-прежнему уместнее.