Простой переезд: Как развернуть Caddy на Debian 13 и подружить его с WordPress и Nextcloud

Привет, друзья! Сегодня у нас будет большой практический разбор, посвященный обновлению серверного веб-стека. Из года в год, настраивая новые серверы, многие системные администраторы по привычке ставят связку из Apache и утилиты Certbot для управления SSL-сертификатами. Мы копируем старые проверенные конфигурационные файлы, настраиваем cron для автоматического восстановления ключей Let’s Encrypt и тратим время на рутинное обслуживание системы.

Но зачем плодить лишний оверхед и усложнять архитектуру, если всю эту работу можно доверить автоматике? Сегодня мы поговорим про Caddy — современный, быстрый и стабильный веб-сервер, написанный на Go. Его главная фишка — ультимативная простота и полностью автоматическое управление безопасностью.

В этой статье мы пошагово разберем, как развернуть Caddy на актуальном Debian 13 (Trixie), как настроить реверс-прокси для внутренних приложений, как полностью заменить им старый Apache для обычных сайтов на WordPress с PHP 8.4 и как без боли перевести на этот стек облачное хранилище Nextcloud. Поехали разбираться!

Часть 1. Почему Caddy — это удобно

Традиционные веб-серверы проектировались в эпоху, когда шифрование трафика было редким исключением, а настройки серверов производились один раз и вручную. В современных реалиях HTTPS стал обязательным стандартом.

Caddy полностью меняет подход к администрированию инфраструктуры:

  • Автоматический HTTPS по умолчанию. Больше не нужно ставить Certbot, настраивать cron и следить за обновлением ключей. Caddy сам определяет доменное имя из конфигурационного файла, отправляет запросы в Let’s Encrypt или ZeroSSL, проходит проверку, выпускает сертификаты и сам обновляет их в фоновом режиме.
  • Лаконичный конфигурационный файл (Caddyfile). Вместо сотен строк XML-подобного кода Apache, конфигурация Caddy занимает всего несколько строк. Ее легко читать, легко изменять, а шанс совершить случайную ошибку сведен к минимуму.
  • HTTP/3 из коробки. Современный быстрый протокол работает без необходимости собирать пакеты из исходников — сервер сам поднимает нужные UDP-сокеты.

Часть 2. Развертывание Caddy штатными средствами Debian 13

Начиная со стабильных версий Debian, веб-сервер Caddy официально включен в базовые репозитории дистрибутива. Вам не нужно скачивать сторонние GPG-ключи или прописывать внешние URL-адреса, рискуя нарушить чистоту системы при глобальном обновлении.

Установка на Debian 13 выполняется стандартным пакетным менеджером apt в одну команду:

sudo apt update && sudo apt install -y caddy

Пакетный менеджер сам создаст системного пользователя caddy, выделит необходимые директории и зарегистрирует службу в systemd. Проверим статус запущенного демона, чтобы убедиться, что система выделила ресурсы под процесс:

systemctl status caddy

Если в терминале горит зеленая строка active (running), значит, базовая часть готова и можно переходить к настройке маршрутов.

Часть 3. Сценарий 1: Настройка реверс-прокси для внутренних приложений

Довольно часто в практике администрирования возникает ситуация, когда на сервере работают изолированные сервисы или панели управления, которые слушают только внутреннюю петлю (localhost) на нестандартных портах — например, :3000 или :5000. Наша задача — безопасно вывести их во внешний мир, повесить на красивый поддомен и защитить SSL-шифрованием.

Открываем главный конфигурационный файл Caddy:

sudo nano /etc/caddy/Caddyfile

Очищаем его содержимое от дефолтных приветственных блоков и пишем простую логику для поддомена:

panel.phoenix901.ru {
    reverse_proxy 127.0.0.1:3000 {
        header_up Host {host}
        header_up X-Real-IP {remote_host}
        header_up X-Forwarded-For {remote_host}
        header_up X-Forwarded-Proto {scheme}
    }
}

В Apache для этого пришлось бы подключать модули проксирования, городить блоки виртуальных хостов по 80 и 443 портам отдельно и вручную прописывать пути к SSL-ключам. В Caddy достаточно указать домен и адрес внутреннего порта. Блок header_up необходим для того, чтобы внутреннее приложение правильно видело реальный IP-адрес внешнего клиента, а не считало, что все запросы идут локально от самого сервера.

Обновим конфигурацию сервера без разрыва текущих сетевых соединений пользователей:

sudo systemctl reload caddy

Часть 4. Сценарий 2: Полная замена Apache для сайтов на WordPress (PHP 8.4)

