Struktur Caddyfile

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 :443 dan :80
  • TLS automation otomatis dikonfigurasi untuk domain
  • Handler dibungkus dalam subroute untuk 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 route untuk kontrol eksplisit.
  • Variabel environment ({env.NAMA}) dan placeholder runtime ({http.request.host}) tersedia untuk nilai dinamis.
  • caddy validate sebelum reload, caddy fmt untuk format konsisten, caddy adapt untuk 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 →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact