SSD Nodes Learn 🎉 VPS mulai $5.50/bln
Panduan Matt ConnorOleh Matt Connor · Diperbarui 2026-08-13

Nginx vs Caddy vs Traefik: Pilih Reverse Proxy

Bandingkan Nginx, Caddy, dan Traefik pada satu VPS dan satu IP publik: sertifikat TLS, biaya konfigurasi tiap 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 permintaan, 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 terasa ketika Anda membutuhkan sesuatu yang tidak dibahas dalam tutorial umum.

Pilih Caddy jika Anda ingin HTTPS ditangani secara otomatis dan service Anda merupakan aplikasi web biasa. Pilih Traefik jika semuanya berjalan dalam Docker Compose dan Anda menambahkan service baru setiap beberapa minggu. Pilih Nginx jika Anda sudah menggunakannya, atau jika Anda memerlukan caching respons, sertifikat klien, penerusan TCP mentah, atau konfigurasi besar yang sudah ada dan tidak ingin Anda tulis ulang.

Bagaimana masing-masing mendapatkan sertifikat TLS?

Faktor ini menjadi penentu bagi kebanyakan 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 menamai sebuah 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, menyediakan pengalihan HTTP ke HTTPS pada port 80, dan memperbarui sertifikat secara otomatis. Anda tidak memerlukan alat kedua atau timer untuk memeriksanya. Sertifikat disimpan di direktori data 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 self-signed di Ubuntu, tetapi pembaruannya ditangani secara otomatis.

Nginx tidak memiliki klien 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. Karena itu, ada dua komponen dan dua hal yang harus diverifikasi: systemctl list-timers | grep certbot menunjukkan bahwa timer tersedia, sedangkan sudo certbot renew --dry-run membuktikan bahwa proses pembaruan masih berfungsi. Langkah-langkahnya tersedia di Certbot di Ubuntu 24.04 dengan Nginx. Alat yang sama juga mendukung sertifikat wildcard melalui challenge DNS-01 jika Anda memiliki lebih banyak subdomain daripada yang ingin dicantumkan satu per satu.

Traefik memiliki klien ACME sendiri. Anda mengonfigurasi 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 pengguna selain pemiliknya. Traefik juga memberi tahu Anda sebelum menghapus resolver:

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 600

Mount sebuah direktori dan biarkan Traefik membuat file tersebut sendiri. Buat direktori itu terlebih dahulu dengan touch. Dengan demikian, direktori mewarisi umask Anda, yang biasanya membuat pemeriksaan tersebut berhasil.

Ada satu hal yang berlaku untuk ketiganya. Challenge HTTP-01 memerlukan port 80 yang dapat dijangkau dari Internet karena certificate authority akan terhubung kembali ke port tersebut. Jika hanya port 443 yang dibuka, penerbitan sertifikat gagal dengan pesan yang tampak seperti masalah DNS.

Tugas routing dua aplikasi yang sama dalam tiga konfigurasi

Tugasnya: 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 konfigurasi lengkapnya pada setiap proxy, sehingga perbedaan panjang konfigurasi terlihat langsung, 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 symlink, uji konfigurasi, muat ulang, 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.com

Perintah nginx -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 sekadar hiasan: ketika proxy_pass mengarah ke sebuah alamat, nginx secara default mengirim Host: 127.0.0.1:8080 ke upstream. Akibatnya, aplikasi yang membuat URL absolut dari header Host dapat mengarahkan pengguna ke localhost.

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 caddy

Itulah keseluruhan isi file tersebut. reverse_proxy menetapkan X-Forwarded-For, X-Forwarded-Proto, dan X-Forwarded-Host secara otomatis. Secara default, reverse_proxy mengabaikan nilai yang dikirim klien pada header tersebut, sehingga request tidak dapat memalsukan asalnya kepada backend Anda. Sertifikat, redirect dari port 80, dan pembaruan sertifikat semuanya ditentukan oleh kedua alamat situs tersebut. Tidak ada bagian lain dalam file yang memintanya.

Traefik

Traefik memerlukan konfigurasi statis sebelum dapat melakukan routing. Berikut konfigurasinya sebagai service Compose, dengan tag image 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:/letsencrypt

