Struktur Caddyfile #
Caddyfile adalah bahasa konfigurasi yang diciptakan khusus untuk Caddy. Ia dirancang dengan satu tujuan utama: memungkinkan konfigurasi web server yang lengkap dan benar bisa ditulis dalam beberapa baris yang bisa dipahami siapa saja — bahkan developer yang belum pernah melihat Caddyfile sebelumnya. Memahami struktur dasarnya adalah fondasi untuk semua konfigurasi Caddy yang lebih kompleks.
Elemen Dasar Caddyfile #
Caddyfile terdiri dari satu atau lebih blok site. Setiap blok site mendefinisikan bagaimana Caddy merespons request untuk satu atau beberapa alamat.
┌───────────────────────────────────────────────┐
│ Caddyfile │
│ │
│ ┌─────────────────────────────────────────┐ │
│ │ Global Options Block (opsional) │ │
│ │ { │ │
│ │ email [email protected] │ │
│ │ } │ │
│ └─────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────┐ │
│ │ Site Block 1 │ │
│ │ example.com { │ │
│ │ directive1 arg1 arg2 │ │
│ │ directive2 { │ │
│ │ subdirective key value │ │
│ │ } │ │
│ │ } │ │
│ └─────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────┐ │
│ │ Site Block 2 │ │
│ │ api.example.com { │ │
│ │ directive3 │ │
│ │ } │ │
│ └─────────────────────────────────────────┘ │
└───────────────────────────────────────────────┘
Anatomi Satu Blok Site #
example.com { ← Site address (key)
root * /var/www/html ← Directive dengan argumen
encode { ← Directive dengan blok subdirective
gzip ← Subdirective
zstd ← Subdirective
}
file_server ← Directive tanpa argumen
}
Token dan Cara Caddy Membaca Caddyfile #
Caddy mem-parse Caddyfile dengan cara yang mirip dengan parser shell. Teks dipecah menjadi token berdasarkan spasi dan baris baru. Memahami tokenisasi ini penting untuk menghindari kesalahan sintaks yang membingungkan.
Quoting untuk Nilai yang Mengandung Spasi #
# Token tunggal — tidak perlu quotes
reverse_proxy localhost:3000
# ANTI-PATTERN: Nilai dengan spasi tanpa quotes — akan salah di-parse
# header X-Custom-Header nilai dengan spasi
# Ini akan membuat Caddy membaca 4 token terpisah yang tidak valid
# BENAR: Gunakan quotes untuk nilai yang mengandung spasi
header X-Custom-Header "nilai dengan spasi"
# Juga berlaku untuk path yang mengandung spasi
root * "/var/www/my website"
Baris Baru sebagai Separator #
Di Caddyfile, baris baru memiliki arti semantik — ia memisahkan satu directive dari directive berikutnya. Ini berbeda dari beberapa bahasa konfigurasi yang mengabaikan whitespace.
# Ini adalah DUA directive terpisah
root * /var/www/html
file_server
# ANTI-PATTERN: Tidak bisa menulis seperti ini (multiple directives dalam satu baris)
# root * /var/www/html file_server ← Salah! Caddy akan bingung
# Untuk directive panjang, gunakan backslash untuk line continuation
# (jarang diperlukan tapi tersedia)
header X-Very-Long-Header-Name \
"nilai-yang-panjang"
Tanda Kurung Kurawal (Braces) #
# Kurung kurawal pembuka harus di baris yang SAMA dengan directive
# BENAR:
encode {
gzip
zstd
}
# ANTI-PATTERN: Kurung kurawal di baris baru — TIDAK VALID di Caddyfile
# encode
# {
# gzip
# }
Komentar #
Caddyfile mendukung dua jenis komentar:
# Ini adalah komentar satu baris
# Semua teks setelah '#' di baris ini diabaikan
example.com {
# Komentar bisa ada di mana saja, termasuk di dalam blok
root * /var/www/html # Komentar di akhir baris juga valid
# Block comment tidak ada di Caddyfile
# Untuk comment panjang, gunakan multiple '#'
# yang masing-masing di baris terpisah
file_server
}
Satu File, Banyak Site #
Satu Caddyfile bisa mengandung banyak blok site. Semua blok site aktif secara bersamaan:
{
email [email protected]
}
# Site pertama — website utama
example.com {
root * /var/www/html
file_server
}
# Site kedua — API
api.example.com {
reverse_proxy localhost:8080
}
# Site ketiga — redirect www ke non-www
www.example.com {
redir https://example.com{uri} permanent
}
# Site keempat — admin panel dengan akses terbatas
admin.example.com {
@internal {
remote_ip 10.0.0.0/8
}
respond @internal "Unauthorized" 403
reverse_proxy localhost:9000
}
Blok Site dengan Multiple Address #
Satu blok site bisa berlaku untuk beberapa alamat sekaligus. Ada dua cara:
# Cara 1: Comma-separated dalam satu deklarasi
example.com, www.example.com, api.example.com {
reverse_proxy localhost:3000
}
# Cara 2: Setiap address di baris terpisah (lebih mudah dibaca)
example.com
www.example.com
api.example.com {
reverse_proxy localhost:3000
}
Kedua cara menghasilkan konfigurasi yang identik — pilih sesuai preferensi keterbacaan.
Urutan Eksekusi Directive #
Ini adalah salah satu aspek Caddyfile yang paling sering menjadi sumber kebingungan: directive dieksekusi dalam urutan yang sudah ditentukan secara internal oleh Caddy, bukan berdasarkan urutan penulisan di Caddyfile.
# Penulisan ini — urutan di Caddyfile
example.com {
file_server # Ditulis pertama
encode gzip # Ditulis kedua
basicauth /secret/* { # Ditulis ketiga
user $2a$14$...
}
root * /var/www/html # Ditulis keempat
}
# Urutan eksekusi SEBENARNYA (ditentukan Caddy secara internal):
# 1. root (urutan 3 dari atas secara eksekusi)
# 2. basicauth (urutan 2 dari atas secara eksekusi)
# 3. encode (urutan 3 dari atas secara eksekusi)
# 4. file_server (urutan 4 dari atas secara eksekusi, terakhir karena terminal handler)
Urutan eksekusi lengkap yang digunakan Caddy (dari paling awal ke paling akhir):
Urutan Eksekusi Handler Caddy:
1. tracing
2. map
3. root
4. vars
5. rewrite
6. uri
7. try_files
8. basicauth
9. forward_auth
10. request_header
11. rate_limit (plugin)
12. encode
13. push
14. header
15. copy_response_headers
16. respond
17. metrics
18. reverse_proxy
19. file_server
20. php_fastcgi
21. templates
22. abort
Implikasi praktis: kamu tidak perlu khawatir tentang urutan penulisan directive di Caddyfile untuk memastikan sesuatu berjalan sebelum yang lain — Caddy sudah mengurus ini. Kecuali dalam satu kondisi: blok route yang memungkinkan kamu mengontrol urutan secara eksplisit.
example.com {
# Dalam blok route, urutan penulisan MENENTUKAN urutan eksekusi
# Gunakan ini jika kamu perlu override urutan default
route {
basicauth /admin/* {
admin $2a$14$...
}
reverse_proxy /admin/* localhost:9000
reverse_proxy localhost:3000
}
}
Variabel Environment di Caddyfile #
Caddy mendukung substitusi variabel environment langsung di Caddyfile menggunakan sintaks {env.NAMA_VAR}:
{
email {env.ADMIN_EMAIL}
}
{env.DOMAIN} {
reverse_proxy {env.BACKEND_HOST}:{env.BACKEND_PORT}
tls {
dns cloudflare {env.CLOUDFLARE_API_TOKEN}
}
}
Ini sangat berguna untuk:
- Menyimpan secret (API keys, password) di environment variable bukan di Caddyfile
- Membuat Caddyfile yang sama bisa digunakan di berbagai environment (dev/staging/prod)
- Integrasi dengan Docker, Kubernetes, atau sistem manajemen secret lainnya
Placeholder — Variabel Runtime #
Caddy juga mendukung placeholder — variabel yang nilainya diketahui pada saat request diproses (runtime), bukan saat konfigurasi dibuat. Placeholder menggunakan format {nama.placeholder}:
example.com {
# Placeholder yang sering digunakan:
# {http.request.host} → Host dari request
# {http.request.uri} → URI lengkap
# {http.request.method} → HTTP method (GET, POST, dll)
# {http.request.remote.ip} → IP address client
# {http.request.header.*} → Nilai header tertentu
# {http.response.status} → Status code response
header X-Request-ID "req-{http.request.uuid}"
log {
format json
# Output: IP client, method, URI, dll
}
# Redirect dengan mempertahankan path
redir https://new-domain.com{http.request.uri}
}
Menjalankan dan Memvalidasi Caddyfile #
# Validasi syntax Caddyfile tanpa menjalankan server
caddy validate --config /etc/caddy/Caddyfile
# Lihat JSON yang dihasilkan dari Caddyfile
# Berguna untuk memahami apa yang sebenarnya dikonfigurasi
caddy adapt --config /etc/caddy/Caddyfile --adapter caddyfile
# Format/pretty-print Caddyfile
caddy fmt /etc/caddy/Caddyfile
# Format dan langsung overwrite file
caddy fmt --overwrite /etc/caddy/Caddyfile
# Jalankan Caddy dengan Caddyfile tertentu
caddy run --config /etc/caddy/Caddyfile
# Jalankan dengan reload otomatis saat file berubah (development)
caddy run --config /etc/caddy/Caddyfile --watch
caddy fmt — Formatter Bawaan
#
Caddy memiliki formatter bawaan yang merapikan penulisan Caddyfile ke format standar. Ini sangat berguna untuk konsistensi tim:
# Sebelum caddy fmt (penulisan tidak konsisten)
example.com{
root * /var/www/html
encode gzip
file_server
}
# Setelah caddy fmt (format standar)
example.com {
root * /var/www/html
encode gzip
file_server
}
Caddy menggunakan tab (bukan spasi) untuk indentasi secara default.
Hubungan Caddyfile dan JSON Native #
Memahami bahwa Caddyfile hanyalah representasi tingkat tinggi dari konfigurasi JSON native Caddy membantu menjelaskan banyak hal:
# Lihat JSON yang dihasilkan dari Caddyfile sederhana ini:
cat << 'EOF' | caddy adapt --adapter caddyfile --config /dev/stdin
example.com {
reverse_proxy localhost:3000
}
EOF
Output JSON (disederhanakan):
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [":443", ":80"],
"routes": [{
"match": [{"host": ["example.com"]}],
"handle": [{
"handler": "subroute",
"routes": [{
"handle": [{
"handler": "reverse_proxy",
"upstreams": [{"dial": "localhost:3000"}]
}]
}]
}],
"terminal": true
}]
}
}
},
"tls": {
"automation": {
"policies": [{"subjects": ["example.com"]}]
}
}
}
}
Beberapa hal yang terlihat jelas dari JSON:
- Caddy otomatis menambahkan listen di
:443dan:80 - TLS automation otomatis dikonfigurasi untuk domain
- Handler dibungkus dalam
subrouteuntuk isolation
Anti-Pattern Umum #
# ANTI-PATTERN 1: Lupa tutup kurung kurawal
example.com {
reverse_proxy localhost:3000
# ← Lupa closing brace — akan error
# ANTI-PATTERN 2: Kurung kurawal di baris baru
example.com
{
reverse_proxy localhost:3000
}
# Caddy akan membaca 'example.com' sebagai site address tanpa blok,
# lalu '{' sebagai site address baru yang tidak valid
# ANTI-PATTERN 3: Spasi antara site address dan kurung kurawal di baris baru
example.com
{
reverse_proxy localhost:3000
}
# Baris kosong antara address dan brace membuat Caddy bingung
# BENAR untuk semua kasus di atas:
example.com {
reverse_proxy localhost:3000
}
Ringkasan #
- Caddyfile terdiri dari blok site — setiap blok mendefinisikan konfigurasi untuk satu atau lebih alamat.
- Kurung kurawal pembuka harus di baris yang sama dengan site address atau directive — tidak boleh di baris terpisah.
- Urutan penulisan directive tidak menentukan urutan eksekusi — Caddy menggunakan urutan internal yang sudah terdefinisi. Gunakan blok
routeuntuk kontrol eksplisit.- Variabel environment (
{env.NAMA}) dan placeholder runtime ({http.request.host}) tersedia untuk nilai dinamis.caddy validatesebelum reload,caddy fmtuntuk format konsisten,caddy adaptuntuk debug dengan melihat JSON output.- Caddyfile adalah abstraksi di atas JSON native — semua Caddyfile dikompilasi ke JSON sebelum dieksekusi oleh Caddy.
← Sebelumnya: Compile dari Source Berikutnya: Site Address →