Templates #
Direktif templates adalah salah satu fitur paling unik dan kuat yang dimiliki oleh server web Caddy. Fitur ini memungkinkan Caddy untuk memproses berkas HTML (atau format berbasis teks lainnya) yang dikirimkan oleh direktif file_server secara dinamis di sisi server sebelum disajikan ke browser klien. Menggunakan mesin template bawaan bahasa Go (text/template dan html/template), kita dapat menyisipkan logika server-side sederhana seperti percabangan kondisi (if/else), perulangan (looping), menyertakan komponen terpisah (partial includes), memformat tanggal, membaca cookie klien, hingga mem-parsing berkas Markdown menjadi HTML secara langsung di tingkat proxy web. Fitur ini sangat ideal bagi kita yang ingin membangun situs web dinamis ringan, halaman landing, atau sistem penanganan kesalahan (error pages) yang interaktif tanpa perlu menyusun infrastruktur aplikasi backend terpisah. Kita akan membahas secara mendalam arsitektur rendering template Caddy, mempelajari sintaks logika template Go, mengakses data request klien, mempraktikkan skenario halaman kesalahan kustom, serta menganalisis aspek keamanan kritis guna mencegah kerentanan Template Injection.
Arsitektur Server-Side Rendering (SSR) di Caddy #
Secara tradisional, server web seperti Nginx bertindak sebagai penyaji berkas statis (static file server) yang kaku. Jika kita ingin menyisipkan data dinamis (misalnya, menampilkan tahun saat ini di bagian footer halaman secara otomatis), kita harus meneruskan permintaan ke server aplikasi backend (seperti PHP-FPM, Node.js, atau Go) atau mengandalkan Javascript di sisi klien (client-side rendering).
Caddy mendobrak batasan ini dengan mengintegrasikan mesin Server-Side Rendering (SSR) langsung di dalam pipeline pemrosesan internalnya. Ketika direktif templates diaktifkan, Caddy akan memeriksa berkas teks yang diminta sebelum dikirimkan ke jaringan. Jika berkas tersebut mengandung sintaks template Go—yang ditandai oleh kurung kurawal ganda {{ ... }}—Caddy akan mem-parsing dan mengeksekusi logika tersebut secara real-time di memori RAM, mengganti sintaks template dengan konten teks dinamis, dan mengirimkan HTML bersih ke browser klien.
Karena evaluasi ini dilakukan di sisi server, browser klien hanya akan menerima dokumen HTML biasa tanpa mengetahui bahwa halaman tersebut dihasilkan secara dinamis. Ini sangat ramah terhadap optimasi mesin pencari (SEO) karena seluruh konten telah selesai dirender sebelum dibaca oleh robot perayap.
Sintaksis dan Logika Go Template di Caddy #
Mesin template Caddy mewarisi seluruh kapabilitas pustaka Go template bawaan yang terbukti sangat aman dan cepat. Berikut adalah beberapa elemen sintaksis dasar yang sering kita gunakan untuk membangun halaman web:
1. Evaluasi Teks dan Variabel Dinamis #
To menampilkan nilai variabel atau placeholder Caddy, kita menuliskan nama variabel di dalam kurung kurawal ganda:
<!-- Menampilkan waktu server saat ini -->
<p>Waktu server sekarang: {{ now | date "Mon, 02 Jan 2006 15:04:05 MST" }}</p>
2. Logika Percabangan Kondisional (If/Else) #
Kita dapat menampilkan bagian HTML tertentu secara selektif berdasarkan evaluasi kondisi:
{{ if .Req.Header.Get "User-Agent" | contains "Mobile" }}
<div class="alert">Kita mendeteksi bahwa Anda mengakses dari perangkat seluler!</div>
{{ else }}
<div class="alert">Selamat datang pengguna desktop.</div>
{{ end }}
3. Perulangan (Looping/Range) #
Kita dapat mengiterasi sekumpulan data—misalnya daftar file di direktori atau array data kustom—menggunakan kata kunci range:
<ul>
<!-- Melakukan iterasi daftar nama file di folder saat ini -->
{{ range .Dir }}
<li><a href="{{ .Name }}">{{ .Name }} ({{ .Size }} byte)</a></li>
{{ end }}
</ul>
4. Menyertakan Berkas Parsial (Include Partial Files) #
Untuk menjaga kode HTML kita tetap rapi dan terorganisir (tidak menuliskan kode header dan footer yang sama berulang kali di setiap file), kita dapat memecahnya menjadi berkas-berkas terpisah dan mengimpornya kembali menggunakan fungsi include:
<!-- file: index.html -->
{{ include "partials/header.html" }}
<main>
<h1>Halaman Utama Kita</h1>
<p>Ini adalah konten utama halaman.</p>
</main>
{{ include "partials/footer.html" }}
Mengakses Data Permintaan Klien (Request Data) #
Salah satu keunggulan terbesar direktif templates Caddy adalah kemampuannya untuk membaca konteks request HTTP klien secara langsung. Caddy membungkus data request ini ke dalam objek-objek khusus yang dapat kita akses dengan mudah di dalam bodi HTML kita:
.Req: Mengakses informasi request HTTP (seperti.Req.Method,.Req.URL.Path,.Req.Header)..Cookie: Fungsi khusus untuk membaca nilai cookie klien (misalnya,{{ cookie "session_id" }})..Env: Fungsi untuk membaca variabel lingkungan sistem operasi (misalnya,{{ env "APP_ENV" }}).
Mari kita perhatikan alur pemrosesan request oleh direktif templates ini pada diagram flowchart berikut:
flowchart TD
A["1. Request HTTP Masuk Klien"] --> B["2. Caddy file_server membaca berkas HTML dari disk"]
B --> C{"3. Apakah direktif 'templates' aktif?"}
C -- "Nu" --> D["4. Sajikan berkas HTML statis asli (200 OK)"]
C -- "Ya" --> E["5. Caddy Engine mem-parsing tag {{ ... }}"]
E --> F["6. Baca data runtime\n(e.g. Cookie, HTTP Header, IP Klien)"]
F --> G["7. Evaluasi logika Go Template di memori RAM\n(e.g. render partial, looping)"]
G --> H["8. Hasilkan dokumen HTML bersih hasil render"]
H --> I["9. Kirim HTML bersih ke browser klien (200 OK)"]Daftar Lengkap Fungsi Template Kustom Caddy #
Selain fungsi bawaan standard Go template, Caddy menyuntikkan puluhan fungsi kustom yang dirancang khusus untuk mempermudah tugas administrasi web kita. Berikut adalah tabel rincian fungsi kustom terpenting di Caddy:
| Nama Fungsi | Contoh Sintaks Penggunaan | Deskripsi Fungsionalitas |
|---|---|---|
include | {{ include "sidebar.html" }} | Membaca dan merender berkas HTML terpisah secara internal. |
markdown | `{{ “# Halo” | markdown }}` |
cookie | {{ cookie "username" }} | Membaca nilai dari cookie klien secara aman. |
env | {{ env "PORT" }} | Mengakses nilai variabel lingkungan sistem operasi server. |
placeholder | {{ placeholder "http.request.uri" }} | Membaca semua placeholder bawaan yang disediakan runtime Caddy. |
now | {{ now }} | Mengambil data waktu server saat ini. |
date | `{{ now | date “2006-01-02” }}` |
uuid | {{ uuid }} | Menghasilkan nilai pengenal unik universal (UUID v4) acak. |
sha256 | `{{ “rahasia” | sha256 }}` |
stripHTML | `{{ “Halo” | stripHTML }}` |
Konfigurasi Mengaktifkan Templates di Caddyfile #
Untuk mengaktifkan fitur templates pada Caddyfile, kita cukup mendeklarasikan direktif templates di dalam blok rute server kita.
# Contoh: Mengaktifkan Templates Secara Global
example.com {
root * /var/www/html
# ✓ BENAR: Aktifkan pemrosesan template untuk seluruh berkas teks (.html, .txt)
templates
file_server
}
Konfigurasi Selektif Menggunakan Named Matcher #
Dalam kasus di mana kita hanya ingin mengaktifkan pemrosesan template pada halaman HTML tertentu guna menghemat performa parsing CPU, kita dapat membatasinya menggunakan named matcher:
# Contoh: Mengaktifkan Templates Hanya untuk Halaman HTML
example.com {
root * /var/www/html
# Buat named matcher untuk mendeteksi berkas berekstensi .html
@html_only path *.html /
# Hanya jalankan mesin template untuk berkas HTML
templates @html_only
file_server
}
Use Case 1: Halaman Kesalahan Kustom yang Dinamis (Custom Error Pages) #
Ketika server kita mengalami masalah—misalnya database backend mati sehingga mengembalikan status 502 Bad Gateway—sangat tidak profesional jika kita menyajikan halaman kesalahan bawaan server yang kaku dan menakutkan bagi pengunjung.
Kita dapat menggunakan kombinasi direktif handle_errors dan templates di Caddy untuk merender halaman kesalahan kustom yang interaktif, lengkap dengan pelacakan UUID unik (Trace ID) untuk mempermudah proses debugging kita:
1. Penulisan Caddyfile Penanganan Error #
# Contoh: Sistem Penanganan Error Terpusat
example.com {
# 1. Rute normal
handle {
reverse_proxy backend:8080
}
# 2. Tangani kesalahan HTTP dari backend / Caddy
handle_errors {
# Arahkan pemrosesan ke berkas error.html secara internal
rewrite * /error.html
# Aktifkan templates agar berkas error.html dapat membaca kode kesalahan
templates
# Sajikan berkas dari direktori error khusus
file_server {
root /var/www/errors
}
}
}
2. Kode HTML Berkas error.html yang Dinamis
#
Kita membuat berkas /var/www/errors/error.html yang menggunakan variabel template untuk menampilkan status kode dan detail kesalahan:
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<title>Terjadi Kesalahan - {{ .Placeholder "http.error.status_code" }}</title>
<style>
body { font-family: sans-serif; text-align: center; padding: 50px; background: #fafafa; color: #333; }
.card { background: white; padding: 30px; border-radius: 8px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); max-width: 500px; margin: 0 auto; }
h1 { color: #d9534f; margin-top: 0; }
.trace-id { background: #eee; padding: 10px; font-family: monospace; font-size: 0.9em; border-radius: 4px; }
</style>
</head>
<body>
<div class="card">
<h1>Ups, Terjadi Kesalahan!</h1>
<p>Kami meminta maaf atas ketidaknyamanan ini. Permintaan Anda tidak dapat diproses oleh server kami.</p>
<!-- Menampilkan status kode HTTP (e.g. 502, 404, 403) -->
<h3>Status Error: {{ .Placeholder "http.error.status_code" }} - {{ .Placeholder "http.error.status_text" }}</h3>
<p>Gunakan ID Pelacakan berikut jika Anda ingin melaporkan masalah ini ke tim teknis kami:</p>
<!-- Menampilkan ID Request Unik (Trace ID) menggunakan UUID request -->
<div class="trace-id">Trace ID: {{ .Placeholder "http.request.uuid" }}</div>
<p><a href="/">Kembali ke Halaman Utama</a></p>
</div>
</body>
</html>
Use Case 2: Render Markdown Menjadi HTML Secara Otomatis #
Bagi kita yang mengelola situs web dokumentasi sederhana, menulis berkas di format HTML bisa sangat melelahkan. Caddy memecahkan masalah ini dengan menyediakan fungsi template bawaan bernama markdown. Fungsi ini akan mem-parsing teks berformat Markdown secara real-time menjadi tag HTML standar.
1. Penulisan Caddyfile untuk Auto-Markdown #
# Contoh: Render Markdown Otomatis
docs.example.com {
root * /var/www/docs
# Aktifkan templates untuk semua file
templates
# Rute fallback: jika klien meminta berkas tanpa ekstensi,
# arahkan secara internal ke berkas .md
try_files {path} {path}.md {path}/index.html
file_server
}
2. Berkas HTML Wrapper docs.md (atau index.html)
#
Kita membuat berkas template pembungkus yang bertindak sebagai tata letak (layout) utama kita, yang secara otomatis membaca dan merender isi berkas Markdown:
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<title>Dokumentasi Teknis Kita</title>
<link rel="stylesheet" href="/css/docs-style.css">
</head>
<body>
<div class="sidebar">
<h3>Daftar Isi</h3>
<ul>
<li><a href="/panduan">Panduan Memulai</a></li>
<li><a href="/instalasi">Proses Instalasi</a></li>
</ul>
</div>
<div class="content">
<!-- Fungsi markdown Caddy membaca file markdown dari path requested -->
<!-- dan merendernya secara dinamis di dalam bodi div ini -->
{{ if .Req.URL.Path | endsWith ".md" }}
{{ include .Req.URL.Path | markdown }}
{{ else }}
<!-- Default jika file tidak berakhiran .md -->
{{ include (printf "%s.md" .Req.URL.Path) | markdown }}
{{ end }}
</div>
</body>
</html>
Use Case 3: Membuat XML Sitemap Dinamis Secara Serverless #
Ketika membangun situs statis yang sering diperbarui, kita harus menyediakan berkas sitemap.xml agar mesin pencari dapat mengindeks semua berkas baru kita secara berkala. Daripada menggunakan skrip build eksternal, kita dapat menyuruh Caddy merender sitemap XML secara dinamis menggunakan direktif templates:
<!-- file: sitemap.xml -->
{{- /* Matikan spasi kosong bawaan template Go dengan tanda minus (-) */ -}}
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://example.com/</loc>
<lastmod>{{ now | date "2006-01-02" }}</lastmod>
<changefreq>daily</changefreq>
<priority>1.0</priority>
</url>
{{- range .Dir -}}
{{- if and (endsWith ".html" .Name) (ne .Name "error.html") (ne .Name "index.html") -}}
<url>
<loc>https://example.com/{{ .Name }}</loc>
<lastmod>{{ now | date "2006-01-02" }}</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
{{- end -}}
{{- end -}}
</urlset>
Di Caddyfile, pastikan kita mengaktifkan header Content-Type: application/xml ketika berkas sitemap.xml diakses agar dibaca dengan benar oleh browser atau bot mesin pencari.
Manajemen Caching untuk Dynamic Templates #
Karena berkas-berkas template HTML dievaluasi secara dinamis pada saat permintaan tiba, kita harus mengatur kebijakan cache secara defensif. Secara bawaan, Caddyfile mungkin menyertakan header cache agresif untuk mempercepat pemuatan aset statis. Namun, jika browser klien meng-cache halaman yang berisi template dinamis secara permanen, mereka tidak akan pernah melihat pembaruan data real-time kita.
Kita harus menonaktifkan caching untuk file dinamis kita:
# Contoh: Manajemen Cache untuk Template Dinamis
example.com {
root * /var/www/html
templates
# Setel cache-control no-store khusus untuk file dinamis kita
@dynamic_pages path *.html /
header @dynamic_pages Cache-Control "no-store, no-cache, must-revalidate"
file_server
}
Aspek Keamanan Kritis: Mencegah Template Injection #
Meskipun direktif templates memberikan fleksibilitas yang luar biasa, jika kita tidak hati-hati, fitur ini dapat membuka celah keamanan serius yang dikenal sebagai Server-Side Template Injection (SSTI).
Bahaya SSTI dan Kebocoran Kredensial #
Jika bodi berkas HTML template kita menuliskan data input mentah dari parameter query klien secara langsung tanpa proses sanitasi yang ketat, penyerang dapat menyisipkan fungsi template Go jahat untuk membaca informasi sensitif di server kita.
<!-- ANTI-PATTERN: JANGAN menuliskan input klien secara langsung -->
<!-- Jika penyerang mengirimkan query: ?user={{ env "DATABASE_PASSWORD" }} -->
<!-- Caddy akan mengevaluasi tag tersebut dan mengekspos kata sandi database kita! -->
<h1>Halo, {{ .Req.URL.Query.Get "user" }}</h1>
Solusi Mitigasi Keamanan di Caddy #
- Gunakan Sanitasi HTML Otomatis: Go template secara bawaan menggunakan mesin
html/templateyang aman yang secara otomatis melakukan escape pada karakter HTML berbahaya (seperti mengubah<menjadi<). Namun, untuk data input dinamis, kita harus sangat waspada. - Batasi Penggunaan Fungsi
env: Jangan gunakan fungsienvdi dalam template berkas HTML publik jika berkas tersebut dapat diedit atau diunggah oleh pengguna luar. - Isolasi Berkas Template: Pastikan direktori root web kita tidak memiliki berkas konfigurasi sensitif yang bisa dibaca menggunakan fungsi
include.
Ringkasan #
- Server-Side Rendering: Caddy mengevaluasi tag template Go
{{ ... }}secara real-time di memori sebelum mengirimkan dokumen HTML bersih ke klien.- Go Template Engine: Mewarisi seluruh kapabilitas logika penulisan template bahasa Go (variabel, range loop, if/else cond, partial include).
- Akses Parameter Klien: Membuka akses penuh untuk membaca data request HTTP klien, termasuk Cookie, Header, dan IP address.
- Fungsi Kustom Caddy: Menyediakan fungsionalitas tambahan seperti
include,markdown,cookie,placeholder, dan fungsi enkripsi/hashing.- Halaman Error Dinamis: Memudahkan pembuatan halaman penanganan error kustom yang interaktif lengkap dengan ID pelacakan UUID.
- Parser Markdown: Menyediakan fungsi bawaan
markdownuntuk mengonversi berkas dokumen.mdmenjadi tata letak HTML secara otomatis di tingkat server.- Pengendalian Cache: Selalu setel header
Cache-Control: no-storepada respons berkas HTML dinamis agar browser klien tidak menyimpan data yang kedaluwarsa.- Mitigasi Keamanan: Hindari merender input parameter klien secara mentah guna mencegah celah keamanan Server-Side Template Injection (SSTI).