Setiap aplikasi kemudian memiliki routing sendiri melalui labels 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 jaringan Docker bersama. Aplikasi tidak memerlukan baris ports: sama sekali. Inilah manfaat utamanya: hanya Traefik yang dipublikasikan. Konfigurasi lengkapnya, termasuk jaringan bersama dan middleware redirect, tersedia di merouting beberapa aplikasi dengan Traefik dan Docker Compose.

Berapa banyak konfigurasi yang diperlukan setiap aplikasi tambahan?

ChartNon-blank config lines for the same two-app routing job
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 penghitungan dari blok-blok di atas. Blok server Nginx terdiri dari 11 baris non-kosong, dan Anda harus menuliskannya kembali untuk setiap hostname. Blok situs Caddy terdiri dari 3 baris. Traefik memerlukan 17 baris konfigurasi statis sebelum dapat melayani satu permintaan, lalu 5 label untuk setiap aplikasi.

Perhatikan komprominya, bukan pemenangnya. Traefik memerlukan konfigurasi paling banyak sebelum aplikasi pertama, tetapi paling sedikit untuk setiap aplikasi berikutnya. Kedua total tersebut bertemu sekitar situs ketiga. Di bawah jumlah itu, konfigurasi statis menjadi beban tambahan yang tidak diperlukan. Di atas jumlah itu, label menjadi lebih unggul dan terus unggul karena konfigurasi routing berada di samping service yang dirutekannya. Hapus service tersebut, lalu rutenya ikut terhapus. Inilah kelemahan file konfigurasi terpusat: menyisakan blok server yang sudah tidak diperlukan selama berbulan-bulan.

Jumlah baris juga membuat Nginx terlihat lebih sederhana. Setiap blok tersebut memerlukan symlink, sebuah nginx -t, reload, dan eksekusi certbot, sedangkan perubahan pada Caddy hanya memerlukan satu reload dan perubahan pada Traefik tidak memerlukan perintah sama sekali. Ketiganya dapat melakukan reload tanpa memutus koneksi aktif. Perbedaannya terletak pada jumlah langkah terpisah yang harus Anda ingat pada pukul satu dini hari.

Yang mana yang mengetahui container Anda?

Traefik memantau Docker socket dan membuat router dari 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 shared Docker network yang juga terhubung ke proxy.

Fitur tersebut memiliki konsekuensi keamanan yang perlu dijelaskan secara langsung. Traefik membaca /var/run/docker.sock. Siapa pun yang dapat mengakses socket tersebut dapat menjalankan container dengan filesystem host di-mount ke dalamnya. Akses ini setara dengan root pada host. Mount read-only mengurangi risiko, tetapi tidak menghilangkannya. Jika hal ini relevan dengan threat model 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. Selanjutnya, Anda bertanggung jawab atas build dan 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, dan nginx tidak meneruskan header hop-by-hop ke upstream kecuali Anda menginstruksikannya.

# /etc/nginx/conf.d/upgrade-map.conf
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

Kemudian, di dalam blok location, terdapat tiga baris yang semuanya wajib ada:

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;

Jika baris tersebut dihilangkan, 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 menimbulkan 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 sekaligus dalam beberapa burst sampai Anda menetapkan proxy_buffering off; pada lokasi tersebut, karena nginx menahan respons di buffer sementara halaman menunggunya.

Caddy melakukan upgrade dan mengubah koneksi menjadi tunnel dua arah tanpa directive apa pun. Caddy juga langsung melakukan flush ketika respons berstatus text/event-stream atau panjangnya tidak diketahui, sehingga streaming dapat berjalan tanpa konfigurasi tambahan. Traefik meneruskan upgrade dan tidak melakukan buffering pada respons kecuali Anda menambahkan middleware buffering secara manual. Jika layanan Anda mencakup chat, terminal web, tail log, atau dashboard live, perbedaannya akan terlihat dari jumlah konfigurasi yang perlu 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 dalam 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 umum?

