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 --> APP5장 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| B6장 여러 도메인을 하나의 Caddy에서 운영하기#
Caddy의 가장 실용적인 사용 방법 중 하나입니다.
예를 들어 서버 한 대에서 다음 서비스를 운영한다고 하겠습니다.
thinkx.example.com
admin.example.com
api.example.com
monitor.example.comCaddyfile은 다음처럼 구성할 수 있습니다.
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-appCaddyfile은 다음과 같이 구성할 수 있습니다.
example.com {
reverse_proxy app:8080
}중요한 점은 Caddy와 app이 같은 Docker Network에서 서로 통신할 수 있어야 한다는 것입니다.
9장 Caddy의 인증서 데이터는 반드시 보존해야 한다#
Docker에서 Caddy를 운영한다면 /data 볼륨은 매우 중요합니다.
예:
volumes:
- ./caddy_data:/dataTLS 인증서와 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/*| ADM11장 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/usershandle_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
↓
/newRewrite#
서버 내부에서만 경로를 바꿉니다.
사용자
/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 --> F17장 정적 사이트와 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 -->|나머지| STATIC18장 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: 압축된 ResponseHTML·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 -.-> Admin32장 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 --> A334장 기본 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| A43장 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 --> G47장 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 -->|그 외| SPA48장 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 --> C253장 실무에서는 이 순서로 적용하는 것이 안전하다#
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 --> STATIC57장 실무에서 자주 사용하는 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 reloadCaddy의 장점은 단순히 설정 문법이 짧다는 데 있지 않습니다.
Automatic HTTPS, Reverse Proxy, L7 라우팅, 정적 파일, 압축, 인증, 로그, Health Check, Load Balancing을 하나의 Caddyfile에서 일관된 방식으로 관리할 수 있다는 것이 실무적인 강점입니다.
특히 하나의 VPS에서 Docker 기반으로 여러 서비스를 운영한다면 Caddy를 앞단의 단일 진입점으로 두고 각 내부 서비스를 컨테이너 이름으로 연결하는 방식은 구조가 단순하고 유지보수하기 쉬운 구성입니다.