Caddy Server: современный веб-сервер
Caddy автоматически получает и обновляет SSL-сертификаты через Let's Encrypt без какой-либо ручной настройки. Это главное преимущество перед Nginx — не нужен Certbot.
Установка на Ubuntu/Debian
apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
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 install caddyБазовая конфигурация (Caddyfile)
# /etc/caddy/Caddyfile
yourdomain.com {
root * /var/www/html
file_server
encode gzip
}
api.yourdomain.com {
reverse_proxy localhost:3000
}PHP-FPM с Caddy
yourdomain.com {
root * /var/www/html
encode gzip
php_fastcgi unix//run/php/php8.3-fpm.sock
file_server
try_files {path} {path}/ /index.php?{query}
}Заголовки безопасности
yourdomain.com {
root * /var/www/html
file_server
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains"
X-Content-Type-Options nosniff
X-Frame-Options DENY
-Server
}
}Балансировка нагрузки
yourdomain.com {
reverse_proxy {
to localhost:8001 localhost:8002 localhost:8003
lb_policy round_robin
health_uri /ping
health_interval 15s
}
}Управление сервисом
systemctl reload caddy
caddy validate --config /etc/caddy/Caddyfile
caddy fmt --overwrite /etc/caddy/CaddyfileРазбор реальных ошибок Caddy
Caddy старается быть предсказуемым, но два класса проблем встречаются регулярно: нехватка прав на привилегированные порты и сбой автоматического получения сертификата. Разберём точные сообщения из лога.
Permission denied при старте
{"level":"fatal","msg":"listen tcp :443: bind: permission denied"}Порты ниже 1024 требует прав root или capability CAP_NET_BIND_SERVICE. Официальный systemd-юнит Caddy уже включает эту capability через AmbientCapabilities, поэтому проблема обычно возникает при запуске бинарника вручную (caddy run от обычного пользователя) или в контейнере без --cap-add=NET_BIND_SERVICE. Решение: запускать через systemctl start caddy или добавить capability явно через setcap 'cap_net_bind_service=+ep' /usr/bin/caddy.
ACME challenge не проходит
{"level":"error","msg":"job failed","error":"leaf certificate creation failed for yourdomain.com: ... [400] urn:ietf:params:acme:error:connection: Fetching http://yourdomain.com/.well-known/acme-challenge/xxxx: Connection refused"}Let's Encrypt не смог достучаться до вашего сервера по порту 80 для HTTP-01 challenge. Причины почти всегда одни и те же: порт 80 закрыт в firewall, DNS-запись домена ещё не обновилась, или порт 80 занят другим процессом (проверьте ss -tlnp | grep :80). Пока challenge не проходит, Caddy отдаёт self-signed сертификат вместо настоящего.
Caddy vs Nginx vs Traefik
Все три сервера решают задачу reverse proxy, но по-разному расставляют приоритеты.
| Критерий | Caddy | Nginx | Traefik |
|---|---|---|---|
| Авто-HTTPS из коробки | Есть | Нет, нужен Certbot | Есть |
| Формат конфига | Caddyfile, декларативный | nginx.conf, императивный | YAML или Docker-лейблы |
| Родная поддержка PHP-FPM | Есть, php_fastcgi | Есть, fastcgi_pass | Нет напрямую |
| Лучше всего подходит для | Небольшие и средние проекты с авто-SSL | Высоконагруженная статика и прокси | Docker и Kubernetes окружения |
Когда Caddy не подходит
- Нужна экосистема зрелых сторонних модулей — ModSecurity, njs-скрипты, десятилетия готовых рецептов конфигурации, которые есть у Nginx.
- Команда уже держит сотни строк отлаженных nginx.conf и миграция обойдётся дороже пользы от авто-HTTPS.
- Нужен raw TCP/UDP проксинг на уровне L4 без сборки кастомного бинарника через xcaddy с плагином layer4.
- Инфраструктура целиком в Docker/Kubernetes с service discovery по лейблам — там нативнее выглядит Traefik.
- Нужна тонкая настройка кэширования на диске (proxy_cache в стиле Nginx) — в Caddy это требует стороннего модуля cache-handler.
Проверка результата
# Проверить синтаксис перед применением
caddy validate --config /etc/caddy/Caddyfile
# Убедиться, что сервис поднялся и слушает порты
systemctl status caddy
ss -tlnp | grep -E ':(80|443)'
# Проверить, что сертификат настоящий, а не self-signed
curl -vI https://yourdomain.com 2>&1 | grep -i "subject\|issuer"
# Посмотреть последние строки лога при проблеме
journalctl -u caddy -n 50 --no-pagerЕсли issuer в выводе curl показывает CN=Caddy Local Authority вместо Let's Encrypt — значит ACME challenge не прошёл и сайт работает на самоподписанном сертификате. Смотрите логи через journalctl и проверяйте DNS с портом 80 отдельно, до перезапуска Caddy. Если Caddy стоит перед несколькими бэкендами, сверьте распределение нагрузки с материалом про Docker Swarm, а мониторинг живости бэкендов удобно вынести в Uptime Kuma.