Di sinilah Nginx membenarkan baris konfigurasi tambahannya.

  • Sertifikat klien, yang juga disebut mTLS (mutual TLS), mengharuskan klien mengirimkan sertifikat. Nginx memerlukan ssl_client_certificate /etc/ssl/ca.pem; dan ssl_verify_client on; di dalam blok server. Caddy memerlukan blok client_auth di dalam tls. Label Traefik sama sekali tidak dapat menyatakannya: Anda menentukan opsi TLS dalam file provider, lalu mengarahkan router ke opsi tersebut dengan traefik.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 berisi client intended to send too large body. Naikkan client_max_body_size. Secara default, Caddy dan Traefik tidak menetapkan batas body, sehingga request diteruskan ke aplikasi dan batas yang ditetapkan aplikasi akan menentukan hasilnya.
  • Caching response. Nginx memiliki proxy_cache dan fitur ini sudah matang. Caddy memerlukan plugin yang dikompilasi ke dalamnya. Build open source Traefik sama sekali tidak memiliki HTTP cache. Hal ini sering mengejutkan orang yang menganggap 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 lainnya.
  • Web server yang sudah berada di belakang proxy. Jika layanannya adalah aplikasi PHP klasik, stack LAMP pada Ubuntu 24.04 sudah mencakup Apache. Menambahkan proxy di depannya berarti ada dua tempat yang menetapkan header dan dua tempat yang dapat menulis ulang URL. Tentukan salah satu yang menangani TLS termination, lalu jalankan yang lain dengan HTTP biasa pada alamat loopback.

Jebakan firewall setelah pilihan ini

Tujuan reverse proxy adalah hanya port 80 dan 443 yang terbuka. Docker membatalkan pembatasan itu secara diam-diam. 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, dan aplikasi Anda berada di Internet publik bersama proxy yang telah dikonfigurasi dengan cermat. Ikat port yang dipublikasikan ke loopback dengan 127.0.0.1:8080:80, atau hapus ports: sepenuhnya dan biarkan proxy mengakses container melalui jaringan Docker. Itulah yang dilakukan 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:8080

Connection refused atau timeout adalah hasil yang diharapkan. Respons HTTP berarti aplikasi tersebut dapat diakses tanpa melalui proxy. Semua konfigurasi yang Anda terapkan di atas tidak memberikan perlindungan.

Proxy mana yang sebaiknya dipilih?

Sebagian besar situs statis, ditambah satu atau dua aplikasi: Caddy. HTTPS otomatis menghilangkan pekerjaan berulang 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. Kekurangannya adalah lebih sedikit referensi siap salin-tempel ketika terjadi masalah yang tidak biasa.

Homelab docker-compose yang terus Anda tambahi: Traefik. Setelah layanan ketiga, penggunaan label lebih praktis daripada mengedit file terpusat, dan layanan yang dihapus akan membawa rutenya sendiri. Sisihkan 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 error parsing sebelum menyimpulkan bahwa aplikasi rusak.

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. Kekurangannya, 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 interface 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 Anda tambahkan sesekali, Traefik sepadan digunakan karena setiap aplikasi memiliki label routing sendiri dan Anda tidak perlu mengedit file pusat. Jika service sudah stabil dan tujuan utama Anda adalah agar HTTPS tidak lagi menjadi beban, Caddy lebih mudah dipelajari dan lebih kecil risikonya untuk salah konfigurasi. Pilih Nginx jika Anda sudah menguasainya, atau jika Anda memerlukan fitur yang tidak tersedia pada dua pilihan lainnya, seperti caching respons atau listener TCP biasa.

Apakah Caddy benar-benar tidak memerlukan konfigurasi sertifikat?

Untuk kasus umum, ya. Menetapkan hostname publik sebagai alamat situs sudah mencakup seluruh konfigurasi: Caddy meminta sertifikat melalui ACME, melayani redirect dari port 80, dan memperbaruinya sebelum kedaluwarsa. 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 me-resolve nama tersebut dan terhubung kembali ke VPS.

Dapatkah saya menjalankan Nginx dan Traefik pada VPS yang sama?

Tidak pada port yang sama. Proxy yang dijalankan 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 berhenti. Jalankan satu proxy pada port 80 dan 443, lalu tempatkan semua layanan lainnya di belakangnya. Jika Anda sedang melakukan migrasi, pindahkan hostname satu per satu: biarkan proxy depan meneruskan koneksi 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 lokasi tersebut dengan proxy_read_timeout 3600s;, atau minta aplikasi mengirim frame ping setiap 30 detik. Caddy dan Traefik tidak menutup koneksi idle yang telah di-upgrade berdasarkan timer satu menit. Karena itu, aplikasi yang sama dapat terlihat stabil di belakang keduanya, tetapi tidak stabil di belakang Nginx.