본문 바로가기
WIKI 기술 지식 베이스

전역 옵션

원문 보기 위키 갱신

전역 옵션 (Global Options)

출처: Caddy 공식 문서

본문

Caddyfile에는 전역적으로 적용되는 옵션을 지정하는 방법이 있어요. 일부 옵션은 기본값으로 작동하고, 다른 옵션은 HTTP 서버를 커스터마이즈하며 특정 사이트 하나에만 적용되지 않고, 또 다른 옵션들은 Caddyfile 어댑터의 동작을 커스터마이즈해요.

Caddyfile의 맨 위는 전역 옵션 블록일 수 있어요. 이는 키가 없는 블록이에요:

{
	...
}

최대 하나만 있을 수 있고, Caddyfile의 첫 번째 블록이어야 해요.

가능한 옵션은 (각 옵션을 클릭하면 문서로 이동해요):

{
	# General Options
	debug
	http_port    <port>
	https_port   <port>
	default_bind <hosts...>
	order <dir1> first|last|[before|after <dir2>]
	storage <module_name> {
		<options...>
	}
	storage_clean_interval <duration>
	admin   off|<addr> {
		origins <origins...>
		enforce_origin
	}
	persist_config off
	log [name] {
		output  <writer_module> ...
		format  <encoder_module> ...
		level   <level>
		include <namespaces...>
		exclude <namespaces...>
	}
	grace_period   <duration>
	shutdown_delay <duration>
	metrics {
		per_host
		observe_catchall_hosts
		otlp
	}

	# TLS Options
	auto_https off|disable_redirects|ignore_loaded_certs|disable_certs
	tls_automate_names <names...>
	email <yours>
	default_sni <name>
	fallback_sni <name>
	local_certs
	skip_install_trust
	acme_ca <directory_url>
	acme_ca_root <pem_file>
	acme_eab {
		key_id <key_id>
		mac_key <mac_key>
	}
	acme_dns <provider> ...
	dns <provider> ...
	tls_resolvers <resolvers...>
	ech <public_names...> {
		dns <provider> ...
	}
	on_demand_tls {
		ask        <endpoint>
		permission <module>
	}
	key_type ed25519|p256|p384|rsa2048|rsa4096
	cert_issuer <name> ...
	renew_interval <duration>
	cert_lifetime  <duration>
	ocsp_interval  <duration>
	ocsp_stapling off
	renewal_window_ratio <ratio>
	preferred_chains [smallest] {
		root_common_name <common_names...>
		any_common_name  <common_names...>
	}

	# Server Options
	servers [<listener_address>] {
		name <name>
		listener_wrappers {
			<listener_wrappers...>
		}
		timeouts {
			read_body       <duration>
			read_body_idle  <duration> [<min_rate>]
			read_header     <duration>
			write           <duration>
			write_idle      <duration> [<min_rate>]
			write_max_chunk <size>
			idle            <duration>
		}
		keepalive_interval <duration>
		keepalive_idle     <duration>
		keepalive_count	   <number>
		0rtt off

		trusted_proxies <module> ...
		trusted_proxies_strict
		trusted_proxies_unix
		client_ip_headers <headers...>

		trace
		max_header_size <size>
		enable_full_duplex
		expected_underscore_headers <headers...>
		expected_dot_headers        <headers...>
		log_credentials
		protocols [h1|h2|h2c|h3]
		strict_sni_host [on|insecure_off]
	}

	# File Systems
	filesystem <name> <module> {
		<options...>
	}

	# PKI Options
	pki {
		ca [<id>] {
			name                  <name>
			root_cn               <name>
			intermediate_cn       <name>
			intermediate_lifetime <duration>
			maintenance_interval  <duration>
			renewal_window_ratio  <ratio>
			root {
				format <format>
				cert   <path>
				key    <path>
			}
			intermediate {
				format <format>
				cert   <path>
				key    <path>
			}
		}
	}

	# Event options
	events {
		on <event> <handler...>
	}
}

일반 옵션 (General Options)

debug

디버그 모드를 활성화해요. 이는 기본 로거의 로그 레벨을 DEBUG로 설정해요. 이는 문제 해결에 유용한 더 많은 세부 정보를 드러내요(프로덕션에서는 매우 장황해요). 커뮤니티 포럼에서 도움을 구하기 전에 이걸 활성화해 달라고 요청해요. 예를 들어 다른 전역 옵션이 없다면 Caddyfile 맨 위에:

{
	debug
}
http_port

서버가 HTTP에 사용할 포트예요.

내부용으로만 사용돼요. 클라이언트의 HTTP 포트를 바꾸지 않아요. 주로 내부 네트워크에서 라우팅 목적으로 Caddy에 도달하기 전에 80을 다른 포트(예: 8080)로 포트 포워딩해야 할 때 사용돼요.

기본: 80

https_port

서버가 HTTPS에 사용할 포트예요.

내부용으로만 사용돼요. 클라이언트의 HTTPS 포트를 바꾸지 않아요. 주로 내부 네트워크에서 라우팅 목적으로 Caddy에 도달하기 전에 443을 다른 포트(예: 8443)로 포트 포워딩해야 할 때 사용돼요.

기본: 443

default_bind

사이트에서 bind지시문을 사용하지 않으면 모든 사이트에 사용될 기본 바인드 주소(들)예요. 기본: 비어 있음, 즉 모든 인터페이스에 바인딩.

이것은 Caddyfile이 생성한 서버에만 적용된다는 점을 명심하세요. 즉 자동 HTTPS가 HTTP→HTTPS 리다이렉트용으로 만든 HTTP 서버는 이 바인드 주소를 상속받지 않아요. 이를 해결하려면 Caddyfile이 어댑트될 때 바인드 주소를 받도록 http:// 사이트(지시문 없이 비어 있을 수 있음)를 선언해 주세요.

{
	default_bind 10.0.0.1
}
order

HTTP 핸들러 지시문(들)에 순서를 할당해요. HTTP 핸들러는 순차 체인으로 실행되므로 핸들러를 올바른 순서로 실행하는 것이 필요해요. 표준 지시문은 사전 정의된 순서를 갖지만, 3rd-party HTTP 핸들러 모듈을 사용한다면 이 옵션을 사용하거나 지시문을 route블록에 배치해 순서를 명시적으로 정의해야 해요. 순서는 절대적(first 또는 last)으로 또는 다른 지시문에 상대적(before 또는 after)으로 설명할 수 있어요.

예를 들어 replace-response플러그인을 사용하려면 응답이 인코딩되기 전에 대체를 수행할 수 있도록(응답은 핸들러 체인을 아래로가 아니라 위로 흐르기 때문에) 그 지시문이 encode 뒤에 오도록 해야 해요:

{
	order replace after encode
}
storage

Caddy의 스토리지 메커니즘을 구성해요. 기본은 file_system이에요. 플러그인으로 제공되는 다른 많은 스토리지 모듈이 있어요.

예를 들어 파일 시스템의 스토리지 위치를 바꾸려면:

{
	storage file_system /path/to/custom/location
}

스토리지 모듈 커스터마이징은 Caddy의 여러 인스턴스가 모두 같은 인증서와 키를 사용하도록 스토리지를 동기화할 때 보통 필요해요. 자세한 내용은 스토리지의 자동 HTTPS 섹션을 참고하세요.

storage_clean_interval

스토리지 유닛에서 오래되거나 만료된 자산을 스캔하고 제거하는 빈도예요. 이러한 스캔은 스토리지 모듈에 많은 읽기(및 목록 연산)를 가하므로 대규모 배포에서는 더 긴 간격을 선택하세요. 기간 값을 받아요.

스토리지는 프로세스가 처음 시작할 때 항상 정리돼요. 그런 다음 이전 정리가 이 간격의 절반 미만의 시간에 끝났다면 새 정리가 이전 정리가 시작된 후 이 기간 이후에 시작돼요(그렇지 않으면 다음 시작이 건너뛰어져요).

기본: 24h

{
	storage_clean_interval 7d
}
admin

admin API 엔드포인트를 커스터마이즈해요. 플레이스홀더를 받아요. 네트워크 주소를 받아요.

