홈/엔지니어/네트워킹 / 서버/Caddy 웹서버 — 자동 HTTPS 리버스 프록시…
네트워킹 / 서버중급linuxcaddyhttpsreverse-proxy

Caddy 웹서버 — 자동 HTTPS 리버스 프록시

Caddy의 자동 HTTPS(ACME)와 Caddyfile 문법, 리버스 프록시, 헤더·압축·로깅, API를 통한 무중단 설정까지 실무 운영 패턴을 정리합니다.

목차
  1. Caddy 란?
  2. 설치
  3. Caddyfile 기본 문법
  4. 리버스 프록시
  5. TLS 세부 옵션
  6. 전역 옵션과 로깅
  7. 설정 검증과 무중단 재로딩
  8. Admin API
  9. 트러블슈팅
  10. 정리

Caddy 란?

Caddy는 Go로 작성된 웹서버이자 리버스 프록시로, 가장 큰 특징은 자동 HTTPS입니다. 도메인을 명시하면 Caddy가 ACME 프로토콜(Let's Encrypt / ZeroSSL)로 인증서를 자동 발급·갱신하고, HTTP를 HTTPS로 리다이렉트하며, OCSP Stapling까지 알아서 처리합니다. Nginx나 Apache처럼 인증서 발급 도구(certbot)를 따로 운영하거나 cron 갱신을 신경 쓸 필요가 없습니다.

설정 파일인 Caddyfile은 선언적이고 간결합니다. 수십 줄짜리 Nginx server 블록이 Caddy에서는 몇 줄로 끝나는 경우가 많습니다. 이 가이드에서는 Caddy 2.x 기준으로 설치, Caddyfile 문법, 리버스 프록시, TLS 옵션, 그리고 무중단 재로딩을 위한 Admin API까지 다룹니다.

Caddy는 인증서 발급을 위해 도메인이 실제로 해당 서버를 가리키고(A/AAAA 레코드), 80/443 포트가 외부에서 접근 가능해야 합니다. ACME HTTP-01 챌린지는 80 포트로 들어옵니다.

설치

Bash
# Debian/Ubuntu 공식 저장소
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 version

apt로 설치하면 systemd 서비스(caddy.service)와 전용 사용자 caddy가 함께 생성되며, 설정 파일 경로는 /etc/caddy/Caddyfile입니다.

Bash
sudo systemctl enable --now caddy
systemctl status caddy

Caddyfile 기본 문법

가장 단순한 정적 사이트:

CADDY
example.com {
    root * /var/www/example.com
    file_server
    encode gzip zstd
}

이 세 줄로 example.com에 대해 인증서가 자동 발급되고, HTTP→HTTPS 리다이렉트가 걸리며, 정적 파일이 서빙됩니다. 블록 첫 줄의 사이트 주소가 곧 인증서 발급 대상입니다.

여러 도메인을 한 블록에 묶을 수 있습니다.

CADDY
example.com, www.example.com {
    redir https://example.com{uri} permanent
}

{uri}, {host}, {remote_host} 같은 플레이스홀더를 쓸 수 있습니다(템플릿 변수). 와일드카드 인증서를 쓰려면 DNS-01 챌린지가 필요하며, 이는 DNS 제공자 플러그인을 빌드해 넣어야 합니다.

리버스 프록시

Caddy의 핵심 용도 중 하나입니다. 백엔드 애플리케이션 앞단에 두면 됩니다.

CADDY
app.example.com {
    reverse_proxy localhost:3000
}

이것만으로 TLS 종료(termination) + 프록시가 완성됩니다. 업스트림이 여러 개면 자동 로드밸런싱이 적용됩니다.

CADDY
api.example.com {
    reverse_proxy {
        to localhost:8001 localhost:8002 localhost:8003
        lb_policy round_robin           # least_conn, ip_hash 등
        health_uri /healthz
        health_interval 10s
        health_timeout 3s
    }
}

경로별로 다른 백엔드로 분기하려면 matcher를 씁니다.

CADDY
example.com {
    # /api/* 는 백엔드로
    reverse_proxy /api/* localhost:8080

    # 나머지는 정적 파일
    handle {
        root * /var/www/spa
        try_files {path} /index.html      # SPA fallback
        file_server
    }
}

업스트림으로 헤더 전달

Caddy는 기본적으로 X-Forwarded-For, X-Forwarded-Proto, X-Forwarded-Host를 자동으로 붙입니다. 추가 헤더는 다음과 같이 조작합니다.

CADDY
reverse_proxy localhost:3000 {
    header_up Host {upstream_hostport}
    header_up X-Real-IP {remote_host}
    header_down -Server                  # 응답에서 Server 헤더 제거
}

TLS 세부 옵션

자동 HTTPS를 그대로 두는 것이 권장이지만, 운영 요구에 따라 조정할 수 있습니다.

CADDY
example.com {
    tls admin@example.com {              # ACME 계정 이메일
        protocols tls1.2 tls1.3
    }
    reverse_proxy localhost:3000
}

내부망/테스트에서 사설 인증서를 쓰거나 인증서 발급을 끄려면:

CADDY
# 직접 발급한 인증서 사용
tls /etc/ssl/site.crt /etc/ssl/site.key

# 내부 CA로 자체 서명 (개발용)
tls internal

발급 한도 테스트 중에는 Let's Encrypt 운영 환경 대신 스테이징을 쓰세요. 전역 옵션 블록에서 acme_ca https://acme-staging-v02.api.letsencrypt.org/directory를 지정하면 rate limit 소진을 피할 수 있습니다.

전역 옵션과 로깅

파일 맨 위 중괄호 블록이 전역 옵션입니다.

CADDY
{
    email admin@example.com
    admin localhost:2019                 # Admin API 엔드포인트
    log {
        output file /var/log/caddy/access.log
        format json
    }
}

example.com {
    reverse_proxy localhost:3000
    log {
        output file /var/log/caddy/example.access.log {
            roll_size 50mb
            roll_keep 10
        }
        format console
    }
}

JSON 로그는 jq로 바로 파싱할 수 있어 관측성 파이프라인에 유리합니다.

설정 검증과 무중단 재로딩

Bash
# 문법 검증 및 포맷팅
caddy validate --config /etc/caddy/Caddyfile
caddy fmt --overwrite /etc/caddy/Caddyfile

# 무중단 리로드 (graceful, 연결 끊김 없음)
sudo systemctl reload caddy
# 또는
caddy reload --config /etc/caddy/Caddyfile

reload는 프로세스를 죽이지 않고 새 설정을 적용하므로 진행 중인 요청이 끊기지 않습니다. 이 점이 운영 환경에서 큰 장점입니다.

Admin API

Caddy는 기본적으로 localhost:2019에서 REST API를 노출합니다. 설정을 JSON으로 조회·교체할 수 있습니다.

Bash
# 현재 동작 중인 전체 설정 확인
curl -s localhost:2019/config/ | jq .

# 업스트림 헬스 상태 확인
curl -s localhost:2019/reverse_proxy/upstreams | jq .

# 설정 일부를 동적으로 교체 (PATCH)
curl -X POST localhost:2019/load \
  -H "Content-Type: application/json" \
  -d @new-config.json

Admin API는 기본적으로 로컬에만 바인딩되지만 인증이 없습니다. 절대 외부에 노출하지 마세요. 컨테이너 환경에서 0.0.0.0에 바인딩하지 않도록 주의합니다.

트러블슈팅

증상원인 / 확인
인증서 발급 실패80 포트 방화벽 차단, A 레코드 미설정, rate limit
journalctl -u caddy 에 ACME 에러DNS 전파 미완료 — dig example.com 으로 확인
502 Bad Gateway업스트림 미기동, reverse_proxy 포트 오타
설정 반영 안 됨caddy validate 통과 후 reload 했는지 확인
TLS handshake 에러protocols 설정과 클라이언트 호환성 확인

로그는 항상 systemd 저널에서 확인합니다.

Bash
journalctl -u caddy -f --no-pager

정리

항목핵심
자동 HTTPS도메인만 명시하면 ACME로 인증서 자동 발급·갱신
Caddyfile선언적·간결, 사이트 주소가 인증서 대상
리버스 프록시reverse_proxy 한 줄, 다중 업스트림 시 LB·헬스체크
matcher경로·헤더별 분기, SPA는 try_files
무중단 리로드systemctl reload caddy / caddy reload
Admin APIlocalhost:2019, 외부 노출 금지

Caddy는 "설정이 적을수록 좋다"는 철학의 웹서버입니다. 자동 HTTPS와 간결한 프록시 문법 덕분에 소규모~중규모 서비스의 엣지 게이트웨이로 빠르게 올릴 수 있습니다.

#caddy#https#reverse-proxy#tls#web-server
편집 안내 · Editorial Note

AI 도구가 초안 작성에 사용될 수 있습니다. 명령어의 실행 여부와 검증 환경은 각 가이드에 기재된 근거를 확인하세요. 적용 전 사용 중인 버전의 공식 문서를 확인하고, 오류는 이메일로 알려주세요. 이메일로 제보

주제 관련 공식 문서(자동 연결 · 문장별 근거 아님)OpenSSL 공식 문서 ↗

질문 & 답변 (Q&A)

이 가이드에 대해 궁금한 점을 질문해보세요. 확인 후 답변드립니다.