9 августа 2026 caddyhttps

Caddy: HTTPS для домашних проектов без боли

Браузер обращается к Caddy по HTTPS, Caddy проксирует запросы в приложение на порту 3000
Браузер обращается к Caddy по HTTPS, Caddy проксирует запросы в приложение на порту 3000

У меня на сервере живёт несколько маленьких веб-приложений: что-то на Node.js, что-то статикой. Раньше каждое из них означало отдельный блок в nginx, ручной выпуск сертификата и напоминание в календаре «продлить через три месяца». С Caddy всё это свелось к трём строчкам конфигурации.

Минимальный Caddyfile

home.example.org {
    encode gzip
    reverse_proxy 127.0.0.1:3000
}

Это всё. Caddy сам получит сертификат Let's Encrypt, включит HTTP/2 и HTTP/3, будет перенаправлять HTTP на HTTPS и продлевать сертификат задолго до истечения. Приложение при этом слушает только 127.0.0.1, снаружи его не видно.

Если порт 443 уже занят

Так бывает, когда на сервере уже есть другой сервис на 443. Caddy можно повесить на другой порт и отключить проверку TLS-ALPN (ей нужен именно 443):

{
    https_port 9443
}

home.example.org:9443 {
    tls {
        issuer acme {
            disable_tlsalpn_challenge
        }
    }
    reverse_proxy 127.0.0.1:3000
}

Сертификат тогда подтверждается через порт 80 (HTTP-01), так что он должен быть открыт снаружи.

Caddy в Docker

Удобнее всего держать Caddy в том же docker-compose.yml, что и приложение, с network_mode: host — тогда не нужно пробрасывать порты, а reverse_proxy 127.0.0.1:3000 работает как на обычном сервере. Главное — вынести каталог /data в volume, иначе при каждом пересоздании контейнера сертификаты будут выпускаться заново и можно упереться в лимиты Let's Encrypt.

Один нюанс: если на порту 80 сидит Caddy, другие утилиты, которые хотят подтверждать сертификаты через тот же порт (например, acme.sh в режиме standalone), перестанут работать. Выхода два: подтверждать сертификаты через DNS (DNS-01) или отдавать файлы проверки через тот же веб-сервер (webroot).