기본: localhost:2019, CADDY_ADMIN 환경 변수가 설정되지 않은 한.

off로 설정하면 admin 엔드포인트가 비활성화돼요. 비활성화되면 caddy reload명령이 admin API를 사용해 실행 중인 서버에 새 구성을 밀어 넣으므로, 서버를 중지하고 시작하지 않고는 구성 변경이 불가능해요.

실행 중인 서버의 주소가 기본에서 바뀌었다면 호환 명령과 함께 --address CLI 플래그를 사용해 현재 admin 엔드포인트를 지정하는 것을 기억하세요.

또한 다음 하위 옵션을 지원해요:

origins는 엔드포인트에 연결하도록 허용된 원본(origins) 목록을 구성해요.

기본이 지능적으로 선택돼요:

  • 리스너 주소가 루프백(예: localhost나 루프백 IP, 또는 unix 소켓)이라면 허용된 원본은 리스너 주소 포트와 결합된 localhost, ::1, 127.0.0.1이에요(그래서 localhost:2019가 유효한 원본).

  • 리스너 주소가 루프백이 아니면 허용된 원본은 리스너 주소와 같아요.

리스너 주소 호스트가 와일드카드 인터페이스가 아니면(와일드카드에는 빈 문자열, 0.0.0.0, [::] 포함) Host 헤더 강제가 수행돼요. 사실상 기본적으로 인터페이스가 localhost이므로 Host 헤더가 origins에 있는지 검증돼요. 하지만 와일드카드 인터페이스가 있는 :2020 같은 주소에 대해서는 Host 헤더 검증이 수행되지 않아요.

enforce_origin은 Origin 요청 헤더의 강제를 시행해요. 클라이언트가 CORS 헤더를 보내거나 Sec-Fetch-Mode: no-cors로 CORS를 명시적으로 비활성화할 때마다 암시적으로 수행돼요. 그 외에는 이 옵션은 리스너 주소가 와일드카드 인터페이스일 때(Host가 검증되지 않으므로) 그리고 admin API가 공개 인터넷에 노출될 때 가장 유용해요. CORS preflight 검사를 활성화하고 Origin 헤더가 origins 목록에 대해 검증되도록 보장해요. 개발 머신에서 Caddy를 실행하고 웹 브라우저에서 admin API에 접근해야 할 때만 사용하세요.

예를 들어 admin API를 모든 인터페이스의 다른 포트에 노출하려면 — ⚠️ 이 포트는 공개적으로 노출하면 안 됩니다, 그렇지 않으면 누구나 서버를 제어할 수 있어요. 공개로 만들어야 한다면 origin 강제를 활성화하는 것을 고려하세요:

{
	admin :2020
}

admin API를 끄려면 — ⚠️ 이렇게 하면 서버를 중지하고 시작하지 않고는 구성 리로드가 불가능해집니다:

{
	admin off
}

admin API에 unix 소켓을 사용해 파일 권한으로 접근 제어를 하려면:

{
	admin unix//run/caddy-admin.sock
}

프로세스 사용자는 소켓 경로를 생성(및 교체)할 수 있어야 해요. 패키지 설치본은 종종 caddy 같은 비-root 사용자로 실행되며, 이는 보통 /run 아래에 쓸 수 없어요. 대신 그 사용자가 소유한 디렉터리(예: 서비스 데이터 디렉터리나 유닛 RuntimeDirectory 아래)를 사용하거나 권한을 조정하세요. 먼저 touch로 빈 파일을 만들어도 충분하지 않아요. Caddy는 소켓을 바인딩해야 해요.

일치하는 Origin 헤더가 있는 요청만 허용하려면:

{
	admin :2019 {
		origins http://localhost:2019 http://example.com:8080
		enforce_origin
	}
}
persist_config

admin API를 통해 수행된 구성 변경이 손실되지 않도록 현재 JSON 구성을 구성 디렉터리에 유지할지 제어해요. 현재는 off 옵션만 지원돼요. 기본적으로 구성은 유지돼요.

{
	persist_config off
}
log

명명된 로거를 구성해요.

이름을 전달해 동작을 커스터마이즈할 특정 로거를 나타낼 수 있어요. 이름을 지정하지 않으면 default 로거의 동작이 수정돼요. default 로거와 Caddy에서 로깅이 작동하는 방식에 대한 설명은 해당 문서에서 더 읽을 수 있어요.

서로 다른 이름의 여러 로거는 log를 여러 번 사용해 구성할 수 있어요.

이는 HTTP 요청 로깅(액세스 로그라고도 함)만 구성하는 log지시문과 다르다는 점을 주의하세요. log 전역 옵션은 지시문과 구성 구조를 공유하며(include와 exclude 제외), 전체 문서는 지시문 페이지에서 찾을 수 있어요.

output은 로그를 쓸 위치를 구성해요.

전체 문서는 log지시문을 참고하세요.

format은 로그를 인코딩하거나 형식화하는 방법을 설명해요.

전체 문서는 log지시문을 참고하세요.

level은 로그할 최소 항목 레벨이에요.

기본: INFO.

가능한 값: DEBUG, INFO, WARN, ERROR, 그리고 아주 드물게 PANIC, FATAL.

include는 이 로거에 포함할 로그 이름을 지정해요.

기본적으로 이 목록은 비어 있어요(즉 모든 로그가 포함됨).

예를 들어 admin API가 내보내는 로그만 포함하려면 admin.api를 포함하면 돼요.

exclude는 이 로거에서 제외할 로그 이름을 지정해요.

기본적으로 이 목록은 비어 있어요(즉 제외되는 로그 없음).

예를 들어 HTTP 액세스 로그만 제외하려면 http.log.access를 제외하면 돼요.

include와 exclude가 받는 로거 이름은 사용된 모듈에 따라 달라지며, 가장 쉬운 발견 방법은 이전 로그에서 확인하는 것이에요.

다음은 모든 http 액세스 로그와 admin 로그를 stdout에 json으로 기록하는 예제예요:

{
	log default {
		output stdout
		format json
		include http.log.access admin.api
	}
}
grace_period

HTTP 서버 종료를 위한 유예 기간을 정의해요(즉 구성 변경 중 또는 Caddy가 중지할 때).

유예 기간 동안 새 연결은 수락되지 않고, 유휴 연결은 닫히며, 활성 연결은 요청을 끝내도록 조급하게 기다려져요. 클라이언트가 유예 기간 내에 요청을 끝내지 않으면 서버는 리로드가 완료되고 리소스를 확보할 수 있도록 강제 종료돼요. 기간 값을 받아요.

기본적으로 유예 기간은 영원하며, 연결이 강제로 닫히지 않아요.

{
	grace_period 10s
}
shutdown_delay

중지될 서버가 유예 기간 전에 정상적으로 계속 작동하는 기간을 정의해요. 단, {http.shutting_down} 플레이스홀더가 true로 평가되고 {http.time_until_shutdown}이 유예 기간이 시작될 때까지의 시간을 제공해요.

이는 구성 변경의 일부로 어떤 서버가 종료되면 지연을 일으키고 사실상 변경을 더 늦은 시간으로 예약해요. 이 서버의 임박한 종료를 헬스 체커에 알리고 로드 밸런서가 로테이션에서 빼낼 시간을 주는 데 유용해요. 예:

{
	shutdown_delay 30s
}

example.com {
	handle /health-check {
		@goingDown vars {http.shutting_down} true
		respond @goingDown "Bye-bye in {http.time_until_shutdown}" 503
		respond 200
	}
	handle {
		respond "Hello, world!"
	}
}

TLS 옵션 (TLS Options)

auto_https

Caddy가 사이트의 인증서 관리와 HTTP→HTTPS 리다이렉트를 자동화할 수 있게 하는 기능인 자동 HTTPS를 구성해요.

선택할 수 있는 몇 가지 모드가 있어요:

off: 인증서 자동화와 HTTP→HTTPS 리다이렉트를 모두 비활성화해요.

disable_redirects: HTTP→HTTPS 리다이렉트만 비활성화해요.

disable_certs: 인증서 자동화만 비활성화해요.

