Snippet & Import #
Ketika kita pertama kali menggunakan Caddy, satu berkas Caddyfile kecil yang berisi beberapa baris konfigurasi sudah cukup untuk melayani website kita. Namun, seiring dengan berkembangnya infrastruktur kita — seperti bertambahnya jumlah subdomain, penerapan kebijakan keamanan (security headers) yang ketat di semua domain, pemisahan log lalu lintas data, dan penanganan autentikasi — berkas Caddyfile kita secara bertahap akan memanjang, menjadi rumit, dan dipenuhi dengan baris-baris kode yang berulang-ulang (duplication).
Dalam dunia rekayasa perangkat lunak, duplikasi kode adalah musuh utama pemeliharaan sistem. Jika kita menyalin 20 baris konfigurasi header keamanan ke 10 blok situs yang berbeda, dan di kemudian hari kita harus memperbarui satu parameter header, kita terpaksa melakukan perubahan di 10 tempat berbeda secara manual. Risiko kelalaian sangatlah tinggi.
Caddy memecahkan tantangan ini dengan menyediakan dua fitur modularitas yang sangat kuat: Snippet dan Import. Bersama-sama, keduanya menerapkan prinsip DRY (Don’t Repeat Yourself) ke dalam Caddyfile, memungkinkan kita membuat konfigurasi yang bersih, terstruktur, mudah dikelola, dan berskala besar (enterprise-ready).
Alur Kerja Kompilasi Multi-File Caddy #
Ketika Caddy membaca berkas Caddyfile utama, ia akan memproses instruksi import secara rekursif dan menyusun semua snippet serta berkas eksternal menjadi satu struktur konfigurasi tunggal di dalam memori sebelum diadaptasi menjadi JSON native.
Untuk memvisualisasikan bagaimana Caddy mengompilasi konfigurasi modular ini, mari perhatikan diagram alur berikut:
flowchart TD
Main["Caddyfile Utama (/etc/caddy/Caddyfile)"] --> LoadGlobal["1. Load Global Options Block"]
Main --> ImportSnippets["2. Import Snippet Modular\n(import snippets/*.caddyfile)"]
Main --> ImportSites["3. Import Konfigurasi Situs\n(import sites/*.caddyfile)"]
subgraph Filesystem["Sistem Berkas Modular"]
direction LR
S1["snippets/security.caddyfile"]
S2["snippets/logging.caddyfile"]
S3["sites/app1.caddyfile"]
S4["sites/app2.caddyfile"]
end
ImportSnippets -.-> S1
ImportSnippets -.-> S2
ImportSites -.-> S3
ImportSites -.-> S4
S1 --> Compile["Engine Compiler Caddy (caddy adapt)"]
S2 --> Compile
S3 --> Compile
S4 --> Compile
Compile --> JSON["JSON Config Utama (Active Memory)"]Apa itu Snippet? #
Snippet adalah blok konfigurasi bernama yang didefinisikan satu kali dan dapat disisipkan (reused) ke dalam blok situs mana pun di dalam Caddyfile.
Sintaks penulisan snippet dideklarasikan di level teratas Caddyfile (di luar blok situs mana pun) dengan membungkus nama snippet menggunakan tanda kurung biasa (nama_snippet). Konten di dalam snippet dapat berisi directive atau logika valid apa pun yang biasanya diletakkan di dalam blok situs.
Mari kita perhatikan perbandingan antara skenario duplikasi (Anti-Pattern) dan penggunaan Snippet (Benar):
# ANTI-PATTERN: Menyalin sekumpulan header keamanan ke setiap domain
domain1.com {
header {
X-Frame-Options "SAMEORIGIN"
X-Content-Type-Options "nosniff"
-Server
}
file_server
}
domain2.com {
header {
X-Frame-Options "SAMEORIGIN"
X-Content-Type-Options "nosniff"
-Server
}
reverse_proxy localhost:3000
}
# ====================================================================
# BENAR: Menggunakan Snippet untuk Mengeliminasi Duplikasi (DRY)
# ====================================================================
# 1. Definisikan Snippet di bagian atas berkas
(security_headers) {
header {
X-Frame-Options "SAMEORIGIN"
X-Content-Type-Options "nosniff"
-Server
}
}
# 2. Impor Snippet ke masing-masing domain menggunakan 'import'
domain1.com {
import security_headers
file_server
}
domain2.com {
import security_headers
reverse_proxy localhost:3000
}
Dengan menggunakan snippet, jika kita ingin mengubah nilai header keamanan atau menambahkan header baru, kita cukup mengeditnya sekali di dalam snippet (security_headers). Perubahan tersebut akan langsung diterapkan ke seluruh blok situs yang mengimpornya.
Mengirimkan Argumen Dinamis ke Snippet #
Sering kali, konfigurasi kita hampir identik, tetapi memiliki satu atau dua nilai parameter yang berbeda. Misalnya, kita ingin struktur konfigurasi log kita sama untuk semua situs, tetapi nama berkas lognya harus mengikuti nama domain masing-masing.
Caddyfile mendukung pengiriman argumen dinamis ke dalam snippet, mirip dengan pengiriman parameter ke dalam sebuah fungsi pada bahasa pemrograman. Kita dapat mengakses nilai argumen tersebut menggunakan sintaksis {args[0]}, {args[1]}, dan seterusnya berdasarkan urutan posisinya saat diimpor.
Berikut adalah contoh implementasinya:
# 1. Definisikan Snippet dengan Placeholder Argumen
(standard_logging) {
log {
# {args[0]} akan digantikan dengan argumen pertama yang dikirimkan
output file /var/log/caddy/{args[0]}.log {
roll_size 50mb
roll_keep 5
}
format json
}
}
(custom_proxy) {
# {args[0]} = alamat backend, {args[1]} = timeout koneksi
reverse_proxy {args[0]} {
transport http {
dial_timeout {args[1]}s
}
}
}
# 2. Gunakan Snippet dengan Mengirimkan Parameter Nilai
site1.com {
import standard_logging "site1-access"
import custom_proxy localhost:3000 5
}
site2.com {
import standard_logging "site2-access"
import custom_proxy localhost:4000 10
}
Penanganan Argumen dengan Spasi atau Tanda Kutip #
Jika kita mengirimkan argumen yang mengandung spasi atau karakter khusus ke dalam snippet, kita wajib membungkus argumen tersebut dengan tanda kutip ganda pada baris import agar parser tidak memecahnya sebagai parameter terpisah:
(custom_header) {
header X-Custom-Message "{args[0]}"
}
example.com {
# Kirim argumen berupa string panjang dengan spasi
import custom_header "Halo dari Caddy Web Server Produksi"
file_server
}
Import Konfigurasi Eksternal (Multi-File) #
Instruksi import tidak hanya terbatas untuk membaca snippet internal yang ditulis di dalam satu berkas yang sama. Kita juga dapat menggunakannya untuk menyertakan konten dari berkas Caddyfile eksternal.
Fitur ini mendukung penggunaan pola wildcard (glob patterns) seperti tanda bintang (*), yang memungkinkan kita memisahkan konfigurasi menjadi puluhan berkas terpisah dan mengimpornya secara otomatis dalam satu baris instruksi.
# Mengimpor berkas konfigurasi situs dari direktori terpisah
import /etc/caddy/sites-enabled/*.caddyfile
# Mengimpor snippet khusus keamanan dari berkas tertentu
import /etc/caddy/snippets/security.conf
Tabel Perbandingan Struktur Modular Caddyfile #
Berikut adalah tabel pembanding antara pendekatan tata letak berkas konfigurasi tunggal, snippet internal, dan modularitas multi-file:
| Kriteria Analitik | Berkas Tunggal Monolitik | Snippet Internal (Satu Berkas) | Struktur Multi-File (Import) |
|---|---|---|---|
| Skala Kompleksitas | Sangat Rendah (1-2 situs) | Menengah (3-5 situs) | Tinggi / Enterprise (>5 situs) |
| Kemudahan Pengaturan | Sangat Mudah | Mudah | Memerlukan struktur folder terencana |
| Tingkat Keterbacaan | Buruk jika baris kode bertambah | Baik (DRY internal) | Sangat Baik (Modular penuh) |
| Kolaborasi Tim (Git) | Sering memicu konflik merge | Konflik merge berkurang | Sangat ramah kolaborasi (satu berkas per situs) |
| Kemudahan Debugging | Mudah (satu lokasi berkas) | Mudah | Memerlukan pelacakan file impor eksternal |
| Rekomendasi Skenario | Uji coba cepat / Sandbox | Server kecil personal | Server produksi perusahaan / Cloud |
Praktik Terbaik Organisasi Multi-File Skala Besar #
Untuk deployment di lingkungan produksi skala besar dengan banyak domain, sangat disarankan untuk menerapkan struktur direktori modular. Hal ini menjaga berkas konfigurasi tetap rapi dan mempermudah tim DevOps dalam mengelola rute situs.
Rekomendasi Struktur Folder #
/etc/caddy/
├── Caddyfile ← Berkas Utama (Entry Point)
├── snippets/
│ ├── security.caddyfile ← Kumpulan snippet header keamanan
│ ├── logging.caddyfile ← Kumpulan snippet konfigurasi log
│ └── cors.caddyfile ← Kumpulan snippet CORS
└── sites-enabled/
├── app1.com.caddyfile ← Konfigurasi situs App 1
├── app2.com.caddyfile ← Konfigurasi situs App 2
└── blog.net.caddyfile ← Konfigurasi situs Blog
Implementasi Berkas Caddyfile Utama
#
Berkas utama bertindak sebagai pusat kendali (orchestrator) yang memuat setelan global dan mengimpor semua modul lainnya:
# 1. Global Options
{
email [email protected]
}
# 2. Impor Semua Snippet Pendukung
import /etc/caddy/snippets/*.caddyfile
# 3. Impor Semua Blok Situs Aktif
import /etc/caddy/sites-enabled/*.caddyfile
Implementasi Berkas /etc/caddy/snippets/security.caddyfile
#
Menyimpan semua template keamanan reusable:
(security_strict) {
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains"
X-Frame-Options "DENY"
X-Content-Type-Options "nosniff"
-Server
}
}
(allow_iframe_sameorigin) {
header {
X-Frame-Options "SAMEORIGIN"
X-Content-Type-Options "nosniff"
-Server
}
}
Implementasi Berkas /etc/caddy/snippets/logging.caddyfile
#
Menyimpan template log modular:
(standard_log) {
log {
output file /var/log/caddy/{args[0]}.log {
roll_size 100mb
roll_keep 7
}
format json
level INFO
}
}
Implementasi Berkas /etc/caddy/snippets/cors.caddyfile
#
Menyimpan template CORS untuk komunikasi lintas domain:
(cors_origin) {
header {
Access-Control-Allow-Origin "{args[0]}"
Access-Control-Allow-Methods "GET, POST, OPTIONS"
Access-Control-Allow-Headers "Content-Type, Authorization"
}
}
Implementasi Berkas /etc/caddy/sites-enabled/app1.com.caddyfile
#
Konfigurasi situs mandiri yang bersih karena memanfaatkan snippet global:
app1.company.com {
# Impor snippet keamanan ketat dari file security.caddyfile
import security_strict
# Impor snippet log kustom
import standard_log "app1-access"
root * /var/www/app1
file_server
}
Implementasi Berkas /etc/caddy/sites-enabled/app2.com.caddyfile
#
Contoh situs API yang memadukan keamanan, log, dan CORS:
api.company.com {
import allow_iframe_sameorigin
import standard_log "api-access"
import cors_origin "https://app1.company.com"
reverse_proxy localhost:8080
}
Import Kondisional Berdasarkan Environment #
Kita dapat menggabungkan instruksi import dengan variabel lingkungan untuk membuat Caddy memuat konfigurasi yang berbeda secara dinamis antara server pengembangan lokal (development) dan server produksi (production).
# Berkas Caddyfile Utama
{
# Membaca email dinamis dari env
email {env.ACME_EMAIL}
}
# Caddy akan mengimpor konfigurasi TLS kustom sesuai variabel environment
# Misalnya jika {env.ENV_TYPE} bernilai 'dev', Caddy akan mencari file 'tls-dev.caddyfile'
# Jika bernilai 'prod', Caddy akan memuat 'tls-prod.caddyfile'
import /etc/caddy/snippets/tls-{env.ENV_TYPE}.caddyfile
example.com {
import tls_config
reverse_proxy localhost:3000
}
Isi berkas /etc/caddy/snippets/tls-dev.caddyfile:
(tls_config) {
# Gunakan CA internal untuk development lokal
tls internal
}
Isi berkas /etc/caddy/snippets/tls-prod.caddyfile:
(tls_config) {
# Gunakan ACME Let's Encrypt standar dengan Cloudflare DNS
tls {
dns cloudflare {env.CLOUDFLARE_TOKEN}
}
}
Perilaku Reload Otomatis (Auto-Reload & Watch) #
Ketika kita menjalankan Caddy di latar depan dengan opsi pengawasan berkas (--watch), Caddy akan memantau perubahan berkas untuk memicu reload otomatis secara real-time tanpa menghentikan lalu lintas server (zero-downtime hot reload).
# Jalankan Caddy dengan fitur pengawasan berkas aktif
caddy run --config /etc/caddy/Caddyfile --watch
[!IMPORTANT] Fitur
--watchdi Caddy sangat cerdas. Caddy tidak hanya memantau berkas entry-point utama/etc/caddy/Caddyfile, tetapi juga akan mendeteksi dan memantau seluruh berkas eksternal yang dimuat melalui direktifimport(termasuk yang menggunakan pola glob). Jika ada perubahan di salah satu file situs di dalam folder/etc/caddy/sites-enabled/, Caddy akan mendeteksi modifikasi tersebut, melakukan validasi internal, dan memicu reload konfigurasi secara instan.
Anti-Pattern yang Harus Dihindari #
Berikut adalah beberapa kesalahan fatal terkait penggunaan snippet dan import yang sering menyebabkan server gagal dijalankan:
1. Mendefinisikan Snippet di Dalam Blok Situs #
Snippet wajib dideklarasikan di level teratas Caddyfile (global scope). Menuliskan definisi snippet di dalam kurung kurawal blok situs akan memicu error pembacaan parser.
# ANTI-PATTERN: Definisi bersarang
example.com {
(my_snippet) { # ← ERROR! Sintaks ini tidak valid di dalam blok situs
header X-Test "value"
}
}
# BENAR: Deklarasikan di luar blok situs terlebih dahulu
(my_snippet) {
header X-Test "value"
}
example.com {
import my_snippet
}
2. Impor Rekursif Tanpa Batas (Recursive Import) #
Hindari situasi di mana Berkas A mengimpor Berkas B, dan Berkas B juga mengimpor Berkas A secara bersamaan. Hal ini akan menyebabkan infinite loop saat Caddy mencoba mengurai konfigurasi dan akhirnya mematikan proses startup dengan status out of memory atau crash stack overflow.
3. Konflik Nama Snippet dengan Directive Bawaan #
Jangan pernah memberikan nama snippet yang sama persis dengan nama directive bawaan Caddy (seperti root, encode, reverse_proxy, dll.). Hal ini akan membuat parser bingung dan menganggap import Anda sebagai pemanggilan directive dengan argumen yang salah.
# ANTI-PATTERN: Nama snippet konflik dengan directive 'encode'
(encode) {
header X-Encoded "true"
}
# BENAR: Berikan nama unik yang deskriptif
(gzip_plus_header) {
encode gzip
header X-Encoded "true"
}
Validasi dan Troubleshooting Konfigurasi Multi-File #
Jika kita menggunakan arsitektur multi-file dengan banyak instruksi import, menelusuri kesalahan sintaksis secara manual bisa menjadi sangat melelahkan. Caddy mempermudah proses ini dengan menyediakan pesan kesalahan yang sangat detail:
# Validasi berkas utama Caddyfile
caddy validate --config /etc/caddy/Caddyfile
Jika terjadi kesalahan di salah satu berkas yang diimpor, Caddy tidak hanya memberi tahu adanya error, tetapi juga akan menyebutkan path absolut dari berkas luar tersebut beserta nomor baris spesifik yang memicu kegagalan parsing.
Kita juga dapat menggunakan perintah adaptasi untuk memeriksa berkas konfigurasi akhir setelah semua import berhasil disisipkan oleh engine Caddy:
caddy adapt --config /etc/caddy/Caddyfile --adapter caddyfile > /tmp/resolved_config.json
Ringkasan #
- Snippet dideklarasikan dengan format nama di dalam tanda kurung
(nama_snippet) { ... }di bagian atas Caddyfile (global scope).- Masukkan argumen posisional (
{args[0]},{args[1]}) untuk membuat snippet berperilaku dinamis layaknya fungsi pemrograman.- Instruksi
importdapat memuat konfigurasi dari snippet internal maupun berkas Caddyfile eksternal.- Gunakan pola wildcard glob (
import /etc/caddy/sites-enabled/*.caddyfile) untuk memuat puluhan konfigurasi situs secara otomatis.- Pisahkan folder
sites-enabled/dansnippets/untuk menjaga kerapian struktur berkas pada server skala produksi.- Jalankan perintah
caddy validatesebelum melakukan reload server untuk memastikan tidak ada kesalahan sintaksis di dalam berkas impor luar.