Transport #
Modul transport di dalam reverse proxy Caddy menentukan bagaimana data secara fisik dikirimkan antara proxy dan server backend kita. Jika direktif utama reverse_proxy mengatur alamat tujuan (upstream) dan pembagian beban (load balancing), maka blok transport mengontrol parameter tingkat rendah seperti negosiasi protokol (HTTP/1.1, HTTP/2, gRPC), enkripsi TLS internal, batas waktu tunggu socket (timeout), serta daur hidup koneksi TCP (connection pooling). Konfigurasi transport yang tepat adalah faktor penentu utama dalam mengoptimalkan latensi jaringan, mengamankan jalur komunikasi internal, dan meningkatkan ketahanan server pada lalu lintas tinggi.
Pengenalan HTTP Transport #
Secara default, jika kita tidak mendefinisikannya secara eksplisit, Caddy akan menggunakan modul transport http untuk berkomunikasi dengan backend. Modul ini menyediakan opsi penyesuaian timeout yang sangat lengkap untuk mencegah socket server kita menggantung akibat backend yang tidak responsif.
Berikut adalah konfigurasi parameter batas waktu (timeout) pada transport HTTP:
# Mengatur timeout transport HTTP
example.com {
reverse_proxy localhost:3000 {
transport http {
# Batas waktu maksimal membuat koneksi TCP + TLS ke backend
dial_timeout 3s
# Batas waktu menerima header respon dari backend setelah request terkirim
response_header_timeout 15s
# Batas waktu pembacaan body data dari backend
# // JANGAN aktifkan ini jika backend melayani streaming data lambat
# read_timeout 60s
# Durasi koneksi idle dipertahankan tetap terbuka di dalam pool
keep_alive 90s
}
}
}
dial_timeout: Mengontrol batas waktu negosiasi socket TCP awal dan jabat tangan TLS dengan backend. Jika backend sedang mati total, Caddy akan langsung memutuskan koneksi dalam waktu 3 detik (fail-fast) untuk dialihkan ke backend cadangan.response_header_timeout: Sangat penting untuk mendeteksi backend yang mengalami deadlock aplikasi (misalnya kueri database yang terkunci). Caddy akan memutuskan koneksi jika backend tidak mengirimkan header HTTP pertama dalam batas waktu yang ditentukan.read_timeout: Membatasi waktu pembacaan body data response. Parameter ini sebaiknya dinonaktifkan (default: tidak terbatas) jika server melayani proses unduhan berkas raksasa atau komunikasi real-time seperti Server-Sent Events (SSE).
Memahami Bahaya Socket Exhaustion (Kelemahan Port) #
Ketika server Caddy memproses ribuan permintaan per detik tanpa konfigurasi connection pool yang tepat, sistem operasi akan dipaksa membuka socket TCP baru untuk setiap request. Setiap koneksi TCP membutuhkan satu ephemeral port (port sementara di kisaran 32768–60999 pada Linux).
Setelah koneksi ditutup, socket TCP akan memasuki status TIME_WAIT selama 60 detik (berdasarkan konfigurasi standar kernel Linux) sebelum port tersebut dapat digunakan kembali. Jika laju pembuatan koneksi baru melebihi laju pembersihan status TIME_WAIT, server akan mengalami Socket Exhaustion. Gejalanya adalah Caddy gagal menghubungi backend dengan pesan error dial tcp: lookup localhost: trigger: assign requested address. Menyetel keep_alive yang stabil memastikan port-port ini digunakan kembali secara efisien.
Enkripsi ke Backend (Upstream TLS) #
Dalam arsitektur keamanan Zero Trust, lalu lintas data di dalam jaringan privat lokal sekalipun harus tetap terenkripsi menggunakan HTTPS. Caddy mendukung pembuatan koneksi TLS yang aman ke arah backend.
# Konfigurasi upstream TLS aman
example.com {
# Perhatikan skema protokol menggunakan https://
reverse_proxy https://internal-backend:8443 {
transport http {
# Aktifkan TLS ke backend
tls
# Tentukan SNI (Server Name Indication) untuk validasi sertifikat backend
tls_server_name internal-backend.company.local
# Hanya percayai sertifikat yang diterbitkan oleh CA internal perusahaan kita
tls_trusted_ca_certs /etc/caddy/certs/internal-ca.pem
}
}
}
Negosiasi ALPN (Application-Layer Protocol Negotiation) #
Selama jabat tangan TLS dengan backend, Caddy secara otomatis melakukan negosiasi protokol menggunakan ekstensi ALPN. Ini memungkinkan Caddy dan backend menyepakati penggunaan protokol terbaik (misalnya HTTP/2) secara langsung tanpa overhead tambahan. Jika backend mendukung HTTP/2, Caddy akan secara otomatis meningkatkan koneksi dari HTTP/1.1 ke HTTP/2, memungkinkan multiplexing banyak request dalam satu koneksi fisik.
Praktik Terbaik Keamanan Upstream TLS #
// ANTI-PATTERN: Mengabaikan verifikasi sertifikat di produksi
tls_insecure_skip_verify // JANGAN GUNAKAN DI PRODUKSI! Ini memicu serangan Man-in-the-Middle
// BENAR: Daftarkan file Certificate Authority (CA) internal tepercaya kita
tls_trusted_ca_certs /path/to/internal-ca.pem
Banyak developer tergoda untuk menuliskan tls_insecure_skip_verify agar Caddy tidak memvalidasi sertifikat self-signed milik backend. Tindakan ini sangat berbahaya di lingkungan produksi karena menonaktifkan fungsi verifikasi identitas, membuat sistem rentan terhadap serangan penyadapan lalu lintas (Man-in-the-Middle). Cara yang benar adalah menerbitkan sertifikat backend menggunakan CA internal kita sendiri, lalu mendaftarkan berkas Root CA tersebut ke opsi tls_trusted_ca_certs milik Caddy.
Mutual TLS (mTLS) End-to-End #
Untuk tingkat keamanan tertinggi, kita dapat menerapkan Mutual TLS (mTLS). Pada mTLS, tidak hanya Caddy yang memverifikasi identitas backend, tetapi backend juga memverifikasi identitas Caddy sebelum menyajikan data sensitif.
sequenceDiagram
autonumber
participant Caddy as Caddy Proxy
participant Backend as App Server (mTLS Required)
Caddy->>Backend: 1. ClientHello (Kirim daftar cipher suite)
Backend-->>Caddy: 2. ServerHello & Server Certificate (Kirim sertifikat backend)
Backend-->>Caddy: 3. Certificate Request (Backend minta sertifikat Caddy)
Caddy->>Backend: 4. Client Certificate (Caddy kirim tls_client_auth)
Note over Caddy,Backend: Kedua belah pihak saling memverifikasi kunci kriptografis
Caddy->>Backend: 5. Finished (Jalur mTLS Aman Terbentuk)Berikut adalah contoh konfigurasi mTLS pada tingkat transport Caddy:
# Konfigurasi mTLS
secure.example.com {
reverse_proxy https://secure-backend:8443 {
transport http {
tls
# 1. Verifikasi sertifikat backend menggunakan CA ini
tls_trusted_ca_certs /etc/caddy/certs/backend-root-ca.pem
# 2. Kirim sertifikat klien milik Caddy ke backend untuk diverifikasi
tls_client_auth /etc/caddy/certs/caddy-client.crt /etc/caddy/certs/caddy-client.key
tls_server_name secure-backend.internal
}
}
}
tls_client_auth: Parameter ini membutuhkan dua argumen, yaitu lokasi berkas sertifikat publik klien (.crt) dan berkas kunci privat (.key) milik Caddy. Backend harus dikonfigurasi untuk mewajibkan (require) jabat tangan TLS klien dan memvalidasinya menggunakan Root CA yang sama.
Tutorial Singkat Pembuatan Sertifikat mTLS via OpenSSL #
Untuk memfasilitasi pengujian di lingkungan lokal, kita dapat menghasilkan sertifikat mTLS secara manual menggunakan perintah OpenSSL berikut:
# 1. Buat Private Key dan Sertifikat Root CA internal (berlaku 10 tahun)
openssl req -x509 -nodes -newkey rsa:4096 -days 3650 \
-keyout internal-ca.key -out internal-ca.pem \
-subj "/CN=Internal Infrastructure Root CA/O=Company"
# 2. Buat CSR dan Kunci untuk Caddy Client
openssl req -newkey rsa:2048 -nodes \
-keyout caddy-client.key -out caddy-client.csr \
-subj "/CN=caddy-edge-proxy/O=Infrastructure"
# 3. Tandatangani Sertifikat Caddy Client menggunakan Root CA kita (berlaku 1 tahun)
openssl x509 -req -in caddy-client.csr -CA internal-ca.pem -CAkey internal-ca.key \
-CAcreateserial -out caddy-client.crt -days 365 -sha256
# 4. Buat CSR dan Kunci untuk Server Backend
openssl req -newkey rsa:2048 -nodes \
-keyout backend.key -out backend.csr \
-subj "/CN=secure-backend.internal/O=Applications"
# 5. Tandatangani Sertifikat Backend menggunakan Root CA yang sama (berlaku 1 tahun)
openssl x509 -req -in backend.csr -CA internal-ca.pem -CAkey internal-ca.key \
-CAcreateserial -out backend.crt -days 365 -sha256
Setelah file-file sertifikat ini terbentuk, tempatkan internal-ca.pem, caddy-client.crt, dan caddy-client.key di server Caddy, lalu tempatkan internal-ca.pem, backend.crt, dan backend.key di server backend.
Optimalisasi Connection Pool untuk Beban Tinggi (High Traffic Tuning) #
Pada server produksi dengan volume lalu lintas yang sangat tinggi, konfigurasi default sistem operasi dan Caddy dapat membatasi throughput data. Kita harus melakukan tuning pada batas jumlah koneksi dan buffer memori:
# Optimasi transport untuk traffic tinggi
traffic.example.com {
reverse_proxy backend-1:3000 backend-2:3000 {
transport http {
# Naikkan batas koneksi siaga (idle) per host
max_idle_conns_per_host 512
# Batasi jumlah koneksi total agar backend tidak kehabisan RAM
max_conns_per_host 1024
# Durasi socket idle dipertahankan
keep_alive 120s
# Tingkatkan ukuran buffer baca dan tulis untuk meminimalkan system call IO
read_buffer_size 16kb
write_buffer_size 16kb
}
}
}
max_idle_conns_per_host: Menaikkan nilai ini (default: 32) memastikan Caddy menyimpan lebih banyak koneksi TCP yang telah terbuka ke backend dalam status siaga. Ini sangat efektif untuk meredam lonjakan request mendadak tanpa membuang waktu untuk proses handshake TCP baru.read_buffer_sizedanwrite_buffer_size: Meningkatkan ukuran penyangga buffer dari 4KB ke 16KB mengurangi frekuensi pemanggilan sistem (system calls) baca/tulis disk, menurunkan utilisasi CPU server secara signifikan pada lalu lintas data besar.
FastCGI Transport untuk PHP-FPM #
Protokol FastCGI adalah protokol khusus yang digunakan untuk berkomunikasi dengan mesin pemroses bahasa pemrograman PHP (PHP-FPM). Berbeda dengan proxy HTTP biasa, Caddy harus menerjemahkan objek HTTP request menjadi format biner record FastCGI sebelum dikirimkan ke socket PHP-FPM.
flowchart LR
Klien["Klien (HTTP Request)"] ===>|Port 443| Caddy{"Caddy Proxy"}
subgraph FastCGI_Translation["Translasi Protocol"]
Caddy -->|Ekstrak Path & Env| FC["FastCGI Transport Module"]
end
FC ===>|Unix Socket / TCP| PHP["PHP-FPM Socket (:9000)"]
PHP --> Exec["Eksekusi script .php"]
style Caddy stroke:#0288d1,stroke-width:2px
style PHP stroke:#43a047,stroke-width:2pxPenerjemahan Parameter CGI Dinamis #
Format biner FastCGI mewajibkan pengiriman parameter lingkungan (environment variables) seperti SCRIPT_FILENAME agar PHP-FPM tahu berkas fisik mana yang harus dieksekusi, REQUEST_METHOD (GET/POST), dan QUERY_STRING. Caddy secara dinamis membaca request HTTP masuk dan memetakan nilai-nilai ini menggunakan Go text template ke biner FastCGI secara real-time.
Caddy menyediakan direktif tingkat tinggi php_fastcgi yang secara otomatis mengonfigurasi seluruh parameter transport FastCGI di belakang layar. Namun, kita juga bisa menuliskan konfigurasinya secara manual jika membutuhkan penyesuaian khusus:
# Konfigurasi manual transport FastCGI untuk PHP-FPM
php.example.com {
root * /var/www/my-php-app
# Kirim request ke socket unix PHP-FPM
reverse_proxy unix//run/php/php8.3-fpm.sock {
transport fastcgi {
# Tentukan root path aplikasi di dalam server
root /var/www/my-php-app
# Tentukan ekstensi pemisah berkas
split .php
# Suntikkan environment variables kustom untuk dibaca oleh PHP ($_SERVER)
env APP_ENV "production"
env DB_CONNECTION "mysql"
# Atur timeout pembacaan script PHP yang berjalan lama
read_timeout 60s
}
}
file_server
}
TCP Socket vs Unix Socket #
Saat menyambungkan Caddy dengan PHP-FPM, kita dihadapkan pada dua pilihan tipe soket koneksi:
- Unix Socket (
unix//run/php/php8.3-fpm.sock): Opsi terbaik jika Caddy dan PHP-FPM berjalan di dalam satu server fisik/VM yang sama. Jalur komunikasi Unix Socket tidak melewati tumpukan protokol jaringan (network stack TCP/IP) sistem operasi, sehingga memberikan latensi yang jauh lebih rendah dan performa throughput yang lebih tinggi. - TCP Socket (
localhost:9000atau10.0.0.12:9000): Opsi wajib jika PHP-FPM berjalan di dalam container Docker terpisah atau server fisik yang berbeda.
# Contoh perbandingan socket
example.com {
# ✓ BENAR & CEPAT untuk single-server: Unix Socket
php_fastcgi unix//run/php/php8.3-fpm.sock
# ✓ BENAR untuk containerized/Docker: TCP Socket
# php_fastcgi php-container:9000
}
Pemecahan Masalah Izin Unix Socket (Permission Denied) #
Masalah yang paling sering dialami ketika pertama kali menggunakan Unix Socket adalah kesalahan 502 Bad Gateway disertai log error permission denied pada Caddy. Hal ini terjadi karena berkas socket .sock dibuat oleh sistem operasi dengan kepemilikan user www-data (PHP-FPM) sementara proses Caddy berjalan sebagai user caddy (tanpa akses baca/tulis ke file tersebut).
Untuk memperbaikinya, edit konfigurasi PHP-FPM pool (biasanya di /etc/php/8.3/fpm/pool.d/www.conf):
; ANTI-PATTERN: Mengosongkan pemilik socket atau membatasi terlalu ketat
; listen.owner = www-data
; listen.group = www-data
; BENAR: Izinkan grup 'caddy' mengakses socket (atau berikan akses 0660)
listen.owner = www-data
listen.group = caddy
listen.mode = 0660
Setelah mengubah baris ini, lakukan restart layanan PHP-FPM (sudo systemctl restart php8.3-fpm) untuk menerapkan hak akses baru.
Proxying gRPC Services #
gRPC adalah framework komunikasi RPC modern berkinerja tinggi yang dikembangkan oleh Google. gRPC bekerja secara eksklusif menggunakan protokol HTTP/2 sebagai lapisan transportnya dan mengandalkan koneksi streaming dua arah yang berumur panjang.
Caddy mendukung penuh bertindak sebagai proxy untuk layanan gRPC. Kita harus menggunakan skema protokol h2c:// (HTTP/2 Cleartext) jika backend gRPC berjalan tanpa enkripsi TLS, atau menggunakan versions 2 pada transport HTTP jika backend mewajibkan HTTP/2 terenkripsi:
# Konfigurasi gRPC reverse proxy
grpc.example.com {
# h2c:// memberitahu Caddy untuk menggunakan HTTP/2 Cleartext tanpa enkripsi TLS ke backend
reverse_proxy h2c://grpc-backend:50051 {
transport http {
# Paksa Caddy hanya menggunakan HTTP/2 untuk koneksi ini
versions 2
# Pastikan dial timeout disesuaikan untuk streaming panjang
dial_timeout 5s
}
}
}
Mengapa HTTP/1.1 Tidak Cukup untuk gRPC? #
gRPC sangat mengandalkan fitur-fitur khusus HTTP/2 seperti bidirectional streaming (klien dan server dapat mengirim data secara konkuren bersamaan tanpa memutus koneksi) dan HTTP/2 Trailers.
Trailers adalah header HTTP tambahan yang baru dikirimkan setelah body response selesai dikirim. gRPC menggunakan trailers untuk mengirimkan status eksekusi program (grpc-status) dan pesan kesalahan (grpc-message). HTTP/1.1 tidak mendukung trailers ini secara asli. Oleh karena itu, proxy HTTP/1.1 tradisional akan memutus header ini, menyebabkan klien gRPC mendeteksi status kegagalan tak dikenal. Caddy menangani siklus HTTP/2 trailers ini secara transparan dan aman.
Ringkasan #
- Definisi Modul: Blok
transportmenentukan parameter fisik koneksi tingkat rendah ke backend, seperti tipe soket, batasan timeout, versi protokol, dan enkripsi.- Keamanan Internal: Selalu gunakan
tls_trusted_ca_certsdengan berkas CA tepercaya kita untuk mengamankan jaringan privat tanpa membuka celah serangan Man-in-the-Middle.- mTLS: Mutually trusted TLS (
tls_client_auth) digunakan untuk mengotentikasi identitas Caddy di tingkat backend, memastikan jalur data tertutup rapat dari luar.- Optimasi Kecepatan: Naikkan parameter
max_idle_conns_per_hostuntuk menyimpan koneksi siaga dalam jumlah besar di pool, memangkas overhead handshake TCP baru.- FastCGI & gRPC: Gunakan socket Unix untuk komunikasi lokal PHP-FPM yang lebih cepat, dan gunakan skema
h2c://denganversions 2untuk merutekan lalu lintas gRPC berbasis HTTP/2 secara native.