ignore_loaded_certs: 수동으로 로드된 인증서에 나타나는 이름에 대해서도 인증서를 자동화해요. tls지시문으로 지정한 인증서에 자동으로 관리되길 원하는 이름(또는 와일드카드)이 포함된 경우 유용해요.

이 옵션은 사이트 주소가 유효한 도메인 이름을 가질 때 항상 HTTPS인 Caddy의 기본 프로토콜에는 영향을 주지 않아요. 즉 auto_https off는 사이트가 HTTP로 서빙되게 하지 않고, 자동 인증서 관리와 리다이렉트만 비활성화해요.

즉 사이트를 HTTP로 서빙하려면 사이트 주소를 http://로 접두사 붙이거나 :80으로 접미사 붙이거나(또는 http_port옵션 사용) 바꿔야 해요.

{
	auto_https disable_redirects
}
tls_automate_names

주어진 이름에 대한 인증서를 서빙하지 않고 관리해요. 라우트가 추가되지 않아 Caddy가 그 이름에 응답하지 않아요. 해당 인증서만 관리돼요.

여기에 이름을 나열하는 것은 명시적인 요청이므로 일반 스위치보다 우선해요. auto_https가 off나 disable_certs로 설정되어도 인증서가 여전히 관리돼요. 이런 점에서 다른 관리 인증서가 적용될 때도 사이트의 자동화를 강제하는 tls force_automate지시문처럼 동작해요.

인증서가 필요하지만 Caddy의 HTTP 서버로 서빙하지 않는 이름에 사용하세요: 다른 사이트만 커버하는 와일드카드, 메일 서버, 또는 레이어 4 앱이 처리하는 이름. 전역 옵션만 포함하는 Caddyfile은 이 옵션이 설정되면 유효해요.

자체 사이트 블록도 있는 이름은 그 사이트의 인증서 설정을 유지해요. 반복할 수 있고 이름은 누적돼요.

(Caddy 2.11.6 이상 필요.)

{
	tls_automate_names *.example.com
}

foo.example.com {
	respond "Hello, world!"
}

이 옵션이 없으면 같은 것은 빈 사이트 블록을 요구하며, 이 블록은 구성하지 않은 이름을 포함해 일치하는 모든 이름에 대해 Caddy가 응답하게 해요:

*.example.com {
}
email

여러분의 이메일 주소예요. 주로 CA로 ACME 계정을 만들 때 사용되며, 인증서에 문제가 있을 경우를 대비해 매우 권장돼요.

Let's Encrypt가 인증서 만료 임박에 대해 이메일을 보낼 수 있다는 점을 명심하세요. 하지만 Caddy가 갱신 시 다른 발급자(예: ZeroSSL)를 선택했을 수 있으므로 이는 오해를 불러일으킬 수 있어요. 로그 및/또는 인증서 자체(예: 브라우저에서)를 확인해 어떤 발급자가 사용되었는지, 만료가 여전히 유효한지 보세요. 유효하면 Let's Encrypt의 이메일을 안전하게 무시할 수 있어요.

{
	email [email protected]
}
default_sni

클라이언트가 ClientHello에서 SNI를 사용하지 않을 때 기본 TLS ServerName을 설정해요.

{
	default_sni example.com
}
fallback_sni

⚠️ 실험적 기능

구성되면 원래 ServerName이 캐시의 어떤 인증서와도 일치하지 않을 때 폴백이 ClientHello의 TLS ServerName이 돼요.

이 용도는 매우 틈새적이에요. 일반적으로 클라이언트가 CDN이고 다운스트림 핸드셰이크의 ServerName을 통과시키지만 원본의 호스트 이름이 있는 인증서를 받아들일 수 있다면, 이것을 원본의 호스트 이름으로 설정하면 돼요. Caddy가 이 이름에 대한 인증서를 관리해야 한다는 점을 주의하세요.

{
	fallback_sni example.com
}
local_certs

모든 인증서가 Let's Encrypt 같은 (공개) ACME CA가 아니라 기본적으로 내부적으로 발급되게 해요. 개발 환경에서 빠른 토글로 유용해요.

{
	local_certs
}
skip_install_trust

로컬 CA의 루트를 시스템 신뢰 저장소 및 Java, Mozilla Firefox 신뢰 저장소에 설치하는 시도를 건너뛰어요.

{
	skip_install_trust
}
acme_ca

ACME CA 디렉터리의 URL을 지정해요. 테스트나 개발을 위해 Let's Encrypt의 스테이징 엔드포인트로 설정하는 것을 강력히 권장해요. 기본: ZeroSSL과 Let's Encrypt의 프로덕션 엔드포인트.

전역으로 구성된 ACME CA가 모든 사이트에 적용되지 않을 수 있음을 주의하세요. 기본 ACME 발급자(들) 사용의 호스트 이름 요구사항을 참고하세요.

{
	acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
}
acme_ca_root

시스템 신뢰 저장소에 없을 경우 ACME CA 엔드포인트용 신뢰된 루트 인증서를 포함하는 PEM 파일을 지정해요.

{
	acme_ca_root /path/to/ca/root.pem
}
acme_eab

모든 ACME 트랜잭션에 사용할 외부 계정 바인딩(External Account Binding)을 지정해요.

예를 들어 모의 ZeroSSL 자격 증명으로:

{
	acme_eab {
		key_id GD-VvWydSVFuss_GhBwYQQ
		mac_key MjXU3MH-Z0WQ7piMAnVsCpD1shgMiWx6ggPWiTmydgUaj7dWWWfQfA
	}
}
acme_dns

모든 ACME 트랜잭션에 사용할 ACME DNS 챌린지 제공자를 구성해요.

DNS 제공자용 플러그인이 있는 Caddy 커스텀 빌드가 필요해요.

제공자 이름 뒤에 오는 토큰들은 tls acme지시문의 발급자에서 지정한 것과 같은 방식으로 제공자를 설정해요.

{
	acme_dns cloudflare {env.CLOUDFLARE_API_TOKEN}
}
dns

관련 컨텍스트에서 로컬로 다른 것이 지정되지 않았을 때 사용할 기본 DNS 제공자를 구성해요. 예를 들어 ACME DNS 챌린지가 활성화되어 있지만 DNS 제공자 구성이 없으면 이 전역 기본값이 사용돼요. Encrypted ClientHello(ECH) 구성 게시에도 적용돼요.

이것이 작동하려면 Caddy 바이너리가 지정된 DNS 제공자 모듈로 컴파일되어야 해요.

환경 변수의 자격 증명을 사용한 예:

{
	dns cloudflare {env.CLOUDFLARE_API_TOKEN}
}

(Caddy 2.10 beta 1 이상 필요.)

tls_resolvers

TLS 관련 DNS 조회에 시스템 리졸버 대신 사용할 기본 DNS 리졸버를 구성해요. 이들은 ACME DNS 챌린지가 활성화될 때(acme_dns 또는 tls지시문 DNS 제공자 사용), 그리고 DNS 챌린지를 검증할 때 acme_server지시문에 사용돼요.

tls 지시문의 resolvers나 acme_server의 resolvers처럼 더 구체적으로 구성된 리졸버가 우선해요. 리졸버에 포트가 없으면 53이 사용돼요.

{
	tls_resolvers 1.1.1.1 8.8.8.8
}

(Caddy 2.11.2 이상 필요.)

ech

TLS 핸드셰이크에서 지정된 공개 도메인 이름(들)을 평문 서버 이름(SNI)으로 사용해 Encrypted ClientHello(ECH)를 활성화해요. 올바른 조건이 주어지면 ECH는 연결 중에 와이어상에서 사이트의 도메인 이름을 보호하는 데 도움이 될 수 있어요. Caddy는 지정된 각 공개 이름에 대해 ECH 구성 하나를 생성하고 게시해요. 게시는 호환 클라이언트(예: 제대로 구성된 최신 브라우저)가 ECH를 사용해 사이트에 접근하는 방법을 아는 방식이에요.

