Security Headers #
Security headers adalah respons HTTP headers yang memberitahu browser bagaimana berperilaku saat menampilkan kontenmu — apakah boleh dimuat dalam iframe, protokol mana yang boleh di-request, resource apa yang boleh dimuat, dan banyak lagi. Mengkonfigurasi security headers yang tepat bisa mencegah berbagai serangan web termasuk XSS, clickjacking, MIME sniffing, dan information disclosure.
Caddy membuat penambahan security headers sangat mudah melalui direktif header.
Header Security Esensial #
example.com {
header {
# ── Proteksi Transport ────────────────────────────────────
# Paksa HTTPS untuk semua request selama 2 tahun
# includeSubDomains: berlaku juga untuk semua subdomain
# preload: daftarkan ke HSTS preload list browser
Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
# ── Proteksi Konten ───────────────────────────────────────
# Cegah browser menebak MIME type (MIME sniffing)
X-Content-Type-Options "nosniff"
# ── Proteksi Framing ──────────────────────────────────────
# Cegah halaman dimuat dalam iframe (clickjacking protection)
# DENY: tidak bisa di-iframe sama sekali
# SAMEORIGIN: bisa di-iframe hanya dari domain yang sama
X-Frame-Options "SAMEORIGIN"
# ── Privacy ───────────────────────────────────────────────
# Kontrol informasi Referer yang dikirim ke site lain
Referrer-Policy "strict-origin-when-cross-origin"
# ── Sembunyikan Info Server ────────────────────────────────
-Server
-X-Powered-By
}
file_server {
root /var/www/html
}
}
HSTS — HTTP Strict Transport Security #
HSTS memberi tahu browser bahwa site ini hanya boleh diakses via HTTPS, bahkan jika user mengetik http://:
example.com {
header {
# max-age=31536000 → 1 tahun
# max-age=63072000 → 2 tahun (direkomendasikan)
# includeSubDomains → berlaku untuk SEMUA subdomain
# preload → minta browser pre-load HSTS (permanen!)
Strict-Transportation-Security "max-age=63072000; includeSubDomains; preload"
}
}
Hati-hati denganpreload! Setelah domain masuk ke HSTS preload list browser, sangat sulit dihapus (bisa butuh berbulan-bulan). Pastikan kamu berkomitmen untuk selalu menggunakan HTTPS di domain dan SEMUA subdomain sebelum menambahkanpreload. Mulai denganmax-age=300(5 menit) untuk testing, naikkan ke nilai besar setelah yakin.
Content Security Policy (CSP) #
CSP adalah header yang paling powerful sekaligus paling kompleks. Ia mendefinisikan sumber resource yang boleh dimuat oleh browser:
example.com {
header {
# CSP yang ketat untuk aplikasi modern
Content-Security-Policy "
default-src 'self';
script-src 'self' 'nonce-{http.request.uuid}' https://cdn.jsdelivr.net;
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
img-src 'self' data: https: blob:;
font-src 'self' https://fonts.gstatic.com;
connect-src 'self' https://api.example.com wss://ws.example.com;
media-src 'self';
object-src 'none';
frame-src 'none';
frame-ancestors 'none';
base-uri 'self';
form-action 'self';
upgrade-insecure-requests;
"
}
}
Penjelasan Direktif CSP #
Direktif Arti
──────────────────────────────────────────────────────────────────────────────
default-src Default untuk semua resource (fallback)
script-src Dari mana JavaScript boleh dimuat
style-src Dari mana CSS boleh dimuat
img-src Dari mana gambar boleh dimuat
font-src Dari mana font boleh dimuat
connect-src URL yang boleh dihubungi via XHR, fetch, WebSocket
media-src Dari mana video/audio boleh dimuat
object-src Dari mana plugin object (<object>, <embed>) boleh dimuat
frame-src Dari mana iframe boleh dimuat
frame-ancestors Siapa yang boleh mem-frame halaman ini (gantikan X-Frame-Options)
base-uri Batasi URL yang bisa digunakan di <base> tag
form-action Batasi URL yang bisa menjadi tujuan form submit
upgrade-insecure-requests Upgrade HTTP ke HTTPS otomatis
Nilai sumber:
'self' Domain yang sama
'none' Tidak diizinkan dari mana pun
'unsafe-inline' Inline script/style (kurangi keamanan)
'unsafe-eval' Eval() dan fungsi sejenisnya (kurangi keamanan)
https: Semua HTTPS URL
data: Data URI
blob: Blob URI
URL spesifik https://trusted-cdn.com
nonce-{value} Script/style dengan nonce yang cocok
sha256-{hash} Script/style dengan hash yang cocok
CSP Report-Only untuk Testing #
Sebelum mengaktifkan CSP secara penuh, gunakan report-only mode untuk melihat apa yang akan diblokir:
example.com {
header {
# Report-Only: tidak memblokir, hanya melaporkan violations
Content-Security-Policy-Report-Only "
default-src 'self';
script-src 'self' https://cdn.jsdelivr.net;
report-uri https://csp.example.com/reports;
"
# Setelah yakin tidak ada false positive, ganti dengan:
# Content-Security-Policy "..."
}
}
Permissions Policy #
Menggantikan Feature Policy, header ini mengontrol akses ke fitur browser:
example.com {
header {
# Nonaktifkan fitur yang tidak digunakan aplikasimu
Permissions-Policy "
camera=(),
microphone=(),
geolocation=(),
payment=(),
usb=(),
magnetometer=(),
accelerometer=(),
gyroscope=(),
fullscreen=(self),
display-capture=()
"
}
}
Menghapus Header yang Mengekspos Info Server #
example.com {
header {
# Header yang mengekspos teknologi stack
-Server # Caddy/2.x.x
-X-Powered-By # PHP/8.x, Express, dll.
-X-AspNet-Version
-X-AspNetMvc-Version
# Header yang tidak diperlukan
-X-Runtime
-X-Request-Id # Jika tidak diperlukan client
}
reverse_proxy backend:3000
}
Set Header di Level Reverse Proxy vs Level Site #
example.com {
# Level site: berlaku untuk SEMUA response termasuk error Caddy sendiri
header Strict-Transport-Security "max-age=31536000"
header X-Frame-Options "SAMEORIGIN"
reverse_proxy backend:3000 {
# Level reverse proxy: hanya untuk response dari backend
# Berguna jika ingin override/tambahkan header spesifik untuk backend
header_down X-Backend-Version "v2.1.0"
header_down -X-Powered-By # Hapus dari response backend
}
}
Template Lengkap Security Headers #
(security_headers) {
header {
# Transport security
Strict-Transport-Security "max-age=63072000; includeSubDomains"
# Content security
X-Content-Type-Options "nosniff"
# Framing protection
X-Frame-Options "SAMEORIGIN"
# Referrer policy
Referrer-Policy "strict-origin-when-cross-origin"
# Permissions (minimal — sesuaikan dengan kebutuhan app)
Permissions-Policy "camera=(), microphone=(), geolocation=()"
# Sembunyikan server info
-Server
-X-Powered-By
}
}
# Gunakan snippet di semua site
example.com {
import security_headers
file_server { root /var/www/html }
}
api.example.com {
import security_headers
reverse_proxy localhost:8080
}
Testing Security Headers #
# Test dengan curl
curl -I https://example.com | grep -i \
"strict-transport\|x-frame\|x-content-type\|referrer\|content-security"
# Atau gunakan tools online:
# https://securityheaders.com
# https://observatory.mozilla.org
# https://www.ssllabs.com/ssltest/
# Cek skor SSL/TLS
# ssllabs.com memberikan grade A+ untuk konfigurasi Caddy default
# Test lokal dengan verbose
curl -sv https://example.com 2>&1 | grep "< " | grep -v "^< $"
Ringkasan #
- HSTS (
Strict-Transport-Security) memaksa browser selalu pakai HTTPS — mulai denganmax-age=300untuk testing, naikkan ke nilai besar (63072000 = 2 tahun) setelah yakin.- CSP (
Content-Security-Policy) adalah header paling powerful untuk mencegah XSS — gunakan Report-Only mode dulu sebelum mengaktifkan penuh.X-Frame-Options: SAMEORIGINmencegah clickjacking — atau gunakanframe-ancestors 'self'di CSP untuk kontrol yang lebih granular.- Selalu hapus header informatif (
-Server,-X-Powered-By) untuk menyembunyikan teknologi stack dari attacker.- Gunakan snippet
(security_headers)untuk menghindari duplikasi konfigurasi header yang sama di banyak site.- Test security headers dengan securityheaders.com dan observatory.mozilla.org — targetkan grade A atau A+.