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 проти Nginx і 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.