제대로 작동하려면 ECH 구성(들)이 클라이언트가 기대하는 방식으로 게시되어야 해요. 대부분의 브라우저(DNS-over-HTTPS 또는 DNS-over-TLS 활성화)는 ECH 구성이 HTTPS 타입 DNS 레코드에 게시되기를 기대해요. Caddy는 이 종류의 게시를 자동으로 하지만, dns 하위 옵션이나 전역의 dns전역 옵션으로 DNS 제공자를 지정해야 하고 Caddy 바이너리가 지정된 DNS 제공자 모듈로 빌드되어야 해요. (커스텀 빌드는 다운로드 페이지에서 가능해요.)

프라이버시 고지:

  • 일반적으로 익명성 집합(anonymity set)의 크기를 최대화하는 것이 좋아요. 그래서 대부분의 사용자는 모든 사이트를 보호하기 위해 공개 도메인 이름 하나만 구성하는 것을 보통 권장해요.

  • 서버는 지정한 공개 도메인 이름(들)에 대해 권위적(authoritative)이어야 해요 (즉 서버를 가리켜야 함) Caddy가 그에 대한 인증서를 얻을 것이기 때문이에요. 이 인증서는 일부 경우 스펙 준수 클라이언트가 ECH로 안정적이고 안전하게 연결하도록 돕는 데 중요해요. 이는 적절한 ECH 핸드셰이크를 용이하게 하는 데만 사용되고 애플리케이션 데이터(여러분의 사이트 - 공개 도메인 이름과 같은 사이트를 정의하지 않는 한)에는 사용되지 않아요.

  • 모든 상황이 다를 수 있어요. ECH는 만능 해결책이 아니므로 위험이 크다면 위협 모델을 검토하도록 전문가와 상의하는 것을 권장해요.

Cloudflare에 있는 네임서버에 게시하기 위해 환경 변수의 자격 증명을 사용한 예:

{
	dns cloudflare {env.CLOUDFLARE_API_TOKEN}
	ech ech.example.net
}

이렇게 하면 호환 클라이언트가 평문으로 노출된 개별 사이트 이름 대신 ech.example.net으로 모든 사이트를 로드해야 해요.

성공적인 게시에는 사이트의 도메인이 구성된 DNS 제공자에 있고 레코드가 주어진 자격 증명/제공자 구성으로 수정될 수 있어야 해요.

(Caddy 2.10 beta 1 이상 필요.)

on_demand_tls

활성화된 곳의 온디맨드 TLS를 구성하지만 활성화하지는 않아요(활성화하려면 tls지시문의 on_demand 하위 지시문을 사용하세요). 프로덕션 환경에서 악용을 방지하기 위해 필수예요.

ask는 Caddy가 주어진 URL에 HTTP 요청을 보내 도메인에 인증서가 발급되어도 되는지 물어보게 해요.

요청에는 도메인 이름 값을 포함하는 ?domain= 쿼리 문자열이 있어요.

엔드포인트가 2xx 상태 코드를 반환하면 Caddy는 그 이름에 대한 인증서를 얻을 권한을 얻게 돼요. 다른 상태 코드는 인증서 발급을 취소하고 TLS 핸드셰이크를 오류 처리해요.

ask 엔드포인트는 가능한 한 빨리, 이상적으로는 몇 밀리초 안에 반환해야 해요. 일반적으로 엔드포인트는 도메인 이름으로 인덱스된 데이터베이스에서 일정 시간(constant-time) 조회를 해야 해요. 루프는 피하세요. DNS 조회나 다른 네트워크 요청은 피하세요.

permission은 특정 이름에 인증서를 발급할지 결정하는 데 커스텀 모듈을 사용할 수 있게 해요. 모듈은 caddytls.OnDemandPermission인터페이스를 구현해야 해요. ask 옵션이 사용하는 http 권한 모듈이 포함되어 있으며, 이는 이전 버전과의 호환성을 위한 지름길로 남아 있어요.

⚠️ interval과 burst 비율 제한 옵션이 있었지만 권장되지 않아요. 아직 있으면 구성에서 제거하세요.

{
	on_demand_tls {
		ask http://localhost:9123/ask
	}
}

https:// {
	tls {
		on_demand
	}
}
key_type

TLS 인증서에 생성할 키의 타입을 지정해요. 커스터마이즈할 특정한 필요가 있을 때만 바꾸세요.

가능한 값: ed25519, p256, p384, rsa2048, rsa4096.

{
	key_type ed25519
}
cert_issuer

TLS 인증서의 발급자(또는 소스)를 정의해요.

이를 통해 tls issuer지시문의 하위 지시문으로 사이트별로 구성하는 대신 전역으로 발급자를 구성할 수 있어요.

시도할 발급자를 둘 이상 구성하려면 반복할 수 있어요. 정의된 순서대로 시도돼요.

{
	cert_issuer acme {
		...
	}
	cert_issuer zerossl {
		...
	}
}
renew_interval

로드되고 관리되는 모든 인증서를 만료에 대해 스캔하고, 만료되면 갱신을 트리거하는 빈도예요.

기본: 10m

{
	renew_interval 30m
}
cert_lifetime

CA에 인증서 발급을 요청할 유효 기간이에요.

이 값은 ACME 주문의 notAfter 필드를 계산하는 데 사용되므로 시스템 클록이 합리적으로 동기화되어야 해요. 참고: 모든 CA가 이를 지원하지는 않아요. CA의 ACME 문서를 확인해 허용되는지 어떤 값을 사용할 수 있는지 보세요.

기본: 0 (CA가 수명 선택, 보통 90일)

⚠️ 실험적 기능이에요. 변경되거나 제거될 수 있어요.

{
	cert_lifetime 30d
}
ocsp_interval

OCSP 스테이플을 업데이트해야 하는지 확인하는 빈도예요.

기본: 1h

{
	ocsp_interval 2h
}
ocsp_stapling

off로 설정해 OCSP 스테이플링을 비활성화할 수 있어요. 방화벽 때문에 리스폰더에 도달할 수 없는 환경에서 유용해요.

{
	ocsp_stapling off
}
renewal_window_ratio

Caddy가 인증서 갱신을 시도하기 전에 남아 있어야 하는 인증서 수명의 비율(0과 1 사이)이에요. 예를 들어 인증서 수명이 90일이고 이 비율이 0.3333(기본값)이면 Caddy는 만료까지 30일 이하 남았을 때 계속 갱신을 시도해요. tls renewal_window_ratio지시문의 하위 지시문으로 사이트별로도 설정할 수 있어요.

이것을 바꿀 필요는 거의 없지만, CA의 발급 시간이 매우 길다면 인증서 수명 후반에 갱신하는 것이 유용할 수 있어요.

ACME 발급자가 ARI 확장을 구현할 수 있고 이는 발급자가 ACME 클라이언트(여기선 Caddy)가 갱신을 시도해야 하는 창을 지시하며 그 창이 이 비율과 정렬되지 않을 수 있으므로, 이는 제안일 뿐임을 명심하세요.

{
	renewal_window_ratio 0.1
}
preferred_chains

CA가 여러 인증서 체인을 제공하면 이 옵션으로 Caddy가 어떤 체인을 선호할지 지정할 수 있어요. 다음 옵션 중 하나를 설정하세요:

smallest는 바이트 수가 가장 적은 체인을 선호하라고 Caddy에 알려줘요.

root_common_name은 하나 이상의 일반 이름 목록이에요. Caddy는 지정된 일반 이름 중 적어도 하나와 일치하는 루트가 있는 첫 번째 체인을 선택해요.

any_common_name은 하나 이상의 일반 이름 목록이에요. Caddy는 지정된 일반 이름 중 적어도 하나와 일치하는 발급자가 있는 첫 번째 체인을 선택해요.

preferred_chains를 전역 옵션으로 지정하면 발급자 레벨 구성을 덮어쓰는 것이 없을 때 모든 발급자에 영향을 준다는 것을 주의하세요.

{
	preferred_chains smallest
}
{
	preferred_chains {
		root_common_name "ISRG Root X2"
	}
}

서버 옵션 (Server Options)

잠재적으로 여러 사이트에 걸친 설정으로 HTTP 서버를 커스터마이즈해요. 그래서 사이트 블록에서 제대로 구성할 수 없어요. 이 옵션들은 HTTP 레이어 아래의 리스너/소켓이나 다른 시설에 영향을 줘요.

