Caddy 상세 설정 가이드: Caddyfile부터 SSL·리버스 프록시·Docker·로드밸런싱까지

1장 Caddy 설정은 Caddyfile에서 시작한다#

Caddy를 운영할 때 가장 일반적으로 사용하는 설정 파일이 Caddyfile입니다.

Caddyfile의 기본 구조는 매우 단순합니다.

도메인 {
    설정
}

예를 들어 example.com으로 들어오는 요청을 내부 8080번 애플리케이션으로 전달하려면 다음과 같이 작성할 수 있습니다.

example.com {
    reverse_proxy localhost:8080
}

Caddyfile에 정상적인 도메인이 지정되면 Caddy의 Automatic HTTPS가 활성화되어 HTTPS와 TLS 인증서 관리를 자동화할 수 있습니다. 공식 문서에서도 도메인을 사이트 주소로 지정하는 것만으로 자동 HTTPS를 사용할 수 있다고 설명합니다.


2장 가장 기본적인 Caddy 구조#

실제 서버 구조를 먼저 보면 이해하기 쉽습니다.

flowchart LR
    U[사용자]
    C[Caddy<br/>80 · 443]
    A[Web Application<br/>8080]

    U -->|HTTPS| C
    C -->|HTTP| A

외부 사용자는:

https://example.com

으로 접속합니다.

하지만 실제 애플리케이션은 내부에서:

localhost:8080

으로 동작할 수 있습니다.

Caddy가 중간에서 두 시스템을 연결합니다.


3장 가장 기본적인 Reverse Proxy 설정#

다음 설정 하나만으로 기본적인 Reverse Proxy를 구성할 수 있습니다.

example.com {
    reverse_proxy 127.0.0.1:8080
}

구조는 다음과 같습니다.

sequenceDiagram
    participant U as 사용자
    participant C as Caddy
    participant A as 애플리케이션

    U->>C: https://example.com
    C->>A: http://127.0.0.1:8080
    A-->>C: 응답
    C-->>U: HTTPS 응답

Caddy의 reverse_proxy는 단순 프록시뿐 아니라 로드밸런싱, 헬스체크, request manipulation 등도 지원합니다.


4장 Caddy의 자동 HTTPS 설정#

다음처럼 도메인을 지정하면 별도의 ssl_certificate 설정을 작성할 필요가 없습니다.

example.com {
    reverse_proxy localhost:8080
}

일반적인 공개 서버라면:

  • DNS가 서버를 가리키고
  • 80번 포트가 접근 가능하고
  • 443번 포트가 접근 가능하면

Caddy가 인증서 발급과 HTTPS 설정을 자동화할 수 있습니다.

구조적으로는 다음과 같습니다.

flowchart LR
    DNS[DNS<br/>example.com]
    C[Caddy]
    CA[인증기관]
    APP[Application]

    DNS --> C
    C <--> CA
    C --> APP

5장 HTTP를 HTTPS로 자동 전환한다#

Caddy의 Automatic HTTPS에는 HTTP 요청을 HTTPS로 리다이렉트하는 기능도 포함됩니다.

따라서 사용자가:

http://example.com

으로 접속하더라도 일반적인 구성에서는:

https://example.com

으로 전환됩니다.

flowchart LR
    A[HTTP :80]
    C[Caddy]
    B[HTTPS :443]

    A --> C
    C -->|Redirect| B

6장 여러 도메인을 하나의 Caddy에서 운영하기#

Caddy의 가장 실용적인 사용 방법 중 하나입니다.

예를 들어 서버 한 대에서 다음 서비스를 운영한다고 하겠습니다.

thinkx.example.com
admin.example.com
api.example.com
monitor.example.com

Caddyfile은 다음처럼 구성할 수 있습니다.

thinkx.example.com {
    reverse_proxy localhost:8080
}

admin.example.com {
    reverse_proxy localhost:9000
}

api.example.com {
    reverse_proxy localhost:3000
}

monitor.example.com {
    reverse_proxy localhost:3001
}

전체 구조는 다음과 같습니다.

