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

Caddy 웹서버 — 자동 HTTPS 리버스 프록시 완전 가이드

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

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 [email protected] {              # 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 [email protected]
    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)

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