서버별로 다른 옵션을 구성하기 위해 서로 다른 listener_address 값으로 두 번 이상 지정할 수 있어요. 예를 들어 servers :443은 리스너 주소 :443에 바인딩된 서버에만 적용돼요. 리스너 주소를 생략하면 옵션이 나머지 서버에 적용돼요.

Caddyfile의 서버에 대한 리스너 주소를 찾으려면 caddy adapt 명령을 사용하세요.

예를 들어 포트 :80과 :443의 서버에 다른 옵션을 구성하려면 두 개의 servers 블록을 지정하면 돼요:

{
	servers :443 {
		listener_wrappers {
			http_redirect
			tls
		}
	}

	servers :80 {
		protocols h1 h2c
	}
}

servers를 사용할 때는 Caddyfile에 실제로 나타나는(즉 사이트 블록이 생성하는) 서버에만 적용돼요. 자동 HTTPS는 HTTP→HTTPS 리다이렉트를 서빙하고 ACME HTTP 챌린지를 해결하기 위해 포트 80(또는 http_port옵션)에서 수신 대기하는 서버를 만들지만, 이는 Caddyfile 어댑터가 servers를 적용한 후에 런타임에 발생해요. 즉 다시 말해 servers는 http://나 :80 같은 사이트 블록을 명시적으로 선언하지 않는 한 :80에 적용되지 않는다는 뜻이에요.

bind지시문이나 default_bind전역 옵션을 사용하면 listener_address 반드시 바인드 주소와 사이트 블록의 포트를 결합한 것과 일치해야 해요. 그렇지 않으면 설정이 적용되지 않아요. 예:

{
	# This will NOT match the server, bind address missing
	servers :8080 {
		name private
	}

	# This will work because it's an exact match
	servers 192.168.1.2:8080 {
		name public
	}
}

:8080 {
	bind 127.0.0.1
}

:8080 {
	bind 192.168.1.2
}
name

이 서버에 할당할 커스텀 이름이에요. 보통 로그와 메트릭에서 이름으로 서버를 식별하는 데 도움이 돼요. 설정하지 않으면 Caddy가 srvX 패턴을 사용해 동적으로 정의하는데, 여기서 X는 0에서 시작해 구성의 서버 수에 따라 증가해요.

구성에서 사이트 블록이 생성한 서버에만 설정이 적용된다는 것을 명심하세요. 자동 HTTPS가 런타임에 :80 서버(또는 http_port)를 만들므로, 이름을 바꾸려면 적어도 빈 http:// 사이트 블록이 필요해요.

예:

{
	servers :443 {
		name https
	}

	servers :80 {
		name http
	}
}

example.com {
}

http:// {
}
listener_wrappers

소켓 리스너의 동작을 수정할 수 있는 리스너 래퍼를 구성할 수 있게 해요. 주어진 순서대로 적용돼요.

tls

tls 리스너 래퍼는 TLS 리스너가 리스너 래퍼 체인의 어디에 있어야 하는지 표시하는 no-op 리스너 래퍼예요. 다른 리스너 래퍼가 TLS 핸드셰이크 앞에 배치되어야 할 때만 사용해야 해요.

http_redirect

http_redirect는 처음 몇 바이트를 감지해 TLS 핸드셰이크가 아니라 HTTP 요청임을 확인함으로써, TLS 포트에 HTTP 요청으로 들어오는 연결에 대한 HTTP→HTTPS 리다이렉트를 제공해요. 이것은 브라우저가 스킴을 지정하지 않으면 HTTP를 시도하므로, 비표준 포트(443 외)에서 HTTPS를 서빙할 때 가장 유용해요. tls 리스너 래퍼 앞에 배치해야 해요. 예:

{
	servers {
		listener_wrappers {
			http_redirect
			tls
		}
	}
}
proxy_protocol

proxy_protocol 리스너 래퍼(v2.7.0 이전에는 플러그인으로만 사용 가능)는 PROXY 프로토콜 파싱(HAProxy가 유명해짐)을 활성화해요. 연결 시작 시 평문 데이터를 파싱하므로 tls 리스너 래퍼 앞에 사용해야 해요:

PROXY 프로토콜의 메타데이터가 매처나 trusted_proxies 평가 전에 연결에 적용될 수 있다는 점에 유의하세요. 직접 피어의 IP 주소는 추가 평가를 위해 손실돼요.

proxy_protocol {
	timeout <duration>
	allow <cidrs...>
	deny <cidrs...>
	fallback_policy <policy>
}

timeout은 PROXY 헤더를 기다릴 최대 기간을 지정해요. 기본 5s.

allow는 PROXY 헤더를 받을 신뢰된 소스의 CIDR 범위 목록이에요. Unix 소켓은 기본적으로 신뢰되며 이 옵션의 일부가 아니에요.

deny는 PROXY 헤더를 거부할 신뢰된 소스의 CIDR 범위 목록이에요.

fallback_policy는 PROXY 헤더가 allow/deny 목록 중 어느 것에도 없는 주소에서 올 때 취할 조치예요. 기본 폴백 정책은 ignore예요. fallback_policy의 허용 값:

  • ignore: PROXY 헤더의 주소는 무시하지만 연결은 수락

  • use: PROXY 헤더의 주소 사용

  • reject: PROXY 헤더가 보내지면 연결 거부

  • require: 연결이 PROXY 헤더를 보내도록 요구, 없으면 거부

  • skip: PROXY 헤더를 요구하지 않고 연결 수락.

예를 들어 특정 IP 범위의 PROXY 헤더를 받고 다른 범위의 PROXY 헤더는 거부하며, 2초 타임아웃이 있는 HTTPS 서버(tls 리스너 래퍼 필요):

{
	servers {
		listener_wrappers {
			proxy_protocol {
				timeout 2s
				allow 192.168.86.1/24 192.168.86.1/24
				deny 10.0.0.0/8
				fallback_policy reject
			}
			tls
		}
	}
}
timeouts

read_body는 클라이언트 업로드에서 읽는 것을 허용하는 시간을 설정하는 기간 값이에요. 전체 업로드에 대한 하드 한도이므로 짧은 값은 정당하게 느린 클라이언트에 영향을 줄 수 있어요. slowloris 공격을 완화하려면 read_body_idle을 선호하세요. 둘 다 설정되면 read_body는 read_body_idle이 마감을 연장할 수 있는 한도를 정해요. 기본은 타임아웃 없음.

read_body_idle은 클라이언트 업로드에서 읽는 것이 연결이 중단되기 전에 얼마나 오래 정체될 수 있는지 설정하는 기간 값이에요. 매 성공적인 읽기 후 마감이 재설정되므로 느린 클라이언트의 대용량 업로드는 계속 데이터를 보내는 한 영향을 받지 않아요. 기본 1m. 비활성화하려면 음수 값(예: -1s)으로 설정하세요.

선택적 **<min_rate>**는 클라이언트가 유지해야 하는 초당 바이트 수로, 요청 본문 읽기 시작부터 평균화돼요. 이 값이 있으면 마감이 단순히 매 읽기 후 재설정되지 않고, 클라이언트에게 유휴 기간 더하기 지금까지 받은 바이트를 min_rate로 보내는 데 걸리는 시간이 허용돼요. 이는 또한 결코 정체되지 않을 만큼 충분한 데이터만 보내는 클라이언트를 막아요. 기본적으로 최소 비율은 시행되지 않아요.

read_header는 클라이언트의 요청 헤더에서 읽는 것을 허용하는 시간을 설정하는 기간 값이에요. 기본 1m.

write는 클라이언트에 쓰는 것을 허용하는 시간을 설정하는 기간 값이에요. 전체 응답에 대한 하드 한도이므로 큰 파일을 서빙할 때 이 값을 작게 설정하면 정당하게 느린 클라이언트에 부정적인 영향을 줄 수 있어요. 둘 다 설정되면 write는 write_idle이 마감을 연장할 수 있는 한도를 정해요. 기본은 타임아웃 없음.

