Wildcard Certificate

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 #

  1. Login ke Cloudflare dashboard
  2. My ProfileAPI TokensCreate Token
  3. Gunakan template “Edit zone DNS” atau buat custom dengan:
    • Permissions: Zone → DNS → Edit
    • Zone Resources: Specific zone → pilih domain kamu
  4. 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.com sendiri) — tambahkan apex secara eksplisit di blok site.
  • Wildcard hanya satu level (*.example.com tidak berlaku untuk sub.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 →

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