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 по-прежнему уместнее.

Насколько публикация полезна?

Нажмите на звезду, чтобы оценить!

Средняя оценка 0 / 5. Количество оценок: 0

Оценок пока нет. Поставьте оценку первым.

Поделиться: Telegram ВКонтакте