Если вы вдохновились простотой конфигурации реверс-прокси, то логичный следующий шаг — полностью отказаться от громоздкого Apache, переведя все свои сайты на Caddy. Он отлично справляется как с отдачей статических файлов (HTML, картинок, стилей), так и с обработкой динамических PHP-скриптов.

Допустим, на сервере крутится простой статический сайт и полноценный blog на WordPress с использованием актуальной версии PHP 8.4.

Важный нюанс: файлы конфигурации Apache .htaccess новым сервером игнорируются физически. Caddy не умеет их читать, поэтому всю логику защиты и ЧПУ мы переносим прямо в Caddyfile.

1. Первым делом полностью останавливаем старый Apache и убираем его из автозагрузки, чтобы освободить сетевые порты 80 и 443:

sudo systemctl stop apache2
sudo systemctl disable apache2

2. Открываем наш /etc/caddy/Caddyfile and добавляем конфигурацию для наших сайтов:

# Простой статический сайт
static.phoenix901.ru {
    root * /var/www/static_site
    file_server
    encode gzip
}

# Блог на WordPress с PHP 8.4
phoenix901.ru {
    root * /var/www/html/phoenix_blog
    file_server
    encode gzip
    
    # Безопасность: блокируем доступ к служебным файлам WordPress
    @wp-internal {
        path /wp-config.php /xmlrpc.php /wp-content/uploads/*.php
        path */.*
    }
    respond @wp-internal "Access Denied" 403

    # Перенаправляем обработку PHP на системный сокет PHP 8.4-FPM
    php_fastcgi unix//run/php/php8.4-fpm.sock
}

Директива file_server указывает Caddy самостоятельно отдавать файлы из указанного корня root. Конструкция encode gzip сжимает данные на лету, ускоряя загрузку страниц.

Директива php_fastcgi автоматически берет на себя все правила перезаписи URL (Rewrite Rules), которые раньше жили в .htaccess. Она сама перенаправляет запросы к несуществующим адресам на index.php, обеспечивая корректную работу постоянных ссылок (permalink) в WordPress.

Для защиты системы мы добавили блок @wp-internal. Он намертво закрывает внешку от чтения критически важного файла конфигурации базы данных wp-config.php, отключает уязвимый протокол xmlrpc.php (который часто брутфорсят тролли) и запрещает выполнение PHP-скриптов, если их попытаются залить в папку медиафайлов uploads. Точки со звездочкой (*/.*) закрывают от сканирования любые скрытые файлы вроде системных логов или старых бэкапов.

Часть 5. Сценарий 3: Перенос Nextcloud под управление Caddy

Если помимо обычных сайтов у вас на сервере развернуто собственное облачное хранилище Nextcloud, его тоже можно легко избавить от опеки Apache. Nextcloud очень чувствителен к настройкам веб-сервера, заголовкам безопасности и путям веб-окружения (WebDAV).

В архитектуре Caddyfile критически важно соблюдать **строгую иерархию директив**: служебные редиректы .well-known должны быть объявлены в самом верху блока конфигурации. Если написать их ниже вызова php_fastcgi, то FastCGI-тракт перехватит трафик раньше, и правила перенаправления просто проигнорируются.

Добавим блок конфигурации для Nextcloud в наш единый Caddyfile:

