Caddy

Caddy — сучасний веб-сервер, який автоматично налаштовує HTTPS через Let's Encrypt. В UMTC ми використовуємо Caddy як основний реверс-проксі для всіх публічних сервісів.

💡 Чому Caddy?
- **Автоматичний HTTPS** — не треба вручну налаштовувати сертифікати - **Простий синтаксис** — Caddyfile читається як документація - **Швидкий перезапуск** — zero-downtime reload - **Все з коробки** — gzip, headers, logging вже вбудовані

Архітектура

flowchart TB
    subgraph internet["Інтернет"]
        CLIENT[👤 Клієнти]
    end

    subgraph caddy["Caddy :443"]
        TLS["🔐 TLS Termination<br/>Let's Encrypt"]
        ROUTER["🔀 Routing<br/>по домену/шляху"]
    end

    subgraph backends["Бекенди"]
        WIKI["📚 Wiki<br/>:3000"]
        API["⚙️ API<br/>:8000"]
        MATRIX["💬 Matrix<br/>:8008"]
        STATIC["📁 Static<br/>/var/www"]
    end

    CLIENT -->|HTTPS| TLS
    TLS --> ROUTER
    ROUTER -->|wiki.example.com| WIKI
    ROUTER -->|wiki.example.com/api| API
    ROUTER -->|matrix.example.com| MATRIX
    ROUTER -->|static.example.com| STATIC

    style caddy fill:#22c55e,color:#000
    style backends fill:#dbeafe

Встановлення

Ubuntu/Debian

# Додаємо репозиторій
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
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 version

Docker

services:
  caddy:
    image: caddy:2-alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config
    restart: unless-stopped

volumes:
  caddy_data:
  caddy_config:

Caddyfile — конфігурація

Файл конфігурації знаходиться в /etc/caddy/Caddyfile.

Базовий reverse proxy

wiki.example.com {
    reverse_proxy localhost:3000
}

Це все! Caddy автоматично:
- Отримає сертифікат Let's Encrypt
- Налаштує редірект HTTP → HTTPS
- Почне проксювати запити на порт 3000

Кілька бекендів

flowchart LR
    REQ["wiki.example.com/api/users"]
    CADDY["Caddy"]
    API["Backend :8000"]
    FRONT["Frontend :3000"]

    REQ --> CADDY
    CADDY -->|"/api/*"| API
    CADDY -->|"/*"| FRONT

    style CADDY fill:#22c55e,color:#000
wiki.example.com {
    # API запити → backend
    handle /api/* {
        reverse_proxy localhost:8000
    }

    # Все інше → frontend
    handle {
        reverse_proxy localhost:3000
    }
}
ℹ️ Порядок важливий
Директиви `handle` обробляються в порядку від специфічних до загальних. Спочатку `/api/*`, потім все інше.

Статичні файли

static.example.com {
    root * /var/www/static
    file_server
}

Кілька доменів

wiki.example.com {
    reverse_proxy localhost:3000
}

api.example.com {
    reverse_proxy localhost:8000
}

matrix.example.com {
    reverse_proxy localhost:8008
}

Автоматичний HTTPS

Caddy автоматично отримує та оновлює сертифікати Let's Encrypt.

Вимоги

Вимога Перевірка
Домен направлений на сервер dig wiki.example.com
Порт 80 відкритий Потрібен для ACME challenge
Порт 443 відкритий Для HTTPS трафіку

Перевірка сертифікатів

# Список сертифікатів
caddy list-certs

# Примусове оновлення (якщо потрібно)
sudo systemctl reload caddy
⚠️ Staging сертифікати
Для тестування використовуйте staging сервер Let's Encrypt, щоб не вичерпати ліміти:
{
    acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
}

Корисні директиви

Security headers

(security) {
    header {
        X-Content-Type-Options nosniff
        X-Frame-Options DENY
        X-XSS-Protection "1; mode=block"
        Referrer-Policy strict-origin-when-cross-origin
        -Server
    }
}

wiki.example.com {
    import security
    reverse_proxy localhost:3000
}

Gzip стиснення

wiki.example.com {
    encode gzip zstd
    reverse_proxy localhost:3000
}

Логування

wiki.example.com {
    log {
        output file /var/log/caddy/wiki.log {
            roll_size 10mb
            roll_keep 5
        }
        format json
    }
    reverse_proxy localhost:3000
}

Basic Auth

# Генеруємо хеш пароля
caddy hash-password
# Enter password: ****
# $2a$14$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
admin.example.com {
    basicauth {
        admin $2a$14$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    }
    reverse_proxy localhost:8080
}

Заголовки для бекенду

wiki.example.com {
    reverse_proxy localhost:3000 {
        header_up X-Real-IP {remote_host}
        header_up X-Forwarded-For {remote_host}
        header_up X-Forwarded-Proto {scheme}
    }
}

Команди управління

# Статус сервісу
sudo systemctl status caddy

# Перезавантаження конфігурації (без downtime!)
sudo systemctl reload caddy

# Повний перезапуск
sudo systemctl restart caddy

# Перевірка синтаксису Caddyfile
caddy validate --config /etc/caddy/Caddyfile

# Форматування Caddyfile
caddy fmt --overwrite /etc/caddy/Caddyfile

# Логи в реальному часі
sudo journalctl -u caddy -f

Повний приклад для UMTC Wiki

# Глобальні налаштування
{
    email admin@eliah.one
}

# Загальні директиви
(common) {
    encode gzip
    header {
        X-Content-Type-Options nosniff
        X-Frame-Options DENY
        Referrer-Policy strict-origin-when-cross-origin
        -Server
    }
}

# Wiki
wiki.eliah.one {
    import common

    # API
    handle /api/* {
        reverse_proxy backend:8000 {
            header_up X-Real-IP {remote_host}
            header_up X-Forwarded-For {remote_host}
        }
    }

    # Frontend
    handle {
        reverse_proxy frontend:3000
    }

    log {
        output file /var/log/caddy/wiki.log
        format json
    }
}

# Auth (Keycloak/Authentik)
auth.eliah.one {
    import common
    reverse_proxy authentik:9000
}

# Matrix
matrix.eliah.one {
    import common
    reverse_proxy synapse:8008
}

Troubleshooting

Проблема Причина Рішення
502 Bad Gateway Бекенд не запущений docker ps, перевірте сервіс
SSL помилка Домен не направлений Перевірте DNS A-запис
Не оновлюється конфіг Синтаксична помилка caddy validate
Повільний сайт Немає стиснення Додайте encode gzip

Діагностика

# Перевірка з'єднання з бекендом
curl -v localhost:3000

# Перевірка DNS
dig wiki.example.com

# Перевірка сертифіката
openssl s_client -connect wiki.example.com:443 -servername wiki.example.com

# Перегляд логів помилок
sudo journalctl -u caddy -p err

Пов'язані теми

Шлях: services/web/caddy.md

UMTC Wiki © 2026 | Ukrainian Military Tactical Communications