flowchart TB
    I[Internet]
    C[Caddy]

    T[ThinkX<br/>8080]
    A[Admin<br/>9000]
    API[API<br/>3000]
    M[Monitoring<br/>3001]

    I --> C

    C -->|thinkx.example.com| T
    C -->|admin.example.com| A
    C -->|api.example.com| API
    C -->|monitor.example.com| M

하나의 서버에서 여러 서비스를 운영할 때 매우 깔끔한 구조입니다.


7장 Docker에서는 localhost 대신 컨테이너 이름을 사용한다#

Caddy와 애플리케이션이 동일한 Docker Network에 있다면 컨테이너 이름을 사용할 수 있습니다.

예를 들어:

thinkx.example.com {
    reverse_proxy trilium:8080
}

처럼 작성할 수 있습니다.

Docker 구조는 다음과 같습니다.

flowchart TB
    U[Internet]

    subgraph Docker Host
        C[Caddy]

        subgraph app_network
            T[trilium:8080]
            API[api:3000]
            G[grafana:3000]
        end

        C --> T
        C --> API
        C --> G
    end

    U --> C

이 구조에서는 내부 애플리케이션 포트를 외부에 직접 노출할 필요가 줄어듭니다.


8장 Docker Compose와 함께 사용할 때의 기본 구조#

예를 들어 다음과 같은 구성을 생각할 수 있습니다.

services:

  caddy:
    image: caddy:latest
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - ./caddy_data:/data
      - ./caddy_config:/config

  app:
    image: my-app

Caddyfile은 다음과 같이 구성할 수 있습니다.

example.com {
    reverse_proxy app:8080
}

중요한 점은 Caddy와 app이 같은 Docker Network에서 서로 통신할 수 있어야 한다는 것입니다.


9장 Caddy의 인증서 데이터는 반드시 보존해야 한다#

Docker에서 Caddy를 운영한다면 /data 볼륨은 매우 중요합니다.

예:

volumes:
  - ./caddy_data:/data

TLS 인증서와 Caddy가 관리하는 데이터가 이 영역에 저장될 수 있기 때문입니다.

컨테이너를 삭제할 때마다 인증서 데이터까지 사라지는 구조는 피하는 것이 좋습니다.


10장 URL 경로에 따라 다른 서비스로 보내기#

도메인을 여러 개 만들지 않고 하나의 도메인을 사용할 수도 있습니다.

예:

example.com
example.com/api/
example.com/admin/

이를 서비스별로 분리할 수 있습니다.