cloud.phoenix901.ru {
    root * /var/www/nextcloud
    file_server
    encode gzip

    # 1. Срочные редиректы .well-known — выполняются строго до FastCGI
    redir /.well-known/carddav /remote.php/dav/ 301
    redir /.well-known/caldav /remote.php/dav/ 301
    redir /.well-known/webfinger /index.php/.well-known/webfinger 307
    redir /.well-known/nodeinfo /index.php/.well-known/nodeinfo 307

    # Перенаправление Client Push на выделенный порт
    reverse_proxy /push/* 127.0.0.1:7867

    # 2. Защита ядра Nextcloud (Исключаем .well-known из бана скрытых файлов)
    @nc-blocked {
        path /data/* /config/* /db_structure /README /3rdparty/* /lib/* /templates/* /occ /issue*
        path /.ncdata
        path */.*
        not path /.well-known/*
    }
    respond @nc-blocked "Access Denied" 403

    # 3. Обработка PHP-скриптов через сокет PHP 8.4 с флагом фронт-контроллера
    php_fastcgi unix//run/php/php8.4-fpm.sock {
        env front_controller_active true
    }

    # Настройки заголовков безопасности, которые требует Nextcloud
    header {
        Strict-Transport-Security "max-age=15552000; includeSubDomains"
        X-Content-Type-Options "nosniff"
        X-Frame-Options "SAMEORIGIN"
        X-Permitted-Cross-Domain-Policies "none"
        X-Robots-Tag "none"
        X-XSS-Protection "1; mode=block"
    }
}

В этом блоке мы учли все ключевые требования Nextcloud:

  • Прописали необходимые HTTP-заголовки безопасности в секции header, чтобы система не ругалась в административной панели.
  • Настроили редиректы redir для путей .well-known. Обратите внимание: для webfinger и nodeinfo используется код ответа 307 (Temporary Redirect). Если оставить там дефолтный 301, внутренний curl-тестер Nextcloud выдаст ошибку конфигурации.
  • С помощью комбинированного правила @nc-blocked закрыли прямой доступ из веба к служебным каталогам (папкам /data/ и /config/). При этом конструкция not path /.well-known/* делает нативное исключение для системной папки, позволяя внешним клиентам синхронизировать контакты и календари.

Часть 6. Права доступа и запуск системы

При тотальном переезде с Apache на Caddy часто возникает классический конфликт прав доступа. Исторически в Debian обработчик PHP-FPM работает от имени пользователя www-data. Caddy же запускается под собственным пользователем caddy.

Лучшая административная практика при полном демонтаже старого веб-сервера — **полностью перевести весь веб-стек на изолированного системного юзера caddy:caddy**. Это исключит путаницу в правах и закроет лишние уязвимости.

1. Передаем права на все директории сайтов и облака пользователю и группе caddy:

sudo chown -R caddy:caddy /var/www/static_site
sudo chown -R caddy:caddy /var/www/html/phoenix_blog
sudo chown -R caddy:caddy /var/www/nextcloud

2. Перенастраиваем сам интерпретатор PHP-FPM, чтобы его рабочие воркеры стартовали от имени нужного нам пользователя. Открываем конфигурационный файл пула (например, /etc/php/8.4/fpm/pool.d/www.conf):

sudo nano /etc/php/8.4/fpm/pool.d/www.conf

Находим параметры процесса и unix-сокета, приводя их к единому стеку caddy:

user = caddy
group = caddy

listen.owner = caddy
listen.group = caddy
listen.mode = 0660

Сохраняем изменения и перезапускаем службу PHP-FPM, чтобы пересоздать файл сокета с новыми правами доступа:

sudo systemctl restart php8.4-fpm

3. Перед окончательным запуском обязательно проверяем синтаксис нашего единого конфигурационного файла на наличие опечаток или пропущенных скобок:

caddy validate --config /etc/caddy/Caddyfile

Если утилита сообщает Valid configuration, автоматически выравниваем отступы форматировщиком и мягко перезапускаем службу Caddy. В этот момент он подхватит все прописанные конфигурации, мгновенно сгенерирует под них SSL-сертификаты и выведет проекты в сеть:

caddy fmt --overwrite /etc/caddy/Caddyfile
sudo systemctl restart caddy

Часть 7. Грабли, на которые я наступил (Реальный боевой опыт)

Теория в мануалах всегда выглядит красиво, но когда переносишь реальную живую инфраструктуру с кучей кастомных скриптов, ядро новой системы обязательно проверит тебя на прочность. Вот три реальные проблемы, с которыми я столкнулся при миграции, и их физическое решение.

1. Капкан приоритетов и падение ЧПУ в Nextcloud

В конфигурации Caddyfile директива php_fastcgi имеет наивысший внутренний приоритет обработки. Если вы просто пропишете редиректы служебных путей .well-known ниже по тексту, Caddy перехватит входящие запросы раньше и сразу швырнет их в сокет FastCGI. В итоге мобильные приложения синхронизации отвалятся, а Nextcloud начнет сыпать ошибками.

Решение: Правила redir для WebDAV обязаны стоять на самом верху блока конфигурации хоста, до вызова PHP-обработчика. Более того, коды редиректов для путей webfinger и nodeinfo должны отдавать статус 307 (Temporary Redirect) вместо дефолтного 301, иначе внутренний curl-тестер облака не сможет верифицировать контур.

2. Синтаксис регулярных выражений Go и скрытые файлы

Пытаясь защитить сервер, логично прописать глобальный запрет на чтение скрытых папок через матчер путей path */.* (чтобы никто не вытащил логи, файлы .env или репозитории .git). Но в этот момент Nextcloud полностью деградирует.

Почему? Потому что путь /.well-known/ физически начинается с точки. Универсальный матчер посчитает его запрещенным скрытым файлом и выдаст жесткий 403 Access Denied. Пытаться решить это через CEL-выражения и конструкции Lookahead/Lookbehind (типа (?!well-known)) бесполезно — Caddy написано на Go, а его встроенный движок регулярных выражений RE2 принципиально не поддерживает Perl-совместимый синтаксис PCRE.

Решение: Использовать простую и быструю нативную комбинацию матчеров Caddy в блоке блокировки, которая работает как логическое И (AND) на уровне ядра веб-сервера:

path */.*
not path /.well-known/*

3. Отвал объектного кэша Redis при сносе Apache

Самый неожиданный подвох ждал со стороны кэширования. Если ваш WordPress или Nextcloud общается с локальной базой Redis через Unix-сокет (например, /var/run/redis/redis-server.sock с правами 770), то после удаления пакетов Apache вся связка упадет с ошибкой Error establishing a Redis connection.

Физика процесса проста: когда интерпретатор PHP-FPM переводится на изолированного пользователя, воркеры PHP теряют права на чтение «трубы» сокета Redis, которая исторически принадлежала группе старого веб-сервера. Чтобы вернуть объектный кэш в строй и сохранить минимальную задержку (латентность) дисковых операций, необходимо принудительно добавить пользователя нового веб-сервера в системную группу Redis:

sudo usermod -aG redis caddy
sudo systemctl restart redis-server php8.4-fpm

Заключение

Как видите, перейти на Caddy и полностью убрать Apache из системы можно без боли и долгих мучений с конфигурациями. Мы объединили управление обычными сайтами, реверс-прокси и облачным хранилищем Nextcloud в одном лаконичном файле, избавились от Certbot и полностью автоматизировали работу с HTTPS. Пробуйте, настраивайте и делайте свои серверы проще и надежнее. Стабильного аптайма вашим проектам! Не прощаемся!

👁️ 10

Простой переезд: Как развернуть Caddy на Debian 13 и подружить его с WordPress и Nextcloud: 2 комментария

  1. Если будете повторять этот переезд, обязательно учитывайте следующие две вещи, иначе словите скрытые аномалии и отвал сервисов.
    Во-первых, вырезайте к чертям on_demand из дефолтного блока-заглушки для левых доменов и IP. Если оставить там автоматический выпуск сертификатов по запросу, вы откроете ящик Пандоры. Буквально через пару часов после запуска сервер попал под массированный скан подсетей (Dictionary Attack / Host Header Injection). Боты начали слать пачки запросов с левыми заголовками SNI (всякие мусорные домены, краулеры и левые прокси). Caddy честно пытался под каждый этот левый запрос выписать валидный SSL, забивая процессор генерацией ключей и моментально словив от Let’s Encrypt бан по лимитам (HTTP 429 RateLimited — too many new registrations from this IP). Физика лечения проста: левые запросы по HTTP швыряем на основу, а по HTTPS сервер должен просто жестко сбрасывать соединение на этапе TLS Handshake (Drop TCP-сессии) без всякой TLS-самодеятельности. В дефолтном блоке оставляем только :80 { redir https://phoenix901.ru{uri} permanent }, и боты моментально идут нахер.
    Во-вторых, когда мы перевели весь веб-стек и воркеры PHP-FPM на изолированного пользователя caddy:caddy, в воздухе повис встроенный планировщик WordPress. Если у вас настроен автопостинг в Telegram (например, через плагин wp-telegram), после сноса Apache он омертвеет. Встроенный механизм WP-Cron дергается виртуально, когда на сайт заходят люди, но на чистом Caddy с его агрессивным кэшированием внутренние триггеры просто перестают срабатывать, и очередь постов замораживается. Плюс при удалении Apache утилитой apt purge старый системный юзер www-data полностью депривилегируется, блокируя старые хвосты. Решение чисто админское — затыкаем встроенный крон в wp-config.php строкой define(‘DISABLE_WP_CRON’, true); и переводим его на системный демон Debian в sudo crontab -u caddy -e строкой * * * * * /usr/bin/php8.4 -f /var/www/html/phoenix_blog/wp-cron.php >/dev/null 2>&1. После этого асинхронный контур закрывается окончательно: Caddy рубит ботов в зародыше, а системный крон стабильно проталкивает посты в Telegram каждую минуту.





  2. Эксперимент свернут. Из-за параноидальной внутренней логики плагина защиты AIOS (All-In-One Security), который при смене веб-сервера намертво заблокировал отправку комментариев (ошибка 403), контур Caddy пришлось полностью демонтировать.
    Чтобы не вырезать боевые плагины брандмауэра и не тратить время на пересчет их баз данных под новый сетевой контекст, я оперативно вернул проверенную временем связку Apache2 (mpm_event) + PHP-FPM.
    Продакшен переведен в исходное состояние, права на файлы и сокет Redis возвращены пользователю www-data. Настройки .htaccess снова в строю, формы работают штатно. Стабильность и аптайм — в приоритете.





Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

двенадцать + восемь =

root@phoenix901:~# connect
[×]

Получай дайджест раз в неделю.
Без спама.