Apache dan Nginx sudah lama menjadi pilihan default untuk menjalankan PHP di server. Keduanya terbukti, matang, dan punya ekosistem konfigurasi yang sangat luas. Tapi keduanya juga mewarisi satu beban yang sama: kompleksitas. Mengaktifkan HTTPS memerlukan Certbot, konfigurasi virtual host berulang, dan setup TLS yang harus dilakukan manual. Untuk banyak proyek, ini terasa berlebihan.
Caddy hadir dengan pendekatan yang berbeda. Web server modern berbasis Go ini menangani HTTPS secara otomatis — mulai dari permohonan sertifikat, validasi domain, hingga pembaruan otomatis — tanpa satu baris konfigurasi tambahan. Untuk menjalankan PHP, Caddy terhubung ke PHP-FPM melalui direktif php_fastcgi yang sederhana tapi fleksibel.
Artikel ini memandu konfigurasi Caddy dengan PHP-FPM secara lengkap, mulai dari struktur file konfigurasi, setup dasar untuk aplikasi PHP modern seperti Laravel, hingga konfigurasi production-ready dengan security headers dan optimasi performa.
Apa yang Membuat Caddy Berbeda dari Nginx
Sebelum masuk ke konfigurasi, ada baiknya memahami filosofi Caddy agar tidak terjebak memikirkannya seperti Nginx.
Caddy menggunakan Caddyfile sebagai format konfigurasi. Sintaksnya jauh lebih ringkas dibanding blok server {} di Nginx atau VirtualHost di Apache. Caddy juga opinionated dalam hal keamanan: TLS 1.2+, cipher suite modern, HTTP/2, dan HTTP/3 diaktifkan secara default tanpa perlu konfigurasi tambahan.
Yang paling signifikan: Caddy mendapatkan sertifikat TLS secara otomatis melalui ACME (protokol yang digunakan Let’s Encrypt dan ZeroSSL). Tidak perlu certbot, tidak perlu cron job untuk renewal. Selama domain sudah mengarah ke server, Caddy akan mengurus sertifikatnya sendiri.
Caddy membutuhkan akses ke port 80 dan 443 untuk proses validasi domain ACME. Pastikan firewall tidak memblokir kedua port ini sebelum menjalankan Caddy di mode production.
Struktur Direktori Konfigurasi Caddy
Konfigurasi Caddy yang bersih dimulai dari struktur direktori yang terorganisir. Alih-alih menumpuk semua konfigurasi dalam satu file, pisahkan berdasarkan fungsinya.
Buat struktur direktori berikut di /etc/caddy/:
/etc/caddy/
├── Caddyfile # Konfigurasi global
├── config/
│ └── php.conf # Snippet reusable untuk PHP-FPM
└── sites/
└── blog.conf # Konfigurasi per site
Buat direktori yang diperlukan:
sudo mkdir -p /etc/caddy/config /etc/caddy/sites
sudo mkdir -p /var/log/caddy
Konfigurasi Global Caddyfile
File /etc/caddy/Caddyfile berisi konfigurasi global yang berlaku untuk semua site, termasuk logging dan import konfigurasi tambahan.
# /etc/caddy/Caddyfile
{
log default {
format console
output file /var/log/caddy/system.log
exclude http.log.access
}
}
import config/*
import sites/*
Blok log default mengatur format log sistem ke console (lebih mudah dibaca manusia) dan menyimpannya di file terpisah. exclude http.log.access memisahkan access log dari system log — masing-masing site akan punya access log sendiri.
Snippet PHP-FPM yang Reusable
Jika menjalankan beberapa versi PHP atau beberapa site, mendefinisikan snippet PHP-FPM di file tersendiri menghemat banyak duplikasi. Buat file /etc/caddy/config/php.conf:
# /etc/caddy/config/php.conf
(php83) {
php_fastcgi unix//run/php/php8.3-fpm.sock
}
(php84) {
php_fastcgi unix//run/php/php8.4-fpm.sock
}
Sintaks (nama_snippet) { ... } mendefinisikan snippet yang bisa di-import di konfigurasi site mana pun.
Konfigurasi Site PHP dengan Caddy
Setup Dasar untuk Aplikasi Modern (Laravel, Slim, dll)
Aplikasi PHP modern seperti Laravel menggunakan pola front controller — semua request diarahkan ke public/index.php. Caddy menangani ini dengan sangat baik melalui direktif php_fastcgi yang sudah mengerti pola ini secara built-in.
Buat file /etc/caddy/sites/blog.conf:
# /etc/caddy/sites/blog.conf
blog.example.com {
root * /var/www/blog/public
log {
output file /var/log/caddy/blog.access.log
format console
}
encode zstd gzip
import php84
@dotFiles {
path */.*
not path /.well-known/*
}
respond @dotFiles 403
}
Penjelasan tiap direktif:
root * /var/www/blog/public— menetapkan direktori root ke folderpublic, sesuai struktur Laravelencode zstd gzip— mengaktifkan kompresi Zstd dan Gzip secara otomatis; Caddy akan memilih yang didukung browserimport php84— menggunakan snippet yang sudah didefinisikan diconfig/php.conf@dotFiles— matcher yang menangkap semua path dimulai dengan titik, lalu merespons dengan 403 untuk mencegah akses ke file seperti.env
Blok @dotFiles sangat penting untuk keamanan. Tanpanya, file seperti .env bisa diakses publik jika berada di dalam direktori root.
Cara Kerja Direktif php_fastcgi
php_fastcgi bukan sekadar meneruskan request ke PHP-FPM. Direktif ini mengimplementasikan tiga perilaku otomatis yang biasanya harus dikonfigurasi manual di Nginx:
- Directory index rewriting — jika request ke
example.com/dashboard/dan filedashboard/index.phpada, request diarahkan ke file tersebut - Front controller pattern — jika file yang diminta tidak ada di disk, request diteruskan ke
index.phpdi root; ini setara dengantry_files $uri $uri/ /index.php?$query_stringdi Nginx - FastCGI routing — file
.phpditeruskan ke PHP-FPM melalui socket Unix dengan parameter CGI yang tepat
Socket Unix (unix//run/php/php8.4-fpm.sock) lebih cepat dari TCP socket karena tidak melewati network stack. Gunakan TCP (127.0.0.1:9000) hanya jika PHP-FPM berjalan di server yang berbeda.
Optimasi Performa untuk Production
Cache Header untuk Aset Statis
Browser tidak perlu mengunduh ulang file CSS, JS, atau gambar yang sudah pernah diunduh. Tambahkan header Cache-Control untuk aset statis:
# /etc/caddy/sites/blog.conf
blog.example.com {
root * /var/www/blog/public
log {
output file /var/log/caddy/blog.access.log
format console
}
encode zstd gzip
@staticAssets {
path_regexp \.(jpg|jpeg|png|webp|gif|avif|ico|svg|css|js|woff|woff2|pdf)$
}
header @staticAssets Cache-Control "max-age=31536000,public,immutable"
@missingStatic {
path_regexp \.(jpg|jpeg|png|webp|gif|avif|ico|svg|css|js|woff|woff2|pdf)$
not file
}
respond @missingStatic 404 {
close
}
import php84
@dotFiles {
path */.*
not path /.well-known/*
}
respond @dotFiles 403
}
@missingStatic berfungsi sebagai fast 404 — jika request untuk file statis tapi file tidak ada di disk, Caddy langsung merespons 404 tanpa meneruskan request ke PHP-FPM. Ini mengurangi beban PHP-FPM secara signifikan, terutama saat ada bot yang memindai path-path umum.
Membatasi Ukuran Request Body
Tanpa batasan, PHP-FPM akan menerima dan memproses upload berapapun ukurannya. Caddy bisa memblokir request yang terlalu besar sebelum mencapai PHP:
blog.example.com {
request_body {
max_size 20MB
}
# konfigurasi lainnya...
}
Untuk membandingkan strategi performa PHP lebih lanjut, termasuk kapan PHP-FPM sudah cukup dan kapan perlu mempertimbangkan Laravel Octane, baca artikel tentang perbedaan keduanya.
Security Headers untuk Perlindungan Standar
Header keamanan adalah lapisan perlindungan tambahan yang memberitahu browser bagaimana menangani konten dari situs. Caddy memudahkan penambahan header ini melalui direktif header:
blog.example.com {
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
X-Frame-Options "SAMEORIGIN"
X-Content-Type-Options "nosniff"
Referrer-Policy "no-referrer-when-downgrade"
X-XSS-Protection "1; mode=block"
Permissions-Policy "camera=(), microphone=(), geolocation=()"
-Server
}
# konfigurasi lainnya...
}
Direktif -Server (dengan tanda minus) menghapus header Server dari respons, sehingga tidak mengekspos informasi bahwa server menggunakan Caddy. Prefix ? sebaliknya — menambahkan header hanya jika belum ada.
Membatasi HTTP Method
Jika aplikasi hanya memerlukan GET, POST, HEAD, dan OPTIONS, tolak method lain sebelum sampai ke PHP:
@blockedMethods {
not method GET HEAD POST OPTIONS
}
respond @blockedMethods 405 {
close
}
Konfigurasi di Balik Load Balancer atau CDN
Jika Caddy berada di belakang load balancer, reverse proxy, atau CDN seperti Cloudflare, header X-Forwarded-For yang diterima perlu dipercaya agar Caddy bisa mendapatkan IP asli pengunjung:
blog.example.com {
trusted_proxies static 10.0.0.1/24
# konfigurasi lainnya...
}
Ganti 10.0.0.1/24 dengan rentang IP load balancer atau CDN yang digunakan. Tanpa konfigurasi ini, REMOTE_ADDR yang diterima PHP akan berisi IP load balancer, bukan IP pengunjung.
Mengelola Service Caddy
Caddy dijalankan sebagai systemd service. Perintah-perintah berikut untuk mengelola service-nya:
# Mulai service
sudo systemctl start caddy
# Aktifkan saat boot
sudo systemctl enable caddy
# Reload konfigurasi tanpa downtime
sudo systemctl reload caddy
# Cek status
sudo systemctl status caddy
Untuk validasi konfigurasi sebelum reload:
sudo caddy validate --config /etc/caddy/Caddyfile
Perintah ini akan melaporkan error sintaks tanpa harus merestart service yang sedang berjalan.
Kesimpulan
Caddy menghapus sebagian besar boilerplate yang biasanya menyertai setup web server PHP: tidak ada Certbot, tidak ada cron renewal, tidak ada konfigurasi TLS manual. Dengan php_fastcgi yang sudah mengerti front controller pattern, setup untuk Laravel atau framework PHP modern lainnya bisa selesai dalam beberapa baris. Untuk konfigurasi yang lebih kompleks — multi-site, multiple PHP version, atau arsitektur microservice — sistem snippet reusable dan import Caddy membuat semuanya tetap terorganisir tanpa duplikasi.