flowchart LR
    U[example.com]
    C{Caddy Router}

    W[Web]
    API[API]
    ADM[Admin]

    U --> C

    C -->|/| W
    C -->|/api/*| API
    C -->|/admin/*| ADM

11장 handle을 이용한 경로 분기#

Caddy의 handle은 서로 배타적인 처리 그룹을 만들 때 유용합니다.

공식 문서에서도 /foo/* 요청은 정적 파일로 처리하고 나머지는 reverse proxy로 처리하는 예를 제공합니다.

예:

example.com {

    handle /api/* {
        reverse_proxy localhost:3000
    }

    handle /admin/* {
        reverse_proxy localhost:9000
    }

    handle {
        reverse_proxy localhost:8080
    }
}

마지막 handle은 fallback 역할을 합니다.


12장 handle_path는 경로 Prefix를 제거한다#

handle_path는 handle과 비슷하지만 매칭된 prefix를 제거합니다.

예:

example.com {

    handle_path /api/* {
        reverse_proxy localhost:3000
    }
}

사용자가:

/api/users

를 요청하면 backend에는:

/users

가 전달됩니다.

구조적으로:

flowchart LR
    U["/api/users"]
    C[Caddy<br/>handle_path]
    A["Backend<br/>/users"]

    U --> C --> A

입니다.


13장 handle과 handle_path 차이#

handle#

경로를 그대로 유지합니다.

/api/users
→
/api/users

handle_path#

앞의 경로를 제거합니다.

/api/users
→
/users

따라서 backend 애플리케이션의 URL 구조에 따라 선택하면 됩니다.


14장 rewrite를 이용해 내부 URL을 바꿀 수 있다#

Caddy는 rewrite를 사용해 요청 URI를 내부적으로 변경할 수 있습니다.

예를 들어:

example.com {
    rewrite /index.html
    file_server
}

또는 API 앞에 내부 prefix를 추가할 수도 있습니다.

api.example.com {
    rewrite /api{uri}
    reverse_proxy localhost:8080
}

15장 Redirect와 Rewrite는 다르다#

둘은 비슷해 보이지만 매우 다릅니다.

Redirect#

사용자 브라우저의 주소가 바뀝니다.

/old
↓
/new

Rewrite#

서버 내부에서만 경로를 바꿉니다.

사용자
/article

Caddy 내부
/index.php?id=article

따라서 검색엔진 주소 이전에는 Redirect, 내부 애플리케이션 처리에는 Rewrite가 적합합니다.


16장 정적 웹사이트 서비스#

Caddy는 자체적으로 정적 파일도 제공할 수 있습니다.

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

공식 문서에서도 file_server를 일반적으로 root와 함께 사용하는 방식을 설명합니다.

구조는 다음과 같습니다.

flowchart LR
    U[사용자]
    C[Caddy]
    F["/var/www/html"]

    U --> C --> F

17장 정적 사이트와 API를 동시에 운영하기#

예:

example.com {

    handle_path /api/* {
        reverse_proxy api:3000
    }

    handle {
        root * /srv/site
        file_server
    }
}

구조는 다음과 같습니다.

flowchart LR
    U[사용자]
    C{Caddy}

    API[API Server]
    STATIC[Static Site]

    U --> C
    C -->|/api/*| API
    C -->|나머지| STATIC

18장 SPA React·Vue 사이트 설정#

React나 Vue 같은 SPA에서는 존재하지 않는 URL을 index.html로 보내야 하는 경우가 많습니다.

개념적으로:

example.com {
    root * /srv/app

    try_files {path} /index.html

    file_server
}

사용자가:

/profile

로 직접 접속해도 실제 파일이 없다면:

/index.html

을 제공하는 구조입니다.


19장 응답 압축 설정#

Caddy의 encode directive를 사용하면 응답 압축을 활성화할 수 있습니다.

공식 문서에 따르면 기본적으로 Zstandard와 Gzip을 지원합니다.

간단한 설정은 다음과 같습니다.

example.com {

    encode zstd gzip

    reverse_proxy localhost:8080
}

또는 단순히:

encode

만 사용할 수도 있습니다.


20장 압축 흐름#

sequenceDiagram
    participant U as Browser
    participant C as Caddy
    participant A as Application

    U->>C: Accept-Encoding: gzip, zstd
    C->>A: Request
    A-->>C: Response
    C-->>U: 압축된 Response

HTML·CSS·JavaScript·JSON 같은 텍스트 기반 콘텐츠에서는 전송량을 줄이는 데 도움이 됩니다.


21장 보안 헤더 추가하기#

Caddy에서는 header directive를 이용해 HTTP response header를 추가할 수 있습니다.

예를 들어:

example.com {

    header {
        X-Content-Type-Options nosniff
        X-Frame-Options SAMEORIGIN
        Referrer-Policy strict-origin-when-cross-origin
    }

    reverse_proxy localhost:8080
}

보안 헤더는 애플리케이션 성격에 맞게 조정해야 합니다.

무조건 많은 보안 헤더를 넣는 것이 좋은 것은 아닙니다.


22장 서버 정보를 최소화하는 관점#

웹서버 환경에서는 불필요한 정보를 외부에 노출하지 않는 것이 좋습니다.

특히:

  • 내부 IP
  • backend 포트
  • 관리자 주소
  • 인증 정보

등이 응답이나 로그를 통해 노출되지 않도록 확인해야 합니다.

Caddy를 외부 게이트웨이로 두면 내부 서비스의 실제 주소를 감출 수 있는 장점도 있습니다.


23장 Basic Auth로 관리자 페이지 보호하기#

Caddy는 basic_auth를 기본 지원합니다.

예:

admin.example.com {

    basic_auth {
        admin HASHED_PASSWORD
    }

    reverse_proxy localhost:9000
}

중요한 점은 평문 비밀번호를 Caddyfile에 넣으면 안 된다는 것입니다.

공식 문서에서도 hashed password를 사용해야 한다고 설명합니다.


24장 비밀번호 Hash 생성#

Caddy CLI를 이용할 수 있습니다.

caddy hash-password

생성된 Hash를 Caddyfile에 넣습니다.

basic_auth {
    admin $2a$...
}

Basic Auth는 반드시 HTTPS와 함께 사용하는 것이 좋습니다.


25장 접근 로그 활성화#

HTTP 요청 로그는 다음처럼 활성화할 수 있습니다.

example.com {

    log

    reverse_proxy localhost:8080
}

Caddy의 log directive는 HTTP Access Log를 활성화합니다. Runtime log와 HTTP Access Log는 서로 다른 개념입니다.


26장 파일로 Access Log 저장하기#

예:

example.com {

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

    reverse_proxy localhost:8080
}

운영 환경에서는 다음 정보 분석에 도움이 됩니다.

  • 요청 URL
  • HTTP 상태 코드
  • 요청 시간
  • 클라이언트 정보
  • 장애 발생 패턴

27장 Docker에서는 stdout 로그도 유용하다#

컨테이너 환경에서는 파일보다 stdout으로 로그를 출력하는 방식을 사용할 수도 있습니다.

{
    log {
        output stdout
        format json
    }
}

Docker에서는:

docker logs caddy

로 로그를 확인할 수 있습니다.


28장 Global Options란 무엇인가#

Caddyfile 가장 위에는 전체 설정에 적용되는 Global Options Block을 둘 수 있습니다.

공식 문서에 따르면 Global Options Block은 Caddyfile 최상단에 하나만 둘 수 있습니다.

예:

{
    email admin@example.com
    debug
}

그 아래에 사이트를 정의합니다.

{
    email admin@example.com
}

example.com {
    reverse_proxy localhost:8080
}

29장 debug 모드#

문제가 발생했을 때:

{
    debug
}

를 사용할 수 있습니다.

Debug 모드는 로그를 매우 상세하게 보여주기 때문에 장애 분석에는 도움이 되지만 운영 환경에서는 로그량이 크게 증가할 수 있습니다. 공식 문서도 debug가 매우 verbose하다고 설명합니다.


30장 ACME 이메일 지정#

인증서 발급에 사용할 이메일을 설정할 수 있습니다.

{
    email admin@example.com
}

공식 문서에서도 ACME 계정 문제 발생 시 연락을 받을 수 있으므로 이메일 설정을 권장합니다.


31장 Admin API는 외부에 공개하지 않는 것이 중요하다#

Caddy는 기본적으로 Admin API를 제공합니다.

기본 주소는:

localhost:2019

입니다.

이 API는 Caddy 설정을 변경할 수 있기 때문에 인터넷에 무방비로 노출해서는 안 됩니다.

flowchart LR
    Internet[Internet]
    X[차단]

    Admin[Caddy Admin API<br/>localhost:2019]

    Internet -.-> X
    X -.-> Admin

32장 Admin API를 끌 수도 있다#

{
    admin off
}

다만 공식 문서에서는 Admin API를 끄면 caddy reload를 사용할 수 없다고 설명합니다.

따라서 단순히 보안을 위해 끄는 것보다는 localhost에 유지하고 외부에 노출하지 않는 구성이 운영상 더 편리할 수 있습니다.


33장 Reverse Proxy Backend가 여러 개일 때#

다음처럼 여러 upstream을 지정할 수 있습니다.

example.com {

    reverse_proxy {
        to app1:8080 app2:8080 app3:8080
    }
}

구조는 다음과 같습니다.

flowchart LR
    U[사용자]
    C[Caddy]

    A1[App 1]
    A2[App 2]
    A3[App 3]

    U --> C

    C --> A1
    C --> A2
    C --> A3

34장 기본 Load Balancing#

Caddy reverse proxy는 여러 upstream에 대해 Load Balancing을 지원합니다.

공식 문서 기준 기본 정책은 random입니다.

명시적으로 Round Robin을 사용할 수도 있습니다.

example.com {

    reverse_proxy app1:8080 app2:8080 app3:8080 {
        lb_policy round_robin
    }
}

35장 Round Robin 구조#

sequenceDiagram
    participant U as 사용자
    participant C as Caddy
    participant A as APP1
    participant B as APP2
    participant D as APP3

    U->>C: Request 1
    C->>A: 전달

    U->>C: Request 2
    C->>B: 전달

    U->>C: Request 3
    C->>D: 전달

요청을 순차적으로 분배하는 방식입니다.


36장 Primary·Standby Failover 구조#

첫 번째 서버를 우선 사용하고 장애가 발생하면 두 번째 서버로 넘기는 방식도 구성할 수 있습니다.

example.com {

    reverse_proxy primary:8080 standby:8080 {

        lb_policy first

        health_uri /health
    }
}

공식 문서에 따르면 first 정책은 순서상 첫 번째로 사용 가능한 upstream을 선택해 Primary/Secondary Failover를 구성할 수 있으며, 실제 장애 전환을 위해 헬스체크를 함께 사용하는 것이 중요합니다.


37장 Failover 구조#

flowchart LR
    U[사용자]
    C[Caddy]

    P[Primary]
    S[Standby]

    U --> C

    C -->|정상| P
    C -. Primary 장애 .-> S

소규모 서비스에서도 비교적 간단하게 고가용성 구조를 만들 수 있습니다.


38장 Active Health Check#

Caddy는 backend 상태를 주기적으로 확인할 수 있습니다.

예:

example.com {

    reverse_proxy app1:8080 app2:8080 {

        health_uri /health

        health_interval 10s

        health_timeout 2s
    }
}

공식 문서에서는 health_uri 또는 health_port를 설정하면 Active Health Check가 활성화된다고 설명합니다.


39장 Health Check 동작#

sequenceDiagram
    participant C as Caddy
    participant A as App1
    participant B as App2

    C->>A: GET /health
    A-->>C: 200 OK

    C->>B: GET /health
    B-->>C: 500 Error

    Note over C,B: App2를 비정상 upstream으로 판단

40장 Passive Health Check#

Active 방식과 달리 실제 요청 결과를 보고 장애를 판단하는 방식도 있습니다.

공식 문서에서는 fail_duration을 지정하면 Passive Health Check가 활성화됩니다.

예:

example.com {

    reverse_proxy app1:8080 app2:8080 {

        fail_duration 30s

        max_fails 3
    }
}

41장 Active와 Passive Health Check 차이#

구분 Active Passive
검사 방식 별도 요청 실제 사용자 요청
대표 설정 health_uri fail_duration
장점 장애 사전 감지 실제 요청 상태 반영
단점 Health endpoint 필요 장애를 실제 요청 후 발견

두 방식을 함께 사용하는 것도 가능합니다.


42장 Backend가 HTTPS인 경우#

Backend 자체가 HTTPS를 제공한다면 다음처럼 설정할 수 있습니다.

example.com {
    reverse_proxy https://backend.example.internal
}

Caddy의 reverse proxy는 HTTPS upstream도 지원합니다.

구조는 다음과 같습니다.

flowchart LR
    U[사용자]
    C[Caddy]
    A[Backend]

    U -->|HTTPS| C
    C -->|HTTPS| A

43장 WebSocket은 별도 서버를 설치할 필요가 없다#

일반적인 WebSocket 애플리케이션 역시 Caddy reverse proxy를 통해 연결할 수 있습니다.

구조는:

flowchart LR
    B[Browser]
    C[Caddy]
    W[WebSocket Server]

    B <-->|WebSocket| C
    C <-->|WebSocket| W

입니다.

일반적인 HTTP 기반 WebSocket 서비스라면 Caddy의 reverse proxy 구조를 그대로 활용할 수 있습니다.


44장 Header를 Backend에 전달하거나 변경하기#

실제 환경에서는 backend가 다음 정보를 알아야 할 수 있습니다.

  • 원래 Host
  • HTTPS 여부
  • Client IP

Caddy reverse proxy는 이러한 요청·응답 Header를 조정할 수 있습니다.

다만 기본 Caddy reverse proxy가 이미 일반적인 proxy header를 처리하므로 필요 이상으로 헤더를 수동 설정하지 않는 것이 좋습니다.


45장 Caddyfile Snippet으로 반복 설정 줄이기#

여러 사이트에서 동일한 설정을 사용한다면 공통 블록을 만들어 재사용할 수 있습니다.

예:

(common) {
    encode zstd gzip

    header {
        X-Content-Type-Options nosniff
    }

    log
}

그리고:

example.com {

    import common

    reverse_proxy app:8080
}

처럼 사용할 수 있습니다.

서비스가 많아질수록 Caddyfile 중복을 줄이는 데 유용합니다.


46장 실제 Docker VPS용 예제#

여러 서비스를 운영하는 현실적인 예입니다.

{
    email admin@example.com
}

thinkx.example.com {

    encode zstd gzip

    log

    reverse_proxy trilium:8080
}

api.example.com {

    encode zstd gzip

    log

    reverse_proxy api:3000
}

admin.example.com {

    basic_auth {
        admin HASHED_PASSWORD
    }

    reverse_proxy admin:9000
}

monitor.example.com {

    reverse_proxy grafana:3000
}

구조는 다음과 같습니다.

flowchart TB
    I[Internet]

    C[Caddy<br/>Automatic HTTPS]

    T[Trilium]
    API[API]
    A[Admin]
    G[Grafana]

    I --> C

    C --> T
    C --> API
    C --> A
    C --> G

47장 SPA + API 통합 예제#

프론트엔드 정적 파일과 API를 하나의 도메인에서 운영할 수도 있습니다.

example.com {

    encode zstd gzip

    handle_path /api/* {
        reverse_proxy api:3000
    }

    handle {
        root * /srv/frontend
        try_files {path} /index.html
        file_server
    }
}

구조는 다음과 같습니다.

flowchart LR
    U[Browser]
    C[Caddy]

    API[API Server]
    SPA[SPA Frontend]

    U --> C

    C -->|/api/*| API
    C -->|그 외| SPA

48장 Maintenance 페이지 처리#

애플리케이션 점검 시 Caddy에서 간단한 응답을 만들 수도 있습니다.

example.com {
    respond "서비스 점검 중입니다." 503
}

또는 별도의 정적 Maintenance 페이지를 제공하는 구조도 만들 수 있습니다.


49장 설정 변경 전 반드시 검증하기#

운영 서버에서 가장 중요한 습관 중 하나입니다.

Caddy는 caddy validate 명령을 제공합니다.

caddy validate --config /etc/caddy/Caddyfile

설정에 문제가 없다면 그 다음 Reload를 수행하는 것이 좋습니다.


50장 Caddyfile 자동 정리#

Caddyfile 형식을 정리하려면:

caddy fmt --overwrite /etc/caddy/Caddyfile

을 사용할 수 있습니다.

Caddy CLI는 fmt 명령을 공식적으로 제공합니다.

사람이 직접 작성하면서 생긴 들여쓰기와 형식을 정돈하는 데 유용합니다.


51장 Caddyfile을 JSON으로 확인하기#

Caddy는 내부적으로 JSON 설정을 사용합니다.

Caddyfile이 실제로 어떤 JSON 설정으로 변환되는지 보고 싶다면:

caddy adapt --config /etc/caddy/Caddyfile --pretty

를 사용할 수 있습니다.

공식 문서에서도 caddy adapt가 Caddyfile을 native JSON config로 변환한다고 설명합니다.


52장 운영 중에는 Restart보다 Reload#

설정을 바꿀 때 매번 Caddy 프로세스를 종료하고 다시 실행할 필요는 없습니다.

공식 문서에서는 운영 중 설정 변경 시 caddy reload를 사용하는 것을 권장합니다.

caddy reload --config /etc/caddy/Caddyfile

구조는 다음과 같습니다.

flowchart LR
    C1[현재 Caddy]
    F[새 Caddyfile]
    V[Validate]
    R[Reload]
    C2[새 설정 적용]

    F --> V
    V --> R
    C1 --> R
    R --> C2

53장 실무에서는 이 순서로 적용하는 것이 안전하다#

Caddyfile 수정

↓

caddy fmt

↓

caddy validate

↓

caddy reload

전체 흐름은 다음과 같습니다.

flowchart LR
    A[Caddyfile 수정]
    B[caddy fmt]
    C[caddy validate]
    D{정상?}
    E[caddy reload]
    F[수정]

    A --> B --> C --> D
    D -->|Yes| E
    D -->|No| F
    F --> A

이 습관만으로도 설정 오류 때문에 서비스가 중단되는 문제를 많이 줄일 수 있습니다.


54장 systemd 환경에서의 기본 운영#

Linux 패키지로 Caddy를 설치한 경우 systemd 서비스로 실행하는 환경이 많습니다.

상태 확인:

systemctl status caddy

로그 확인:

journalctl -u caddy

실시간 확인:

journalctl -u caddy -f

실제 서비스 파일이나 설치 방식은 사용한 배포 방식에 따라 달라질 수 있습니다.


55장 Docker에서는 Caddy Reload 방식도 고려해야 한다#

컨테이너를 매번 내렸다 올리는 방법도 있지만 그 과정에서 불필요한 연결 단절이 발생할 수 있습니다.

운영 환경에서는 가능한 경우 Caddy 설정 Reload 방식을 사용하는 것이 좋습니다.

공식 문서에서도 프로덕션 설정 변경은 서버를 stop/start하는 대신 reload를 사용하라고 안내합니다.


56장 Caddy 설정의 전체 흐름#

Caddy를 실제 서비스에 적용하는 흐름을 정리하면 다음과 같습니다.

flowchart TB
    DNS[DNS]
    FW[Firewall<br/>80 · 443]
    C[Caddy]

    TLS[Automatic HTTPS]
    ROUTE[L7 Routing]
    LB[Load Balancer]

    APP1[Application 1]
    APP2[Application 2]
    STATIC[Static Site]

    DNS --> FW
    FW --> C

    C --> TLS
    TLS --> ROUTE
    ROUTE --> LB

    LB --> APP1
    LB --> APP2
    ROUTE --> STATIC

57장 실무에서 자주 사용하는 Caddy 지시어#

지시어 역할
reverse_proxy Backend 프록시
handle 요청 분기
handle_path Prefix 제거 후 분기
rewrite 내부 URI 변경
redir 브라우저 Redirect
root 정적 파일 Root
file_server 정적 파일 서비스
encode gzip·zstd 압축
header HTTP Header 설정
basic_auth Basic Authentication
log HTTP Access Log
respond 직접 HTTP 응답
import 공통 설정 재사용

Caddy의 기본 HTTP Caddyfile에는 이 외에도 다양한 directive가 포함되어 있습니다.


58장 운영 환경에서 기본적으로 고려할 구성#

실제 Caddy 운영에서는 최소한 다음 항목을 확인하는 것이 좋습니다.

DNS
Firewall
80 / 443 Port
Automatic HTTPS
Reverse Proxy
Docker Network
Access Log
Backend Health
Admin API 노출 여부
설정 Validation
Reload 절차

단순히 사이트가 열리는지만 확인하면 운영 환경에서는 부족합니다.


Caddy 상세 설정 FAQ#

Caddyfile은 어디에 두는가#

설치 방법에 따라 다르지만 패키지 설치 환경에서는 일반적으로 /etc/caddy/Caddyfile 형태로 관리하는 경우가 많습니다.

Caddy 설정을 바꾸면 서버를 재시작해야 하는가#

반드시 그렇지 않습니다. 운영 중에는 caddy reload를 이용해 새 설정을 적용할 수 있으며 공식 문서에서도 프로덕션에서는 Reload 사용을 권장합니다.

설정 오류는 어떻게 확인하는가#

다음 명령을 사용할 수 있습니다.

caddy validate --config Caddyfile

공식 CLI에서 제공하는 설정 검증 기능입니다.

여러 도메인을 Caddy 하나에서 운영할 수 있는가#

가능합니다. 각 도메인마다 Site Block을 만들고 서로 다른 Backend에 reverse_proxy하면 됩니다.

URL 경로에 따라 다른 서버로 보낼 수 있는가#

가능합니다. handle, handle_path 또는 matcher를 이용할 수 있습니다.

/api Prefix를 Backend에 전달하지 않으려면#

handle_path를 사용할 수 있습니다. 공식 문서에 따르면 handle_path는 매칭된 Prefix를 자동으로 제거합니다.

React나 Vue SPA도 서비스할 수 있는가#

가능합니다. root, try_files, file_server를 조합하는 방식이 일반적입니다.

gzip 압축을 사용할 수 있는가#

가능합니다.

encode gzip

또는 Zstd와 함께:

encode zstd gzip

를 사용할 수 있습니다.

Caddy에서 로드밸런싱이 가능한가#

가능합니다. reverse_proxy에 여러 upstream을 지정하고 lb_policy를 설정할 수 있습니다. 기본 정책은 random입니다.

Backend 장애를 자동으로 감지할 수 있는가#

Active Health Check와 Passive Health Check를 사용할 수 있습니다. Active는 health_uri, Passive는 fail_duration 등을 이용합니다.

Caddy Admin API를 외부에 공개해도 되는가#

권장하지 않습니다. 기본 Admin API는 localhost:2019에서 동작하며 설정을 제어할 수 있으므로 외부에 무방비로 노출하면 위험합니다.

Docker에서는 Backend를 어떻게 지정하는가#

같은 Docker Network라면 컨테이너 이름을 이용할 수 있습니다.

reverse_proxy trilium:8080

처럼 구성할 수 있습니다.

핵심 정리#

Caddy를 실제로 운영할 때 가장 기본적인 형태는 다음과 같습니다.

example.com {
    reverse_proxy app:8080
}

여기서 필요에 따라 기능을 추가합니다.

example.com {

    encode zstd gzip

    log

    header {
        X-Content-Type-Options nosniff
    }

    reverse_proxy app:8080
}

서비스가 여러 개라면:

flowchart TB
    I[Internet]
    C[Caddy]

    W[Web]
    A[API]
    ADM[Admin]
    M[Monitoring]

    I --> C

    C -->|www.example.com| W
    C -->|api.example.com| A
    C -->|admin.example.com| ADM
    C -->|monitor.example.com| M

로 확장할 수 있습니다.

Backend가 여러 개라면:

flowchart LR
    U[사용자]
    C[Caddy<br/>Load Balancer]

    A1[APP 1]
    A2[APP 2]
    A3[APP 3]

    U --> C

    C --> A1
    C --> A2
    C --> A3

까지 확장할 수 있습니다.

운영에서는 특히 다음 순서를 습관화하는 것이 좋습니다.

Caddyfile 수정
↓
caddy fmt
↓
caddy validate
↓
caddy reload

Caddy의 장점은 단순히 설정 문법이 짧다는 데 있지 않습니다.

Automatic HTTPS, Reverse Proxy, L7 라우팅, 정적 파일, 압축, 인증, 로그, Health Check, Load Balancing을 하나의 Caddyfile에서 일관된 방식으로 관리할 수 있다는 것이 실무적인 강점입니다.

특히 하나의 VPS에서 Docker 기반으로 여러 서비스를 운영한다면 Caddy를 앞단의 단일 진입점으로 두고 각 내부 서비스를 컨테이너 이름으로 연결하는 방식은 구조가 단순하고 유지보수하기 쉬운 구성입니다.

이 페이지의 목차