Nginx vs Caddy vs Traefik: Pilih Reverse Proxy
Bandingkan Nginx, Caddy, dan Traefik pada satu VPS dan satu IP publik: sertifikat TLS, biaya konfigurasi per aplikasi, WebSocket, serta routing Docker.
Nginx vs Caddy vs Traefik: jawaban singkat
Nginx, Caddy, dan Traefik menjalankan fungsi yang sama sebagai reverse proxy: mendengarkan pada port 443, membaca hostname dalam setiap request, lalu meneruskannya ke service yang tepat pada VPS Anda. Ketiganya dapat menempatkan empat aplikasi self-hosted di balik satu alamat IP publik, dan semuanya cukup cepat sehingga aplikasi Anda akan menjadi bagian yang lebih lambat. Perbedaannya terletak pada cara masing-masing memperoleh sertifikat TLS (transport layer security) dan seberapa besar konfigurasi tambahan yang diperlukan untuk setiap aplikasi baru. Perbedaan lainnya baru terlihat ketika Anda membutuhkan sesuatu yang tidak dibahas dalam tutorial umum.
Pilih Caddy jika Anda ingin HTTPS ditangani secara otomatis dan service Anda adalah aplikasi web biasa. Pilih Traefik jika seluruh sistem berjalan dalam Docker Compose dan Anda menambahkan service baru setiap beberapa minggu. Pilih Nginx jika Anda sudah menggunakannya, atau jika Anda memerlukan caching response, sertifikat client, penerusan TCP mentah, atau konfigurasi besar yang sudah ada dan tidak ingin Anda tulis ulang.
Bagaimana masing-masing memperoleh sertifikat TLS?
Sumbu ini menjadi penentu bagi sebagian besar orang, jadi mulai dari sini. Ketiganya pada akhirnya menyimpan sertifikat yang sama dari otoritas yang sama. Namun, langkah untuk mendapatkannya berbeda.
Caddy meminta sertifikat karena Anda mencantumkan hostname. Tulis app.example.com sebagai alamat situs. Caddy kemudian meminta sertifikat melalui ACME (automatic certificate management environment) dari Let's Encrypt, beralih ke ZeroSSL jika permintaan tersebut gagal, melayani pengalihan HTTP ke HTTPS pada port 80, dan memperbarui sertifikat secara otomatis. Anda tidak memerlukan tool kedua atau timer untuk memeriksanya. Sertifikat disimpan di direktori data milik pengguna caddy, yaitu /var/lib/caddy/.local/share/caddy pada instalasi paket. Tambahkan path tersebut ke backup Anda, atau terima penerbitan sertifikat baru setelah rebuild. Untuk hostname yang tidak bersifat publik, tls internal menandatangani sertifikat menggunakan certificate authority lokal milik Caddy. Hasilnya sama seperti membuat sertifikat yang ditandatangani sendiri di Ubuntu, tetapi pembaruannya ditangani secara otomatis.
Nginx tidak memiliki client ACME. Certbot memperoleh sertifikat tersebut, dan pluginnya, --nginx, menulis ulang server block Anda untuk menambahkan listener 443 dan pengalihan. Pembaruan berjalan melalui systemd timer yang dipasang oleh paket tersebut. Jadi, ada dua komponen yang perlu diperhatikan dan dua hal yang perlu diverifikasi: systemctl list-timers | grep certbot menunjukkan bahwa timer tersebut tersedia, sedangkan sudo certbot renew --dry-run membuktikan bahwa jalur pembaruan masih berfungsi. Langkah-langkahnya tersedia di Certbot pada Ubuntu 24.04 dengan Nginx. Tool yang sama juga menangani sertifikat wildcard melalui challenge DNS-01 jika Anda memiliki lebih banyak subdomain daripada yang ingin dicantumkan satu per satu.
Traefik memiliki client ACME sendiri. Konfigurasikan satu certificate resolver dalam konfigurasi statis. Setelah itu, setiap router dapat menggunakannya. Seluruh state, termasuk account key dan sertifikat, disimpan dalam satu file acme.json. Traefik menolak menggunakan file tersebut jika file dapat dibaca oleh siapa pun selain pemiliknya. Traefik akan memberi tahu Anda sebelum menghapus resolver tersebut:
The ACME resolver "le" is skipped from the resolvers list because: unable to get ACME account: permissions 660 for /letsencrypt/acme.json are too open, please use 600Mount sebuah direktori dan biarkan Traefik membuat file tersebut sendiri. Buat direktori itu terlebih dahulu dengan touch. Dengan demikian, direktori tersebut mewarisi umask Anda, yang biasanya menyebabkan baris tersebut berfungsi bagi sebagian besar pengguna.
Ada satu hal yang berlaku untuk ketiganya. Challenge HTTP-01 memerlukan port 80 agar dapat dijangkau dari Internet karena certificate authority akan terhubung kembali ke port tersebut. Jika Anda hanya membuka port 443, penerbitan sertifikat gagal dengan gejala yang terlihat seperti masalah DNS.
Pekerjaan routing dua aplikasi yang sama dalam tiga konfigurasi
Pekerjaannya: app.example.com diarahkan ke service pada 127.0.0.1:8080, sedangkan files.example.com diarahkan ke service pada 127.0.0.1:8081, keduanya melalui HTTPS. Berikut seluruh konfigurasi untuk masing-masing proxy, sehingga perbedaan panjang konfigurasi dapat terlihat, bukan sekadar dinyatakan.
Nginx
# /etc/nginx/sites-available/app.example.com
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}Selanjutnya, buat tautan untuk file tersebut, lakukan pengujian, muat ulang konfigurasi, lalu tambahkan sertifikat.
sudo ln -s /etc/nginx/sites-available/app.example.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
sudo certbot --nginx -d app.example.comnginx -t yang mencetak syntax is ok dan test is successful adalah pemeriksaan yang harus dijalankan sebelum setiap reload. Aplikasi kedua menggunakan blok yang sama, dengan hostname dan port yang diubah. Baris proxy_set_header bukan hiasan: ketika proxy_pass menentukan sebuah alamat, nginx secara default mengirim Host: 127.0.0.1:8080 ke upstream. Akibatnya, aplikasi yang membuat URL absolut dari header Host akan mengarahkan pengguna ke localhost. Fungsi setiap dari keempat header tersebut, serta alasan trailing slash pada proxy_pass mengubah path yang diterima aplikasi tanpa pesan yang jelas, dibahas directive demi directive dalam panduan server block nginx ini.
Caddy
app.example.com {
reverse_proxy 127.0.0.1:8080
}
files.example.com {
reverse_proxy 127.0.0.1:8081
}sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddyItulah seluruh isi file tersebut. reverse_proxy mengatur X-Forwarded-For, X-Forwarded-Proto, dan X-Forwarded-Host sendiri. Secara default, Caddy mengabaikan nilai yang dikirim client dalam header tersebut, sehingga request tidak dapat memalsukan asalnya kepada backend. Sertifikat, redirect dari port 80, dan renewal semuanya mengikuti kedua alamat situs tersebut. Tidak ada konfigurasi lain dalam file ini yang memintanya.
Traefik
Traefik memerlukan konfigurasi statis sebelum dapat melakukan routing. Sebagai service Compose, dengan image tag yang berlaku pada Agustus 2026:
services:
traefik:
image: traefik:v3.7
command:
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.le.acme.email=you@example.com"
- "--certificatesresolvers.le.acme.storage=/letsencrypt/acme.json"
- "--certificatesresolvers.le.acme.httpchallenge.entrypoint=web"
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./letsencrypt:/letsencryptSelanjutnya, setiap aplikasi memiliki routing sendiri melalui label, dalam file compose masing-masing:
labels:
- "traefik.enable=true"
- "traefik.http.routers.app.rule=Host(`app.example.com`)"
- "traefik.http.routers.app.entrypoints=websecure"
- "traefik.http.routers.app.tls.certresolver=le"
- "traefik.http.services.app.loadbalancer.server.port=8080"loadbalancer.server.port adalah port di dalam container, bukan port yang dipublikasikan, karena Traefik mengakses container melalui shared Docker network. Aplikasi tersebut sama sekali tidak memerlukan baris ports:, dan inilah manfaat utamanya: hanya Traefik yang dipublikasikan. Konfigurasi lengkapnya, termasuk shared network dan redirect middleware, tersedia dalam routing beberapa aplikasi dengan Traefik dan Docker Compose.
Berapa banyak konfigurasi yang diperlukan setiap aplikasi tambahan?
The data behind this chart
[
{
"tool": "Nginx",
"proxy_setup_lines": 0,
"lines_per_app": 11
},
{
"tool": "Caddy",
"proxy_setup_lines": 0,
"lines_per_app": 3
},
{
"tool": "Traefik",
"proxy_setup_lines": 17,
"lines_per_app": 5
}
]Berdasarkan blok-blok di atas, server block Nginx terdiri dari 11 baris yang tidak kosong, dan Anda menulisnya kembali untuk setiap hostname. Site block Caddy terdiri dari 3 baris. Traefik memerlukan 17 baris konfigurasi statis sebelum dapat melayani satu request, kemudian 5 label untuk setiap aplikasi.
Pahami perbandingannya, bukan hanya pemenangnya. Traefik memerlukan konfigurasi paling banyak sebelum aplikasi pertama, tetapi paling sedikit untuk setiap aplikasi berikutnya. Kedua total tersebut bertemu sekitar site ketiga. Di bawah jumlah itu, konfigurasi statis menjadi overhead yang sebenarnya tidak diperlukan. Di atas jumlah itu, label menjadi lebih unggul dan terus bertambah keunggulannya karena routing berada di dekat service yang dirutekannya. Hapus service tersebut, dan route-nya ikut terhapus. Inilah kelemahan central config file: server block lama tetap tertinggal untuk aplikasi yang sudah tidak ada selama berbulan-bulan.
Jumlah baris juga membuat Nginx tampak lebih sederhana. Setiap block memerlukan symlink, nginx -t, reload, dan eksekusi certbot, sedangkan pengeditan Caddy memerlukan satu reload dan pengeditan Traefik tidak memerlukan command sama sekali. Ketiganya dapat melakukan reload tanpa memutus koneksi yang sedang aktif. Perbedaannya terletak pada jumlah langkah terpisah yang harus Anda ingat pada pukul satu pagi.
Mana yang mengetahui container Anda?
Traefik memantau Docker socket dan membuat router berdasarkan label container saat container dijalankan dan dihentikan. Tidak ada komponen lain di sini yang melakukan hal tersebut. Nginx dan Caddy memerlukan pengeditan konfigurasi dan reload saat container baru muncul. Keduanya juga memerlukan alamat yang dapat dijangkau: port yang dipublikasikan pada loopback, atau jaringan Docker bersama yang juga terhubung ke proxy.
Fitur tersebut memiliki konsekuensi, dan hal ini perlu dinyatakan secara jelas. Traefik membaca /var/run/docker.sock. Siapa pun yang dapat berkomunikasi dengan socket tersebut dapat menjalankan container dengan filesystem host yang di-mount di dalamnya, sehingga memperoleh akses root pada host. Memasang socket dalam mode read-only mengurangi risiko tanpa menghilangkannya. Jika hal ini relevan dengan model ancaman Anda, tempatkan socket proxy di antaranya. Proxy tersebut hanya boleh mengekspos endpoint daftar container yang diperlukan Traefik.
Caddy dapat melakukan discovery berbasis label melalui plugin komunitas. Namun, plugin Caddy dikompilasi ke dalam binary. Jadi, Anda harus membuat binary khusus atau image khusus dengan xcaddy, lalu mengelola build tersebut beserta pembaruannya. Untuk tiga atau empat service, mengedit Caddyfile membutuhkan lebih sedikit pekerjaan.
WebSocket dan streaming: apa yang rusak dan alasannya
Nginx yang memerlukan konfigurasi tambahan. Koneksi WebSocket dimulai sebagai permintaan HTTP yang membawa Upgrade: websocket, tetapi nginx tidak meneruskan header hop-by-hop ke upstream kecuali Anda mengaturnya.
# /etc/nginx/conf.d/upgrade-map.conf
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}Kemudian, di dalam blok location, tiga baris berikut harus ada semuanya:
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;Jika baris tersebut tidak ada, konsol browser menampilkan WebSocket connection to 'wss://app.example.com/ws' failed, sedangkan log backend menunjukkan GET biasa. map diperlukan karena Connection: upgrade yang ditulis secara hardcode akan dikirim pada setiap permintaan, termasuk permintaan biasa yang seharusnya menyatakan close.
Ada dua default Nginx lain yang dapat menyebabkan masalah. proxy_read_timeout bernilai 60 detik dan berlaku untuk tunnel setelah upgrade, sehingga WebSocket tanpa trafik selama satu menit akan ditutup oleh proxy. Server-sent events juga tiba terlambat atau secara berkelompok hingga Anda menetapkan proxy_buffering off; pada lokasi tersebut, karena nginx menahan respons di buffernya sementara halaman Anda menunggunya.
Caddy melakukan upgrade dan mengubah koneksi menjadi tunnel dua arah tanpa direktif apa pun. Caddy juga langsung melakukan flush ketika responsnya text/event-stream atau tidak memiliki panjang yang diketahui, sehingga streaming dapat berfungsi tanpa perubahan. Traefik meneruskan upgrade dan tidak melakukan buffering terhadap respons kecuali Anda menambahkan middleware buffering sendiri. Jika layanan Anda mencakup chat, terminal web, tail log, atau dashboard live, perbedaan ini memengaruhi jumlah konfigurasi yang harus Anda tulis dan debug.
Blok server Nginx lengkap, termasuk WebSocket dan SSE
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
server_name app.example.com;
client_max_body_size 64m;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 3600s;
proxy_buffering off;
}
}map berada dalam konteks http, bukan di dalam server, jadi simpan di file tersendiri di bawah /etc/nginx/conf.d/. Nonaktifkan proxy_buffering hanya pada lokasi yang melakukan streaming, karena buffering memungkinkan nginx membebaskan worker backend lebih awal pada respons biasa. Certbot menulis ulang blok ini saat Anda menjalankannya, jadi baca kembali file tersebut setelahnya.
Apa yang terjadi jika Anda memerlukan sesuatu yang tidak biasa?
Di sinilah Nginx menunjukkan keunggulan dari baris konfigurasi tambahannya.
- Sertifikat klien, yang juga disebut mTLS (mutual TLS), mengharuskan klien menyertakan sertifikat. Nginx memerlukan
ssl_client_certificate /etc/ssl/ca.pem;danssl_verify_client on;di dalam blok server. Caddy memerlukan blokclient_authdi dalamtls. Label Traefik sama sekali tidak dapat mengekspresikannya: Anda harus menentukan opsi TLS dalam file provider, lalu mengarahkan router ke opsi tersebut dengantraefik.http.routers.app.tls.options=mtls@file. Model yang menempatkan semua konfigurasi dalam label memiliki pengecualian saat Anda pertama kali memerlukan fitur ini. - Upload berukuran besar. Secara default, Nginx membatasi body request hingga 1 MB. Upload yang lebih besar menghasilkan
413 Request Entity Too Large, dan error log mencatatclient intended to send too large body. Naikkan nilainya denganclient_max_body_size. Secara default, Caddy dan Traefik tidak menetapkan batas body, sehingga request diteruskan ke aplikasi dan batas yang ditetapkan aplikasi tersebut yang menentukan. - Caching response. Nginx memiliki
proxy_cachedan fiturnya sudah matang. Caddy memerlukan plugin yang dikompilasi ke dalam binary. Build open source Traefik sama sekali tidak memiliki HTTP cache. Hal ini mengejutkan pengguna yang berasumsi bahwa setiap proxy melakukan caching. - TCP atau UDP mentah, misalnya untuk port database atau game server. Nginx memiliki modul
stream. Traefik memiliki router TCP dan UDP pada entrypoint masing-masing. Caddy memerlukan plugin lain, sehingga Anda harus membuat build kustom lain. - Web server yang sudah berada di belakang proxy. Jika service tersebut adalah aplikasi PHP klasik, stack LAMP pada Ubuntu 24.04 sudah menyertakan Apache. Menambahkan proxy di depannya berarti ada dua tempat yang menetapkan header dan dua tempat yang dapat menulis ulang URL. Tentukan komponen yang melakukan terminasi TLS, lalu jalankan komponen lainnya menggunakan HTTP biasa yang terikat ke loopback.
Jebakan firewall setelah pilihan ini
Tujuan reverse proxy adalah hanya port 80 dan 443 yang terbuka. Docker dapat membatalkan tujuan ini tanpa terlihat. Mempublikasikan port dengan -p 8080:80 menulis aturan DNAT ke dalam tabel nat. Aturan tersebut dievaluasi sebelum aturan INPUT yang dikelola ufw. Karena itu, ufw deny 8080 tidak memblokirnya. Akibatnya, aplikasi Anda berada di Internet publik berdampingan dengan proxy yang telah dikonfigurasi dengan cermat. Bind port yang dipublikasikan ke loopback dengan 127.0.0.1:8080:80. Alternatifnya, hapus ports: sepenuhnya dan biarkan proxy mengakses container melalui jaringan Docker, seperti pada contoh Traefik di atas. Mekanisme dan solusinya dijelaskan dalam alasan port yang dipublikasikan Docker melewati ufw.
Uji dari mesin yang bukan VPS tersebut. Pemeriksaan yang dijalankan langsung pada server selalu berhasil:
curl --max-time 5 http://your.server.address:8080Connection refused atau timeout adalah hasil yang diharapkan. Respons HTTP berarti aplikasi tersebut dapat diakses tanpa melalui proxy. Dengan demikian, semua konfigurasi yang Anda buat di atas tidak memberikan perlindungan.
Proxy mana yang harus dipilih?
Sebagian besar situs statis, ditambah satu atau dua aplikasi: Caddy. HTTPS otomatis menghilangkan pekerjaan rutin terbesar yang harus Anda lakukan. Konfigurasinya cukup singkat untuk dibaca dalam satu layar, dan situs statis hanya memerlukan satu baris root serta satu baris file_server di dalam blok situs yang sama. Konsekuensinya, pilihan solusi siap salin-tempel lebih sedikit ketika terjadi masalah yang tidak biasa.
Homelab docker-compose yang terus Anda tambahkan layanannya: Traefik. Setelah layanan ketiga, penggunaan label lebih praktis daripada mengedit satu file pusat, dan layanan yang dihapus akan sekaligus membawa rutenya. Sediakan waktu satu sore untuk penyiapan pertama karena entrypoint, router, service, dan middleware merupakan istilah baru. Kesalahan ketik pada label biasanya muncul sebagai 404 dari Traefik, bukan sebagai kegagalan saat start. Karena itu, baca docker logs traefik untuk melihat kesalahan parsing sebelum menyimpulkan bahwa aplikasi bermasalah.
Konfigurasi Nginx yang sudah ada, atau persyaratan apa pun dari daftar di atas: Nginx. Nginx sudah menyediakan solusi untuk caching respons dan sertifikat klien, dan hampir semua panduan pihak ketiga mengasumsikan penggunaannya. Konsekuensinya, sertifikat dan dukungan websocket harus Anda konfigurasi sendiri, bukan fitur yang langsung tersedia.
Satu aturan berlaku apa pun pilihan Anda. Tepat satu proses mendengarkan pada antarmuka publik, sedangkan semua proses lainnya mendengarkan pada loopback atau jaringan Docker privat.
FAQ
Reverse proxy mana yang terbaik untuk beberapa aplikasi Docker pada satu VPS?
Untuk tiga atau empat service yang sesekali Anda tambahkan, Traefik sepadan digunakan karena setiap aplikasi memiliki routing label sendiri dan Anda tidak perlu mengubah file pusat. Jika service sudah stabil dan tujuan utama Anda adalah agar HTTPS tidak lagi menjadi masalah, Caddy lebih mudah dipelajari dan lebih kecil risikonya untuk rusak. Pilih Nginx jika Anda sudah mengenalnya atau jika memerlukan fitur yang tidak tersedia pada dua pilihan lain, seperti caching respons atau listener TCP biasa.
Apakah Caddy benar-benar tidak memerlukan konfigurasi sertifikat?
Untuk penggunaan normal, ya. Menetapkan hostname publik sebagai alamat situs sudah merupakan seluruh konfigurasi: Caddy meminta sertifikat melalui ACME, melayani redirect dari port 80, dan memperbaruinya sebelum masa berlaku berakhir. Namun, dua hal tetap harus terpenuhi. Port 80 harus dapat dijangkau dari Internet untuk challenge HTTP-01, dan record DNS A atau AAAA hostname tersebut harus sudah mengarah ke VPS karena certificate authority akan melakukan resolusi nama lalu terhubung kembali ke VPS itu.
Dapatkah saya menjalankan Nginx dan Traefik pada VPS yang sama?
Tidak pada port yang sama. Proxy yang start kedua akan gagal melakukan bind, dan nginx menampilkan bind() to 0.0.0.0:443 failed (98: Address already in use), sedangkan Traefik mencatat error bind serupa lalu keluar. Jalankan satu proxy pada port 80 dan 443, lalu tempatkan semua layanan lain di belakangnya. Jika Anda sedang melakukan migrasi, pindahkan hostname satu per satu: biarkan front proxy meneruskan request ke proxy lama melalui port loopback sampai situs terakhir dipindahkan.
Mengapa websocket saya terputus setelah 60 detik di belakang Nginx?
proxy_read_timeout secara default bernilai 60 detik dan berlaku pada tunnel setelah proses upgrade selesai. Karena itu, koneksi tanpa trafik selama satu menit akan ditutup oleh proxy, bukan oleh aplikasi Anda. Naikkan nilainya pada location tersebut menggunakan proxy_read_timeout 3600s;, atau minta aplikasi mengirimkan ping frame setiap 30 detik. Caddy dan Traefik tidak menutup koneksi hasil upgrade yang idle berdasarkan timer satu menit. Karena itu, aplikasi yang sama dapat terlihat stabil di belakang keduanya tetapi tidak stabil di belakang Nginx.