Virtual Host #
Virtual Hosting adalah teknologi yang memungkinkan satu server fisik atau satu instansi server web melayani banyak nama domain dan situs yang berbeda secara bersamaan. Di dunia infrastruktur modern, ini adalah kebutuhan yang sangat umum: mulai dari pengembang individu yang ingin menjalankan beberapa proyek portofolio pada satu VPS murah, hingga tingkat perusahaan (enterprise) yang perlu mengelola ratusan subdomain aplikasi dari satu klaster server terpusat. Dengan virtual hosting, pemanfaatan sumber daya server dapat dimaksimalkan tanpa perlu membayar biaya tambahan untuk perangkat keras baru.
Caddy mendukung konfigurasi virtual hosting secara native dengan sintaksis yang sangat bersih dan mudah dipahami. Pada Caddyfile, setiap blok situs (site block) pada dasarnya didefinisikan sebagai satu virtual host independen. Caddy secara dinamis mengisolasi konfigurasi, direktori dasar berkas statis, logika routing, hingga siklus hidup sertifikat TLS untuk masing-masing host tersebut tanpa risiko saling tumpang tindih.
Mekanika Name-Based Virtual Hosting & SNI #
Bentuk virtual hosting yang paling umum digunakan saat ini adalah Name-Based Virtual Hosting. Pada metode ini, server membedakan situs yang dituju oleh pengunjung berdasarkan tajuk (header) Host HTTP yang dikirimkan oleh browser.
Namun, di era web modern di mana hampir seluruh lalu lintas menggunakan enkripsi HTTPS, muncul tantangan baru: server harus menyajikan sertifikat TLS yang tepat sebelum browser mengirimkan tajuk HTTP Host. Untuk mengatasi hal ini, protokol TLS menggunakan ekstensi yang disebut Server Name Indication (SNI).
Alur Pemrosesan Koneksi HTTPS Virtual Host via SNI:
1. Browser Memulai Koneksi:
Browser mengirimkan pesan TLS Client Hello ke IP Server. Di dalam pesan ini,
ekstensi SNI menyertakan nama host tujuan (misal: "api.example.com").
2. Caddy Membaca SNI:
Caddy menangkap ekstensi SNI sebelum jabat tangan (handshake) TLS selesai.
3. Pemilihan Sertifikat Terisolasi:
Caddy mencari sertifikat TLS yang cocok untuk "api.example.com" di penyimpanan lokalnya
dan menyajikannya ke browser.
4. Handshake TLS Selesai & Kirim Request:
Saluran terenkripsi terbentuk. Browser kini mengirimkan HTTP request yang berisi
header "Host: api.example.com".
5. Routing Caddyfile:
Caddy mencocokkan header Host dengan blok situs di Caddyfile dan meneruskan
permintaan ke handler yang sesuai (misal: reverse proxy backend).
Siklus ini berjalan secara otomatis pada Caddy. Setiap virtual host yang kita deklarasikan di Caddyfile akan didaftarkan ke mesin TLS Caddy untuk pengelolaan sertifikat SNI yang dinamis.
Di dalam memori, Caddy memelihara peta sertifikat (certificate cache) yang sangat efisien. Ketika jabat tangan TLS terjadi, Caddy melakukan pencarian pada peta ini menggunakan nama host SNI sebagai kunci (key). Jika sertifikat yang cocok ditemukan dan masih valid, Caddy akan langsung menggunakannya. Jika sertifikat sudah mendekati masa kedaluwarsa, mesin latar belakang Caddy akan dipicu untuk melakukan pembaruan tanpa mengganggu koneksi aktif yang sedang berjalan.
Konfigurasi Dasar Multi-Domain #
Berikut adalah contoh konfigurasi Caddyfile standar untuk meng-host beberapa domain dan subdomain yang berbeda pada satu server:
# Opsi global
{
email [email protected]
}
# Virtual Host 1: Situs Pemasaran Utama (Static File)
example.com {
root * /var/www/landing-page/dist
encode gzip zstd
file_server
}
# Virtual Host 2: Subdomain API Layanan (Reverse Proxy)
api.example.com {
reverse_proxy localhost:8080
# Menambahkan CORS header khusus untuk API
header {
Access-Control-Allow-Origin "https://example.com"
Access-Control-Allow-Methods "GET, POST, OPTIONS"
}
}
# Virtual Host 3: Blog Perusahaan (Domain Berbeda)
other-blog.com {
root * /var/www/blog
encode gzip
file_server
}
# Virtual Host 4: Pengalihan Domain Lama ke Domain Baru
old-brand.com {
# Mengalihkan domain lama secara permanen (HTTP 301) dengan mempertahankan URI
redir https://example.com{uri} permanent
}
Setiap blok situs di atas akan mendapatkan sertifikat TLS yang terpisah. Caddy memantau tanggal kedaluwarsa masing-masing domain secara independen dan memperbaruinya secara bergantian di latar belakang tanpa saling mengganggu.
Redirect Patterns: WWW vs Non-WWW (Kanonikal) #
Untuk menjaga kualitas SEO (Search Engine Optimization) dan menghindari hukuman konten duplikat (duplicate content penalty) dari mesin pencari seperti Google, kita wajib mengarahkan seluruh lalu lintas ke satu versi domain yang kanonikal (apakah menggunakan prefiks www atau langsung ke domain induk non-www).
Berikut adalah 4 pola penulisan pengalihan kanonikal yang didukung oleh Caddy:
Pola 1: Redirect Blok Terpisah (www ke non-www) #
Ini adalah metode yang paling bersih dan direkomendasikan karena memisahkan logika utama dengan logika pengalihan:
# Mengamankan www dan mengalihkannya
www.example.com {
redir https://example.com{uri} permanent
}
# Blok utama aplikasi
example.com {
root * /var/www/html
file_server
}
Pola 2: Redirect Blok Terpisah (non-www ke www) #
Kebalikan dari Pola 1, digunakan jika domain kanonikal kita ingin mempertahankan prefiks www:
example.com {
redir https://www.example.com{uri} permanent
}
www.example.com {
root * /var/www/html
file_server
}
Pola 3: Penanganan Bersama Tanpa Pengalihan Kanonikal #
Kedua domain melayani konten yang sama tanpa memaksa pengalihan (tidak disarankan untuk SEO karena dianggap konten duplikat):
example.com, www.example.com {
root * /var/www/html
file_server
}
Pola 4: Pengalihan Internal dalam Satu Blok #
Menggabungkan kedua domain dalam satu blok tetapi melakukan pengalihan internal menggunakan pencocok nama host:
example.com, www.example.com {
# Matcher host untuk mendeteksi domain www
@www host www.example.com
redir @www https://example.com{uri} permanent
root * /var/www/html
file_server
}
Perutean Subdomain Dinamis dalam Satu Blok #
Jika kita mengelola aplikasi multi-tenant di mana setiap pengguna baru mendapatkan subdomain khusus (misalnya user1.example.com, user2.example.com), menulis blok situs terpisah untuk setiap subdomain di Caddyfile sangat tidak praktis.
Solusi terbaik adalah menggunakan Wildcard Subdomain dan menangani perutean menggunakan pencocok host di dalam satu blok situs tunggal:
# Caddyfile Subdomain Dinamis
example.com, *.example.com {
# Wajib menggunakan DNS challenge untuk wildcard SSL
tls {
dns cloudflare {env.CF_API_TOKEN}
}
# 1. Mendefinisikan Pencocok Host (Host Matchers)
@apex host example.com
@api host api.example.com
@docs host docs.example.com
# 2. Logika Penanganan untuk Masing-Masing Host
handle @apex {
root * /var/www/landing
file_server
}
handle @api {
reverse_proxy localhost:8080
}
handle @docs {
root * /var/www/documentation
file_server
}
# 3. Penanganan untuk Subdomain Pengguna Dinamis
# Jika tidak cocok dengan apex, api, atau docs, diasumsikan sebagai subdomain tenant
handle {
# Kita dapat menggunakan header untuk diteruskan ke backend SaaS kita
header X-Tenant-Subdomain {labels.2} # Mengambil label subdomain dinamis
reverse_proxy tenant-service:3000
}
}
Di dalam Caddy, placeholder {labels.2} merujuk pada label domain jika dihitung dari belakang (0-indexed). Untuk user1.example.com:
{labels.0}=com{labels.1}=example{labels.2}=user1(subdomain dinamis)
Port-Based Virtual Hosting (Non-Standard Ports) #
Meskipun secara default Caddy mendengarkan lalu lintas web pada port standar HTTP 80 dan HTTPS 443, ada situasi di mana kita perlu menjalankan Caddy pada port kustom. Hal ini umum terjadi di lingkungan internal kantor, di balik firewall korporat, atau saat Caddy dijalankan sebagai kontainer Docker yang port-nya dipetakan secara khusus oleh sistem orkestrasi.
Caddy mendukung penuh pendefinisian port kustom secara langsung pada header nama host di Caddyfile:
# Mendengarkan pada port non-standar
http://internal.example.local:8080 {
root * /var/www/internal-docs
file_server
}
https://secure.example.local:8443 {
# Karena ini port non-standar, kita harus menentukan konfigurasi TLS internal
# agar Caddy tidak gagal menyelesaikan HTTP challenge Let's Encrypt eksternal
tls internal
reverse_proxy localhost:9000
}
[!NOTE] Jika kita menyertakan skema protokol
http://secara eksplisit sebelum nama domain, Caddy secara otomatis menonaktifkan fitur Automatic HTTPS untuk blok tersebut dan hanya melayani lalu lintas HTTP tidak aman pada port yang ditentukan. Sebaliknya, menyertakanhttps://akan mengaktifkan mesin TLS Caddy untuk mendengarkan jabat tangan TLS pada port kustom tersebut.
Snippet untuk Konfigurasi yang DRY (Don’t Repeat Yourself) #
Ketika kita mengelola puluhan virtual host di Caddyfile, kita sering kali menulis konfigurasi yang sama berulang kali (seperti tajuk keamanan, pengaturan kompresi, dan logging). Caddy menyediakan fitur Snippet untuk membungkus konfigurasi berulang tersebut agar dapat digunakan kembali secara efisien.
Contoh Penting: Snippet untuk Reverse Proxy Kritis #
Di lingkungan produksi, kita sering kali memerlukan penanganan kompresi khusus dan pengaturan parameter proxy yang kokoh untuk mencegah kegagalan gateway:
# 1. Mendefinisikan Snippet Header Keamanan
(security_headers) {
header {
Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
X-Content-Type-Options "nosniff"
X-Frame-Options "SAMEORIGIN"
Referrer-Policy "strict-origin-when-cross-origin"
# Menghapus identitas server web dari publik
-Server
}
}
# 2. Mendefinisikan Snippet Logging dengan Parameter Dinamis
(access_log) {
log {
# Menggunakan argumen dinamis {args[0]} untuk membedakan nama file log
output file /var/log/caddy/{args[0]}-access.log {
roll_size 50mb
roll_keep 10
}
format json
}
}
# 3. Snippet untuk Optimalisasi Reverse Proxy
(proxy_settings) {
reverse_proxy {
# Atur batas waktu koneksi agar proxy tidak menggantung
dial_timeout 5s
read_timeout 30s
# Kebijakan load balancing jika memiliki upstream cadangan
lb_policy round_robin
}
}
# 4. Mendefinisikan Snippet Komposit (Mengimpor Snippet Lain)
(standard_site) {
encode gzip zstd
import security_headers
}
# ── Menerapkan Snippet ke Virtual Host ────────────────────────
example.com {
root * /var/www/main
import standard_site
import access_log "main-site"
file_server
}
blog.example.com {
root * /var/www/blog
import standard_site
import access_log "blog"
file_server
}
Path-Based Virtual Hosting #
Selain membedakan perutean berdasarkan nama host (name-based), Caddy juga mendukung perutean berdasarkan jalur URL (path-based routing) di dalam satu virtual host yang sama. Hal ini berguna jika kita ingin menyajikan frontend statis dan backend API di bawah domain tunggal tanpa subdomain terpisah:
# Perutean berbasis jalur URL
example.com {
# 1. Jalur /app/* mengarah ke Frontend SPA
handle /app/* {
# Potong prefix "/app" agar file index.html dicari di root, bukan di root/app/
uri strip_prefix /app
root * /var/www/app/dist
try_files {path} /index.html
file_server
}
# 2. Jalur /api/* mengarah ke Backend Node.js
handle /api/* {
reverse_proxy localhost:8080
}
# 3. Jalur Fallback default (Landing Page Statis)
handle {
root * /var/www/landing
file_server
}
}
Manajemen Konfigurasi Berbasis Environment #
Dalam alur kerja pengembangan modern, kita memerlukan konfigurasi virtual host yang berbeda untuk lingkungan lokal (komputer kerja kita) dan lingkungan server produksi asli. Caddyfile mendukung variabel lingkungan untuk mempermudah transisi ini tanpa mengubah kode konfigurasi.
Caddyfile Produksi: #
# Konfigurasi di Server Produksi
{
email [email protected]
}
# Menggunakan variabel lingkungan untuk domain
{env.DOMAIN_NAME} {
import security_headers
root * /var/www/html
file_server
}
Caddyfile Pengembangan Lokal: #
# Konfigurasi di Komputer Developer (Caddyfile.dev)
{
# Menggunakan Otoritas CA Lokal
local_certs
}
# Domain lokal untuk pengembangan
app.localhost {
tls internal
reverse_proxy localhost:3000
}
Diagnosis dan Pemantauan Virtual Host #
Caddy menyediakan API administratif lokal di port 2019 untuk memeriksa pemetaan virtual host yang sedang aktif di memori:
# 1. Mendapatkan daftar seluruh virtual host yang terdaftar di server HTTP Caddy
curl -s http://localhost:2019/config/apps/http/servers/ | jq 'keys'
# Output sukses:
# [ "srv0" ]
# 2. Memeriksa detail rute perutean pada server srv0
curl -s http://localhost:2019/config/apps/http/servers/srv0/routes | jq .
# 3. Menguji respons tajuk Host secara lokal menggunakan curl
curl -H "Host: api.example.com" http://localhost:80
Ringkasan #
- Site Block sebagai Host — Setiap blok situs di Caddyfile didefinisikan sebagai satu virtual host independen dengan sertifikat TLS terpisah.
- Teknologi SNI — Caddy membaca ekstensi Server Name Indication (SNI) selama handshake TLS untuk memilih sertifikat yang tepat sebelum header HTTP Host diterima.
- Pengalihan Kanonikal — Gunakan blok situs terpisah dengan direktif
rediruntuk mengalihkan subdomainwwwke domain utama guna menjaga kesehatan SEO.- Port-Based Virtual Hosting — Caddy mendukung pendengaran lalu lintas pada port kustom non-standar dengan prefiks protokol kustom (
http://atauhttps://).- Wildcard Subdomain — Gunakan matcher
hostdi dalam satu blok wildcard (*.domain.com) untuk merutekan subdomain dinamis ke layanan backend yang berbeda.- Snippet DRY — Manfaatkan Snippet untuk menyatukan konfigurasi tajuk keamanan, kompresi, dan logging agar tidak terjadi duplikasi kode di Caddyfile.
- Path-Based Routing — Lokasi perutean
handledengan kombinasi pencocokan jalur (misal/api/*) memungkinkan kita melayani API dan berkas statis di bawah satu nama domain yang sama.