Wildcard Certificate #
Wildcard certificate adalah sertifikat TLS yang berlaku untuk satu domain dan semua subdomain satu levelnya. Sertifikat untuk *.example.com secara otomatis berlaku untuk app.example.com, api.example.com, blog.example.com, dan subdomain lainnya — tanpa perlu sertifikat terpisah untuk masing-masing.
Ini terdengar sangat nyaman, dan memang begitu — tapi ada satu syarat penting: wildcard certificate hanya bisa diperoleh via DNS-01 challenge. Ini bukan keterbatasan Caddy, melainkan aturan dari protokol ACME itu sendiri. HTTP-01 dan TLS-ALPN-01 secara teknis tidak bisa digunakan untuk memverifikasi kepemilikan wildcard domain.
Mengapa Wildcard Membutuhkan DNS-01? #
Alasan teknis di balik pembatasan ini bersifat mendasar:
HTTP-01 untuk *.example.com:
CA: "Taruh token di http://app.example.com/.well-known/..."
Masalah: Ini hanya memverifikasi 'app.example.com', bukan '*.example.com'
CA harus mengecek setiap subdomain → Tidak mungkin untuk wildcard
TLS-ALPN-01 untuk *.example.com:
CA: "Lakukan TLS handshake ke app.example.com:443"
Masalah: Sama seperti HTTP-01 — hanya verifikasi satu subdomain
DNS-01 untuk *.example.com:
CA: "Buat TXT record di _acme-challenge.example.com"
Kunci: TXT record di domain apex membuktikan kontrol atas
SELURUH domain, termasuk semua subdomain (wildcard)
→ Satu verifikasi untuk semua subdomain
Kapan Menggunakan Wildcard Certificate #
Wildcard certificate masuk akal dalam kondisi berikut:
COCOK untuk wildcard: LEBIH BAIK tanpa wildcard:
────────────────────────────────────── ────────────────────────────────────
Platform multi-tenant Beberapa subdomain statis yang sudah
(customer.yourapp.com) diketahui sebelumnya
Banyak subdomain dinamis yang Satu atau dua subdomain
sering berubah
Ingin mengurangi jumlah request Production dengan banyak domain
ACME ke CA independen
Server yang tidak expose port 80 Semua subdomain bisa pakai
(hanya DNS-01 yang tersedia) HTTP-01 (lebih sederhana)
Plugin DNS Provider — Wajib untuk Wildcard #
Karena DNS-01 membutuhkan akses ke DNS API, kamu perlu plugin DNS provider yang sesuai. Plugin ini tidak ada di binary standar Caddy — perlu di-compile menggunakan xcaddy.
Daftar Provider Populer #
Provider Plugin Module
──────────────────────────────────────────────────────────────
Cloudflare github.com/caddy-dns/cloudflare
AWS Route 53 github.com/caddy-dns/route53
DigitalOcean github.com/caddy-dns/digitalocean
Google Cloud DNS github.com/caddy-dns/googleclouddns
Azure DNS github.com/caddy-dns/azure
Namecheap github.com/caddy-dns/namecheap
GoDaddy github.com/caddy-dns/godaddy
Hetzner github.com/caddy-dns/hetzner
Porkbun github.com/caddy-dns/porkbun
Gandi github.com/caddy-dns/gandi
Netlify DNS github.com/caddy-dns/netlify
Vultr github.com/caddy-dns/vultr
PowerDNS github.com/caddy-dns/powerdns
Compile Caddy dengan Plugin DNS #
# Install xcaddy jika belum ada
go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
# Build dengan plugin Cloudflare
xcaddy build --with github.com/caddy-dns/cloudflare
# Build dengan beberapa plugin sekaligus
xcaddy build \
--with github.com/caddy-dns/cloudflare \
--with github.com/caddy-dns/route53
# Verifikasi plugin tersedia
./caddy list-modules | grep dns
# Output: dns.providers.cloudflare
# dns.providers.route53
# Ganti binary sistem (jika menggunakan APT/systemd)
sudo systemctl stop caddy
sudo cp ./caddy /usr/bin/caddy
sudo systemctl start caddy
Konfigurasi Wildcard: Cloudflare #
Cloudflare adalah provider DNS yang paling umum digunakan bersama Caddy karena API-nya yang sederhana dan mendukung semua fitur yang diperlukan.
Buat API Token Cloudflare #
- Login ke Cloudflare dashboard
- My Profile → API Tokens → Create Token
- Gunakan template “Edit zone DNS” atau buat custom dengan:
- Permissions: Zone → DNS → Edit
- Zone Resources: Specific zone → pilih domain kamu
- Simpan token yang dihasilkan
Gunakan API Token (bukan API Key global). Token memiliki scope yang terbatas hanya untuk zone DNS yang kamu pilih — jauh lebih aman karena jika token bocor, dampaknya terbatas pada zone tersebut saja, bukan seluruh akun Cloudflare.
Konfigurasi Caddyfile #
{
email [email protected]
}
# Wildcard untuk semua subdomain
*.example.com {
tls {
dns cloudflare {env.CLOUDFLARE_API_TOKEN}
}
# Routing berdasarkan subdomain
@app host app.example.com
@api host api.example.com
@blog host blog.example.com
reverse_proxy @app localhost:3000
reverse_proxy @api localhost:8080
reverse_proxy @blog localhost:4000
}
# Domain apex perlu sertifikat terpisah
# Wildcard *.example.com TIDAK mencakup example.com itu sendiri
example.com {
tls {
dns cloudflare {env.CLOUDFLARE_API_TOKEN}
}
file_server {
root /var/www/html
}
}
# Set API token sebagai environment variable
export CLOUDFLARE_API_TOKEN="your-token-here"
# Atau tambahkan ke /etc/environment (permanen)
echo 'CLOUDFLARE_API_TOKEN=your-token-here' | sudo tee -a /etc/environment
# Untuk systemd, gunakan override
sudo systemctl edit caddy
# Tambahkan:
# [Service]
# Environment="CLOUDFLARE_API_TOKEN=your-token-here"
Menggabungkan Apex dan Wildcard #
{
email [email protected]
}
# Satu sertifikat untuk apex DAN wildcard
example.com, *.example.com {
tls {
dns cloudflare {env.CLOUDFLARE_API_TOKEN}
}
@apex host example.com
@app host app.example.com
@api host api.example.com
reverse_proxy @apex localhost:4000
reverse_proxy @app localhost:3000
reverse_proxy @api localhost:8080
}
Konfigurasi Wildcard: AWS Route 53 #
Setup IAM Permissions #
Buat IAM Policy dengan permissions minimal:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"route53:GetChange",
"route53:ChangeResourceRecordSets",
"route53:ListResourceRecordSets"
],
"Resource": [
"arn:aws:route53:::hostedzone/YOUR_HOSTED_ZONE_ID",
"arn:aws:route53:::change/*"
]
},
{
"Effect": "Allow",
"Action": [
"route53:ListHostedZones",
"route53:ListHostedZonesByName"
],
"Resource": "*"
}
]
}
Konfigurasi Caddyfile #
{
email [email protected]
}
*.example.com, example.com {
tls {
dns route53 {
max_retries 10
# Credentials via environment variables (direkomendasikan)
# AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION
# akan di-baca otomatis dari environment
}
}
# Routing subdomain
@api host api.example.com
reverse_proxy @api localhost:8080
# Default untuk apex
file_server
}
# Set credentials AWS
export AWS_ACCESS_KEY_ID="your-access-key"
export AWS_SECRET_ACCESS_KEY="your-secret-key"
export AWS_REGION="us-east-1"
# Atau gunakan IAM role jika di EC2 (tidak perlu key sama sekali)
# Plugin route53 otomatis menggunakan instance metadata
Konfigurasi Wildcard: DigitalOcean #
# Buat API token di:
# DigitalOcean Dashboard → API → Generate New Token
# Pilih: Read + Write scope
{
email [email protected]
}
*.example.com, example.com {
tls {
dns digitalocean {env.DO_AUTH_TOKEN}
}
reverse_proxy localhost:3000
}
On-Demand TLS untuk Platform Multi-Tenant #
Wildcard certificate memiliki keterbatasan: ia hanya mencakup satu level subdomain. *.example.com berlaku untuk app.example.com tapi tidak untuk sub.app.example.com.
Untuk platform multi-tenant di mana customer menggunakan domain sendiri (customer1.com, customer2.com), pendekatan yang tepat adalah On-Demand TLS — Caddy mendapatkan sertifikat untuk setiap domain saat pertama kali domain tersebut diakses:
{
email [email protected]
on_demand_tls {
# Endpoint yang di-konsultasikan untuk setiap domain baru
# Harus return 200 untuk domain yang valid, non-200 untuk yang tidak
ask https://yourplatform.com/api/validate-domain
# Rate limiting untuk mencegah abuse
interval 2m # Maksimal satu domain baru per 2 menit
burst 5 # Atau 5 domain dalam satu burst
}
}
# Listen di port 443, terima semua domain
:443 {
tls {
on_demand
}
reverse_proxy localhost:3000
}
Endpoint Validasi Domain #
// Contoh endpoint validasi (Node.js/Express)
app.get('/api/validate-domain', async (req, res) => {
const domain = req.query.domain;
// Cek apakah domain terdaftar di platform kamu
const isValid = await db.domains.exists({ domain, status: 'active' });
if (isValid) {
res.status(200).json({ valid: true });
} else {
res.status(403).json({ valid: false, reason: 'Domain not registered' });
}
});
Troubleshooting Wildcard Certificate #
Plugin DNS Tidak Ditemukan #
# Error: "unrecognized module" atau "no module named dns.providers.cloudflare"
# Cek apakah binary Caddy yang digunakan sudah include plugin
caddy list-modules | grep dns
# Jika tidak ada, perlu compile ulang
xcaddy build --with github.com/caddy-dns/cloudflare
sudo cp ./caddy /usr/bin/caddy
DNS Propagation Terlalu Lambat #
# Error: "DNS record not found" saat challenge
# DNS-01 challenge memerlukan TXT record yang sudah propagate
# Cek apakah TXT record sudah ada
dig TXT _acme-challenge.example.com @8.8.8.8
dig TXT _acme-challenge.example.com @1.1.1.1
# Jika belum ada, tunggu beberapa menit
# Caddy secara otomatis retry dengan jeda
# Untuk mempercepat, set TTL DNS record ke nilai rendah sebelumnya
# Atau gunakan resolvers yang lebih cepat
API Token Tidak Valid atau Kurang Permission #
# Error: "authentication error" atau "insufficient permissions"
# Test API token Cloudflare secara manual
curl -X GET "https://api.cloudflare.com/client/v4/zones" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json"
# Respons harus menyertakan zone domain kamu
# Jika error 403, token kurang permission DNS:Edit
Ringkasan #
- Wildcard certificate (
*.example.com) hanya bisa diperoleh via DNS-01 challenge — ini bukan keterbatasan Caddy tapi aturan protokol ACME.- Perlu compile Caddy dengan plugin DNS yang sesuai provider kamu (
xcaddy build --with github.com/caddy-dns/cloudflare).- Wildcard TIDAK mencakup apex domain (
example.comsendiri) — tambahkan apex secara eksplisit di blok site.- Wildcard hanya satu level (
*.example.comtidak berlaku untuksub.app.example.com) — untuk multi-level, gunakan On-Demand TLS.- Simpan API token DNS di environment variable, bukan hardcoded di Caddyfile.
- Gunakan API token dengan scope minimal (hanya DNS:Edit untuk zone tertentu), bukan API key global.
← Sebelumnya: Self-Signed & Internal CA Berikutnya: DNS Challenge →