write_idle은 클라이언트에 쓰는 것이 연결이 중단되기 전에 얼마나 오래 정체될 수 있는지 설정하는 기간 값이에요. 매 쓰기 전 마감이 재설정되므로 크거나 스트리밍되는 응답, 그리고 쓰기 사이에 멈추는 응답(서버 전송 이벤트 같은)은 각 쓰기가 진행되는 한 영향을 받지 않아요. 기본 1m. 비활성화하려면 음수 값(예: -1s)으로 설정하세요.

선택적 **<min_rate>**는 read_body_idle 것과 같지만 클라이언트에 대한 쓰기용이에요. 비율이 응답 시작부터 평균화되므로 쓰기 사이의 멈춤이 이에 포함되니, 장기 스트리밍 응답에는 피하세요.

write_max_chunk는 클라이언트에 대한 단일 밑바닥 쓰기가 커버할 수 있는 최대 바이트 수예요. 그래서 write_idle이 큰 응답의 하나의 큰 쓰기 전체에 적용되는 것이 아니라 큰 응답의 청크 사이에 적용돼요. go-humanize가 지원하는 모든 형식을 받아요. write_idle이 활성화된 경우에만 효과가 있어요. 기본 64KiB.

idle은 keep-alive가 활성화될 때 다음 요청을 기다릴 최대 시간을 설정하는 기간 값이에요. 리소스 고갈을 피하기 위해 기본 5분으로 도움이 돼요.

일부 요청에만 유휴 읽기/쓰기 타임아웃을 설정하려면 timeouts지시문을 참고하세요.

{
	servers {
		timeouts {
			read_body      5m
			read_body_idle 30s
			read_header    5s
			write          10m
			write_idle     30s 1024
			idle           10m
		}
	}
}
keepalive_interval

다른 데이터가 전송되지 않을 때 TCP 레이어에서 연결을 유지하기 위해 TCP keepalive 패킷을 보내는 간격이에요. 기본 15s.

{
	servers {
		keepalive_interval 30s
	}
}
keepalive_idle

다른 데이터가 전송되지 않을 때 TCP keepalive 패킷을 보내기 전에 연결이 유휴 상태여야 하는 기간이에요. 기본 15s.

{
	servers {
		keepalive_idle 1m
	}
}
keepalive_count

연결이 죽은 것으로 간주되기 전에 보낼 최대 TCP keepalive 패킷 수예요. 기본 9.

{
	servers {
		keepalive_count 5
	}
}
0rtt

기본적으로 0-RTT(초기 데이터)는 QUIC 리스너(즉 HTTP/3)에 대해 활성화되어 클라이언트가 TLS 핸드셰이크의 첫 번째 왕복에서 데이터를 보낼 수 있게 하며, 이는 반복 연결의 성능을 향상시킬 수 있어요.

QUIC 리스너의 0-RTT를 비활성화하려면 off로 설정할 수 있어요. 0-RTT를 비활성화하는 한 가지 이유는 remote_ip매처를 사용할 때인데, 이는 TLS 핸드셰이크가 완료되기 전에 라우팅이 발생하면 원격 주소가 검증되는 것에 의존하게 만들어요. 그 경우 HTTP 425 응답이 기록되지만 일부 클라이언트(브라우저)가 잘못 동작해 재시도를 수행하지 않을 수 있어서, 0-RTT를 비활성화하면 425 응답이 사용자에게 보이지 않도록 보장하면서 0-RTT의 성능 이점은 잃을 수 있어요.

{
	servers {
		0rtt off
	}
}
trusted_proxies

요청이 신뢰되어야 하는 프록시 서버의 IP 범위(CIDR)를 구성할 수 있게 해요. 기본적으로 신뢰된 프록시는 없어요.

이것을 활성화하면 신뢰된 요청은 HTTP 헤더에서(기본적으로 X-Forwarded-For; 다른 헤더를 구성하려면 client_ip_headers 참고) 실제 클라이언트 IP를 파싱하게 돼요. 신뢰되면 클라이언트 IP가 액세스 로그에 추가되고 {client_ip} 플레이스홀더로 사용 가능하며 client_ip매처를 사용할 수 있게 해요. 요청이 신뢰된 프록시에서 온 것이 아니면 클라이언트 IP는 직접 들어오는 연결의 리모트 IP 주소나 사용된다면 PROXY 프로토콜로 설정된 주소로 설정돼요. 기본적으로 헤더의 IP는 왼쪽에서 오른쪽으로 파싱돼요. 이 동작을 바꾸려면 trusted_proxies_strict를 참고하세요.

일부 매처나 핸들러는 요청의 신뢰 상태를 사용해 결정을 내릴 수 있어요. 예를 들어 신뢰되면 reverse_proxy 핸들러는 민감한 X-Forwarded-* 요청 헤더를 프록시하고 보강해요.

현재 Caddy 표준 배포에는 static IP 소스 모듈만 포함되지만, IP 범위의 동적 목록을 유지하도록 플러그인으로 확장될 수 있어요.

static

신뢰할 IP 범위(CIDR)의 정적(변하지 않는) 목록을 받아요.

지름길로 private_ranges를 사용해 모든 사설 IPv4 및 IPv6 범위를 일치시킬 수 있어요. 다음 모든 범위를 지정하는 것과 같아요: 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8 127.0.0.1/8 fd00::/8 ::1.

구문:

trusted_proxies static [private_ranges] <ranges...>

IPv4 범위와 IPv6 범위를 신뢰하는 완전한 예:

{
	servers {
		trusted_proxies static 12.34.56.0/24 1200:ab00::/32
	}
}
trusted_proxies_strict

trusted_proxies가 활성화되면 헤더(client_ip_headers로 구성)의 IP는 기본적으로 왼쪽에서 오른쪽으로 파싱돼요. 발견된 첫 번째 신뢰되지 않은 IP 주소가 실제 클라이언트 주소가 돼요. v2.8부터 trusted_proxies_strict로 이 헤더의 오른쪽에서 왼쪽 파싱을 선택할 수 있어요. 기본적으로 이 옵션은 이전 버전과의 호환성을 위해 비활성화돼 있어요.

HAProxy, CloudFlare, AWS ALB, CloudFront 등과 같은 다운스트림 프록시는 각 새로 연결되는 리모트 주소를 X-Forwarded-For의 오른쪽에 추가해요. 가장 왼쪽 IP 주소는 클라이언트가 스푸핑할 수 있으므로, 이들과 함께 작업할 때는 trusted_proxies_strict를 활성화하는 것이 권장돼요.

{
	servers {
		trusted_proxies static private_ranges
		trusted_proxies_strict
	}
}

특히 AWS ALB의 경우 이 옵션을 활성화하고 싶을 확실한 경우예요. 그들의 문서에 따르면 XFF 모드를 append로 설정해야만 실제 클라이언트 IP를 식별할 수 있어요. 이 IP는 X-Forwarded-For의 오른쪽에 추가되며 trusted_proxies_strict로만 안전하게 추출할 수 있어요.

trusted_proxies_unix

trusted_proxies_unix 옵션은 Unix 소켓에서 오는 모든 연결을 신뢰할 수 있게 해줘요. 이는 Caddy가 Unix 소켓을 통해 연결하는 리버스 프록시(아마도 다른 Caddy 인스턴스) 뒤에 있을 때 유용해요(즉 bind지시문이 unix 소켓으로 설정됨). 기본적으로 비활성화돼 있어요.

{
	servers {
		trusted_proxies_unix
	}
}
client_ip_headers

trusted_proxies와 짝을 이루어, 클라이언트의 IP 주소를 결정하는 데 사용할 헤더를 구성할 수 있게 해요. 기본적으로 X-Forwarded-For만 고려돼요. 여러 헤더 필드를 지정할 수 있고, 이 경우 첫 번째 비어 있지 않은 헤더 값이 사용돼요.

{
	servers {
		trusted_proxies static private_ranges
		client_ip_headers X-Forwarded-For X-Real-IP
	}
}
metrics

메트릭 수집을 활성화해요. 메트릭을 스크래핑하거나 OTLP로 푸시하기 전에 필요해요. 메트릭은 정말 바쁜 서버에서 어느 정도 성능 오버헤드가 있지만, v2.11에서 핸들러별로가 아니라 라우트당 한 번 메트릭을 수집함으로써 크게 개선되었어요.

{
	metrics
}

per_host 옵션을 추가해 메트릭을 메트릭의 호스트 이름으로 레이블링할 수 있어요.

{
	metrics {
		per_host
	}
}

클라이언트가 보낼 수 있는 모든 가능한 호스트를 관찰할 때의 무한 카디널리티 가능성 때문에 Caddy는 구성된 호스트에 대해서만 메트릭을 기록하고 다른 모든 호스트(예: attacker.com)는 "_other" 레이블로 집계돼요. 모든 호스트의 관찰을 강제하고 무한 카디널리티 가능성이 수용 가능한 위험이라면 observe_catchall_hosts를 추가하세요. observe_catchall_hosts를 추가해도 per_host는 활성화되지 않는다는 점을 주의하세요. 하지만 이는 HTTPS 서버에는 자동으로 활성화되고(인증서가 무제한 카디널리티에 대한 일부 보호를 제공하므로), 임의의 Host 헤더로 인한 카디널리티 공격을 방지하기 위해 HTTP 서버에는 기본적으로 비활성화돼 있어요.

{
	metrics {
		per_host
		observe_catchall_hosts
	}
}

otlp 옵션을 추가해 같은 메트릭을 OpenTelemetry Protocol(OTLP) 엔드포인트로 푸시할 수 있어요. 내보내기는 OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_METRICS_ENDPOINT, OTEL_EXPORTER_OTLP_PROTOCOL, OTEL_EXPORTER_OTLP_HEADERS, OTEL_METRIC_EXPORT_INTERVAL, OTEL_METRICS_EXPORTER 같은 표준 OpenTelemetry OTEL_* 환경 변수로 구성돼요.

{
	metrics {
		otlp
	}
}

예:

$ OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4318 \
	OTEL_METRICS_EXPORTER=otlp \
	caddy run

자세한 내용은 메트릭으로 Caddy 모니터링하기를 참고하세요.

trace

호출되는 각 개별 핸들러를 로그해요. 로그가 DEBUG 레벨로 출력되어야 해요(debug전역 옵션으로 그렇게 할 수 있어요).

참고: 이는 HTTP 핸들러 모듈의 구성을 로그할 수 있어요. 구성에 민감한 데이터가 있을 때는 안전하지 않은 컨텍스트에서 활성화하지 마세요.

⚠️ 실험적 기능이에요. 변경되거나 제거될 수 있어요.

{
	servers {
		trace
	}
}
max_header_size

클라이언트의 HTTP 요청 헤더에서 파싱할 최대 크기예요. 한도를 초과하면 서버는 HTTP 상태 431 Request Header Fields Too Large로 응답해요. go-humanize가 지원하는 모든 형식을 받아요. 기본적으로 한도는 16KiB예요.

{
	servers {
		max_header_size 64KiB
	}
}
enable_full_duplex

HTTP/1 요청에 대한 전이중(full-duplex) 통신을 활성화해요.

HTTP/1 요청의 경우 Go HTTP 서버는 기본적으로 응답 쓰기를 시작하기 전에 요청 본문의 읽지 않은 부분을 소비해, 핸들러가 요청을 읽고 응답을 쓰는 것을 동시에 하는 것을 방지해요. 이 옵션을 활성화하면 이 동작을 비활성화하고 핸들러가 응답을 동시에 쓰면서 요청을 계속 읽을 수 있게 해요.

HTTP/2+ 요청의 경우 Go HTTP 서버는 항상 동시 읽기와 응답을 허용하므로 이 옵션은 효과가 없어요.

일부 오래된 클라이언트는 전이중 HTTP/1을 지원하지 않아 교착 상태에 빠질 수 있으므로 HTTP 클라이언트와 철저히 테스트하세요. 자세한 내용은 golang/go#57786을 참고하세요.

⚠️ 실험적 기능이에요. 변경되거나 제거될 수 있어요.

{
	servers {
		enable_full_duplex
	}
}
expected_underscore_headers

기본적으로 Caddy는 이름에 밑줄(_)이 포함된 들어오는 요청 헤더를 버려요. CGI, FastCGI, PHP 백엔드는 헤더를 변수로 바꿀 때 하이픈을 밑줄로 변환하므로, X_Remote_User 같은 헤더는 forward_auth가 설정한 것 같은 합법적인 X-Remote-User 헤더와 충돌할 수 있어요.

이 옵션은 버리는 대신 유지할 밑줄이 포함된 헤더 이름 목록이에요. 항목은 대소문자를 구분하지 않고, 끝 *는 그 접두사로 시작하는 모든 헤더와 일치해요(예: webhook_*). 각 항목은 밑줄을 포함해야 해요.

이렇게 유지된 헤더가 있으면 그 하이픈 변형(예: X_Custom_Header에 대한 X-Custom-Header)이 대신 버려져 둘이 혼동되지 않아요. 유지된 헤더가 두 번 이상 보내지면 그 모든 값이 버려져요. 밑줄과 점이 모두 포함된 헤더 이름은 정확히 나열된 경우에만 유지되고 접두사로는 유지되지 않아요.

⚠️ 실험적 기능이에요. 변경되거나 제거될 수 있어요.

{
	servers {
		expected_underscore_headers X_Custom_Header webhook_*
	}
}
expected_dot_headers

기본적으로 Caddy는 이름에 점(.)이 포함된 들어오는 요청 헤더를 버려요. PHP는 $_SERVER에 헤더를 등록할 때 점을 밑줄로 변환하므로 X.Remote.User 같은 헤더가 합법적인 X-Remote-User 헤더와 충돌할 수 있기 때문이에요.

이 옵션은 버리는 대신 유지할 점이 포함된 헤더 이름 목록이에요. expected_underscore_headers와 같은 방식으로 작동해요. 각 항목은 점을 포함해야 해요.

점이 있는 헤더는 PHP, CGI, FastCGI 스타일 백엔드에만 모호해요. 다른 백엔드는 이를 일반 헤더 이름으로 취급해요. 그런 백엔드를 사용한다면 같은 헤더 이름의 점 표기와 밑줄 표기를 모두 허용하지 마세요. 백엔드가 어느 값을 볼 수 있기 때문이에요.

⚠️ 실험적 기능이에요. 변경되거나 제거될 수 있어요.

{
	servers {
		expected_dot_headers X.Custom.Header webhook.*
	}
}
log_credentials

기본적으로 잠재적으로 민감한 정보(Cookie, Set-Cookie, Authorization, Proxy-Authorization)가 포함된 헤더가 있는 액세스 로그(log지시문으로 활성화)는 REDACTED로 기록돼요.

이 헤더들을 삭제하지 않으려면 log_credentials 옵션을 활성화하면 돼요.

{
	servers {
		log_credentials
	}
}
protocols

지원할 HTTP 프로토콜의 공백으로 구분된 목록이에요.

기본: h1 h2 h3

허용 값:

  • h1: HTTP/1.1

  • h2: HTTP/2

  • h2c: 클리어텍스트 위의 HTTP/2

  • h3: HTTP/3

현재 HTTP/2(H2C 포함)를 활성화하면 필연적으로 HTTP/1.1도 활성화되는데, Go 표준 라이브러리가 그 HTTP 서버를 사용할 때 HTTP/1.1을 비활성화하도록 허용하지 않기 때문이에요. 하지만 HTTP/1.1이나 HTTP/3은 독립적으로 활성화할 수 있어요.

H2C("클리어텍스트 HTTP/2" 또는 "H2 over TCP")와 HTTP/3은 Go 표준 라이브러리로 구현되지 않아 일부 기능이 제한될 수 있음을 주의하세요. 애플리케이션에 절대적으로 필요하지 않다면 H2C 활성화를 권장하지 않아요.

{
	servers :80 {
		protocols h1 h2c
	}
}
strict_sni_host

활성화하면 요청의 Host 헤더가 클라이언트의 TLS ClientHello가 보낸 ServerName 값과 일치해야 해요. TLS 클라이언트 인증을 사용할 때 필요한 보호 장치예요. 불일치하면 HTTP 상태 421 Misdirected Request 응답이 클라이언트에 기록돼요.

클라이언트 인증이 구성되면 이 옵션이 자동으로 켜져요. 이는 TLS 핸드셰이크 중에 보호되지 않은 SNI 값을 보낸 다음 연결 설정 후 Host 헤더에 보호된 도메인을 넣어 악용할 수 있는 TLS 클라이언트 인증 우회(도메인 프론팅)를 허용하지 않아요. 이 동작은 안전한 기본값이지만, 예를 들어 도메인 프론팅이 바람직하고 호스트 이름으로 접근이 제한되지 않는 프록시를 실행하는 경우 insecure_off로 명시적으로 끌 수 있어요.

{
	servers {
		strict_sni_host on
	}
}

파일 시스템 (File Systems)

filesystem 전역 옵션은 파일 I/O에 사용할 수 있는 하나 이상의 파일 시스템을 선언할 수 있게 해요.

이것은 클라우드에서 실행 중인 원격 파일 시스템, 파일과 비슷한 인터페이스를 가진 데이터베이스, 심지어 Caddy 바이너리 내에 내장된 파일에서 읽는 것까지 연결할 수 있게 해줘요.

파일 시스템은 식별할 이름으로 선언돼요. 필요하다면 같은 타입의 파일 시스템을 둘 이상 연결할 수 있다는 뜻이에요.

기본적으로 Caddy는 파일 시스템 모듈이 없으므로 사용하려는 파일 시스템용 플러그인으로 Caddy를 빌드해야 해요.

예제

가상의 custom 파일 시스템 모듈을 사용해 두 개의 파일 시스템을 선언할 수 있어요:

{
	filesystem foo custom {
		...
	}

	filesystem bar custom {
		...
	}
}

foo.example.com {
	fs foo
	file_server
}

foo.example.com {
	fs bar
	file_server
}

PKI 옵션 (PKI Options)

PKI(공개 키 인프라) 앱은 Caddy의 로컬 HTTPS 및 ACME 서버 기능의 기반이에요. 이 앱은 인증서를 서명할 수 있는 인증 기관(CA)을 정의해요.

기본 CA ID는 local이에요. ca 구성에서 ID를 생략하면 local이 가정돼요.

name

인증 기관의 사용자 접근 이름이에요.

기본: Caddy Local Authority

{
	pki {
		ca local {
			name "My Local CA"
		}
	}
}
root_cn

루트 인증서의 CommonName 필드에 넣을 이름이에요.

기본: {pki.ca.name} - {time.now.year} ECC Root

{
	pki {
		ca local {
			root_cn "My Local CA - 2024 ECC Root"
		}
	}
}
intermediate_cn

중간 인증서의 CommonName 필드에 넣을 이름이에요.

기본: {pki.ca.name} - ECC Intermediate

{
	pki {
		ca local {
			intermediate_cn "My Local CA - ECC Intermediate"
		}
	}
}
intermediate_lifetime

중간 인증서가 유효한 기간이에요. 이 값 반드시 루트 인증서의 수명(3600d 또는 10년)보다 작아야 해요.

기본: 7d. 절대적으로 필요하지 않으면 바꾸지 않는 것을 권장해요. 발급된 인증서의 수명을 높이는 것이 그러한 경우 중 하나예요. 자세한 내용은 acme_server구문 문서의 lifetime을 확인하세요.

{
	pki {
		ca local {
			intermediate_lifetime 30d
		}
	}
}
maintenance_interval

중간(및 해당 시 루트) 인증서가 갱신이 필요한지 확인하는 빈도의 기간이에요.

기본: 10m. 절대적으로 필요하지 않으면 바꾸지 않는 것을 권장해요.

{
	pki {
		ca local {
			maintenance_interval 30m
		}
	}
}
renewal_window_ratio

Caddy가 인증서 갱신을 시도하기 전에 남아 있어야 하는 인증서 수명의 비율(0과 1 사이)이에요. 예를 들어 인증서 수명이 1년이고 이 비율이 0.2(기본값)이면 Caddy는 만료까지 73일 이하 남았을 때 계속 갱신을 시도해요.

{
	pki {
		ca local {
			renewal_window_ratio 0.1
		}
	}
}
root

CA의 루트로 사용할 키 쌍(인증서와 개인 키)이에요. 지정하지 않으면 자동으로 생성되고 관리돼요.

  • format은 인증서와 개인 키가 제공되는 형식이에요. 현재 기본값인 pem_file만 지원되므로 이 필드는 선택적이에요.

  • cert는 인증서예요. pem_file 형식을 사용할 때 PEM 파일의 경로여야 해요.

  • key는 개인 키예요. pem_file 형식을 사용할 때 PEM 파일의 경로여야 해요. 서명된 중간 인증서를 제공하고 키를 제공하고 싶지 않거나 제공할 수 없는 경우(예: 하드웨어 키에 저장되어 있음) 생략할 수 있어요. 따라서 이 필드는 선택적이에요.

intermediate

CA의 중간 인증서로 사용할 키 쌍(인증서와 개인 키)이에요. 지정하지 않으면 자동으로 생성되고 관리돼요.

  • format은 인증서와 개인 키가 제공되는 형식이에요. 현재 기본값인 pem_file만 지원되므로 이 필드는 선택적이에요.

  • cert는 인증서예요. pem_file 형식을 사용할 때 PEM 파일의 경로여야 해요.

  • key는 개인 키예요. pem_file 형식을 사용할 때 PEM 파일의 경로여야 해요.

{
	pki {
		ca local {
			root {
				format pem_file
				cert /path/to/root.pem
				key /path/to/root.key
			}
			intermediate {
				format pem_file
				cert /path/to/intermediate.pem
				key /path/to/intermediate.key
			}
		}
	}
}

루트 개인 키를 오프라인으로 유지하면서 커스텀 중간 인증서로 사이트 인증서를 서명하려면 중간 인증서/키를 평소처럼 로드하고 체인 구성을 위해 루트 인증서만 제공하세요. Caddyfile은 여전히 root 아래에 key 경로를 기대해요. 루트 키가 없을 때는 Caddy가 읽을 수 있는 아무 PEM 파일을 가리키세요(운영자는 종종 중간 키 경로를 대용으로 재사용해요). 중간 키 쌍이 구성되면 Caddy는 서명에 그 루트 키를 사용하지 않아요.

자동 관리되는 로컬 CA와 충돌하지 않도록 기본이 아닌 CA ID(local 아님)를 선호하고 사이트 블록에서 그것을 선택하세요:

{
	pki {
		ca company {
			root {
				format pem_file
				cert /var/certs/root-ca.crt
				key /var/certs/sub-ca.key
			}
			intermediate {
				format pem_file
				cert /var/certs/sub-ca.crt
				key /var/certs/sub-ca.key
			}
		}
	}
}

my.example {
	tls {
		issuer internal {
			ca company
		}
	}
}

이벤트 옵션 (Event Options)

Caddy 모듈은 흥미로운 일이 발생할 때(또는 발생하려 할 때) 이벤트를 내보내요.

이벤트는 보통 메타데이터 페이로드를 포함해요. 이벤트와 그 페이로드에 대해 배우는 가장 좋은 방법은 각 모듈의 문서에서지만, debug전역 옵션을 활성화하고 로그를 읽어 이벤트와 데이터 페이로드를 볼 수도 있어요.

on

명명된 이벤트에 이벤트 핸들러를 바인딩해요. 이벤트 핸들러 모듈의 이름 뒤에 그 구성을 지정하세요.

예를 들어 인증서를 얻은 후 명령을 실행하려면(3rd-party 플러그인 필요), 이벤트 페이로드의 일부를 플레이스홀더로 스크립트에 전달합니다:

{
	events {
		on cert_obtained exec ./my-script.sh {event.data.certificate_path}
	}
}

이벤트 (Events)

Caddy가 내보내는 표준 이벤트:

플러그인도 이벤트를 내보낼 수 있으므로 자세한 내용은 그 문서를 확인하세요.

더 알아보기 (Learn more)