SSD Nodes Learn Hosting plans →
Panduan Matt ConnorOleh Matt Connor · Dikemas kini 2026-08-29

Nginx vs Caddy vs Traefik: Mana Satu Pilihan Terbaik?

Bandingkan Nginx, Caddy dan Traefik untuk VPS anda. Ketahui perbezaan pengurusan sijil TLS, konfigurasi Docker, sokongan websocket serta prestasi untuk aplikasi web anda.

Nginx vs Caddy vs Traefik: jawapan ringkas

Nginx, Caddy dan Traefik melakukan tugas yang sama sebagai reverse proxy: mendengar pada port 443, membaca hostname dalam setiap permintaan, dan menghalakannya ke servis yang betul pada VPS anda. Mana-mana daripada ketiga-tiganya mampu meletakkan empat aplikasi self-hosted di sebalik satu alamat IP awam, dan kesemuanya cukup pantas sehingga aplikasi anda akan menjadi bahagian yang lebih perlahan. Perbezaannya terletak pada cara setiap satu memperoleh sijil TLS (transport layer security) dan berapa banyak konfigurasi yang diperlukan oleh setiap aplikasi tambahan. Perbezaan lain akan muncul kemudian, pada hari anda memerlukan sesuatu yang tidak disentuh oleh tutorial biasa.

Pilih Caddy jika anda mahu HTTPS diuruskan untuk anda dan servis anda adalah aplikasi web biasa. Pilih Traefik jika semuanya berjalan dalam Docker Compose dan anda menambah servis baharu setiap beberapa minggu. Pilih Nginx jika anda sudah menggunakannya, atau jika anda memerlukan response caching, client certificates, raw TCP forwarding, atau konfigurasi sedia ada yang besar yang anda tidak mahu tulis semula.

Bagaimanakah setiap satunya memperoleh sijil TLS?

Paksi ini menentukan pilihan bagi kebanyakan orang, jadi mulakan di sini. Ketiga-tiganya akhirnya memegang sijil yang sama daripada pihak berkuasa yang sama. Usaha yang anda lakukan untuk mencapainya tidak sama.

Caddy meminta sijil kerana anda menamakan hostname. Tulis app.example.com sebagai alamat tapak dan Caddy akan meminta sijil melalui ACME (automatic certificate management environment) daripada Let's Encrypt, beralih kepada ZeroSSL jika permintaan gagal, menyediakan redirect HTTP ke HTTPS pada port 80, dan melakukan pembaharuan secara automatik. Tiada alat kedua dan tiada pemasa untuk diperiksa. Sijil disimpan dalam direktori data pengguna caddy, /var/lib/caddy/.local/share/caddy bagi pemasangan pakej, jadi tambahkan laluan tersebut ke dalam sandaran anda atau terima pengeluaran sijil baharu selepas pembinaan semula. Bagi hostname yang tidak awam, tls internal menandatangani sijil dengan pihak berkuasa sijil tempatan Caddy sendiri. Ini memberikan hasil yang sama seperti mencipta sijil self-signed pada Ubuntu, dengan pembaharuan diuruskan untuk anda.

Nginx tidak mempunyai klien ACME. Certbot memperoleh sijil tersebut, dan pemalam --nginx miliknya menulis semula blok pelayan anda untuk menambah listener 443 dan redirect. Pembaharuan dijalankan daripada pemasa systemd yang dipasang oleh pakej, jadi terdapat dua bahagian yang bergerak dan dua perkara untuk disahkan: systemctl list-timers | grep certbot menunjukkan pemasa tersebut wujud, dan sudo certbot renew --dry-run membuktikan laluan pembaharuan masih berfungsi. Langkah demi langkah terdapat dalam Certbot pada Ubuntu 24.04 dengan Nginx, dan alat yang sama meliputi sijil wildcard melalui cabaran DNS-01 apabila anda mempunyai lebih banyak subdomain daripada yang anda ingin senaraikan.

Traefik membawa klien ACME sendiri. Anda mengkonfigurasi satu penyelesai sijil (certificate resolver) dalam konfigurasi statik, dan setiap router kemudiannya boleh menggunakannya. Semua keadaan, termasuk kunci akaun dan sijil, terletak dalam satu fail acme.json. Traefik enggan menggunakan fail tersebut jika ia boleh dibaca oleh sesiapa sahaja selain pemiliknya, dan ia akan memberitahu anda perkara itu sebelum ia menggugurkan penyelesai 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 600

Mount satu direktori dan biarkan Traefik mencipta fail tersebut sendiri. Jika anda menciptanya dahulu dengan touch, ia akan mewarisi umask anda, yang merupakan punca kebanyakan orang menghadapi ralat tersebut.

Satu perkara adalah benar untuk ketiga-tiganya. Cabaran HTTP-01 memerlukan port 80 boleh dicapai dari internet, kerana pihak berkuasa sijil akan menyambung semula ke port tersebut. Jika anda hanya membuka port 443, pengeluaran sijil akan gagal dengan mesej yang kelihatan seperti ralat DNS.

Tugas penghalaan dua aplikasi yang sama dalam tiga konfigurasi

Tugasnya: app.example.com dihalakan ke servis pada 127.0.0.1:8080, files.example.com dihalakan ke servis pada 127.0.0.1:8081, kedua-duanya melalui HTTPS. Berikut adalah keseluruhan konfigurasi bagi setiap proksi, supaya perbezaan tahap verbositi dapat dilihat dengan jelas dan bukan sekadar dakwaan.

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;
    }
}

Kemudian pautkan fail tersebut, uji, muat semula, dan tambah sijil.

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

nginx -t yang mencetak syntax is ok dan test is successful ialah semakan yang perlu dijalankan sebelum setiap muat semula. Aplikasi kedua menggunakan blok yang sama dengan hostname dan port yang telah diubah. Baris proxy_set_header bukanlah hiasan: apabila proxy_pass menamakan alamat, nginx menghantar Host: 127.0.0.1:8080 ke upstream secara lalai, jadi aplikasi yang membina URL mutlak daripada header Host akan menghantar pengguna anda ke localhost. Fungsi setiap empat header tersebut, dan sebab garis miring (trailing slash) pada proxy_pass mengubah laluan yang diterima oleh aplikasi anda secara senyap, dihuraikan secara terperinci mengikut direktif dalam panduan blok pelayan 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 caddy

Itu adalah keseluruhan fail tersebut. reverse_proxy menetapkan X-Forwarded-For, X-Forwarded-Proto dan X-Forwarded-Host secara automatik, dan secara lalai ia mengabaikan apa sahaja yang dihantar oleh klien dalam header tersebut, jadi permintaan tidak boleh menipu backend anda tentang asal usulnya. Sijil, redirect port 80 dan pembaharuan semuanya terhasil daripada dua alamat tapak tersebut. Tiada perkara lain dalam fail yang memintanya.

Traefik

Traefik memerlukan konfigurasi statik sebelum ia menghalakan sebarang trafik. Sebagai servis Compose, dengan tag imej terkini setakat Ogos 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 membawa konfigurasi penghalaannya sendiri, melalui label, dalam fail 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 ialah port di dalam kontena, bukan port yang diterbitkan, kerana Traefik mencapai kontena tersebut melalui rangkaian Docker yang dikongsi. Aplikasi tidak memerlukan baris ports: langsung, dan itulah kelebihan sebenar: hanya Traefik yang diterbitkan. Binaan penuh, termasuk rangkaian yang dikongsi dan middleware redirect, terdapat dalam penghalaan berbilang aplikasi dengan Traefik dan Docker Compose.

Berapakah kos konfigurasi bagi 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
  }
]

Dikira daripada blok di atas. Blok pelayan Nginx mengandungi 11 baris yang bukan kosong, dan anda perlu menulisnya semula bagi setiap hostname. Blok tapak Caddy mengandungi 3 baris. Traefik memerlukan 17 baris konfigurasi statik sebelum ia melayan satu permintaan pun, kemudian 5 label bagi setiap aplikasi.

Fahami pertukaran ini, bukan sekadar mencari pemenang. Traefik menelan kos paling tinggi sebelum aplikasi pertama dan kos paling rendah bagi setiap aplikasi seterusnya, dan kedua-dua jumlah ini bertemu pada sekitar tapak ketiga. Di bawah jumlah itu, konfigurasi statik merupakan bebanan yang tidak diperlukan. Di atas jumlah itu, label menjadi lebih efisien dan terus mendahului, kerana penghalaan (routing) terletak bersebelahan dengan servis yang dihalanya. Padamkan servis tersebut dan laluannya akan turut terpadam, iaitu perkara yang sukar dilakukan oleh fail konfigurasi berpusat: blok pelayan yang lapuk bagi aplikasi yang sudah tidak wujud sejak berbulan-bulan lalu.

Kiraan baris juga tidak menggambarkan kesukaran sebenar Nginx. Setiap blok tersebut memerlukan symlink, satu nginx -t, muat semula (reload) dan satu proses certbot, manakala suntingan Caddy hanya memerlukan satu muat semula dan suntingan Traefik tidak memerlukan sebarang arahan langsung. Ketiga-tiganya boleh dimuat semula tanpa memutuskan sambungan yang sedang aktif. Perbezaannya terletak pada bilangan langkah berasingan yang perlu anda ingat pada pukul satu pagi.

Yang manakah mengenali kontena anda?

Traefik memantau Docker socket dan membina router daripada label kontena apabila kontena bermula dan berhenti. Tiada perisian lain di sini yang melakukan perkara tersebut. Nginx dan Caddy kedua-duanya memerlukan pengeditan konfigurasi dan muat semula (reload) apabila kontena baharu muncul, dan ia memerlukan alamat yang boleh dicapai: sama ada port yang diterbitkan pada loopback, atau rangkaian Docker berkongsi yang disambungkan dengan proksi.

Ciri tersebut mempunyai harganya, dan ia perlu dinyatakan dengan jelas. Traefik membaca /var/run/docker.sock. Sesiapa yang boleh berhubung dengan socket tersebut boleh memulakan kontena dengan sistem fail hos yang dilekapkan (mounted) di dalamnya, yang bermaksud akses root pada hos. Melekapkannya sebagai baca-sahaja (read-only) mengurangkan risiko tanpa menghilangkannya sepenuhnya. Jika perkara ini penting bagi model ancaman anda, letakkan socket proxy di antara kedua-duanya yang hanya mendedahkan endpoint senarai kontena yang diperlukan oleh Traefik.

Caddy boleh melakukan penemuan berasaskan label melalui pemalam komuniti, tetapi pemalam Caddy perlu dikompilasi masuk, jadi anda perlu membina binari tersuai atau imej tersuai dengan xcaddy dan kemudian anda bertanggungjawab ke atas binaan tersebut serta kemas kininya. Untuk tiga atau empat servis, mengedit Caddyfile adalah lebih mudah.

Websocket dan penstriman: perkara yang tergendala dan puncanya

Nginx memerlukan konfigurasi tambahan. Sambungan WebSocket bermula sebagai permintaan HTTP yang membawa Upgrade: websocket, dan nginx tidak menghantar header hop-by-hop ke upstream melainkan anda mengarahkannya berbuat demikian.

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

Kemudian, di dalam blok location, tiga baris berikut mesti disertakan:

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

Jika anda meninggalkannya, konsol pelayar akan memaparkan WebSocket connection to 'wss://app.example.com/ws' failed manakala log backend anda hanya menunjukkan GET biasa. map wujud kerana Connection: upgrade yang ditetapkan secara hardcode akan dihantar pada setiap permintaan, termasuk permintaan biasa yang sepatutnya menyatakan close.

Dua lagi tetapan lalai Nginx sering menjadi masalah. proxy_read_timeout ditetapkan kepada 60 saat dan ia terpakai pada terowong selepas naik taraf (upgrade), jadi websocket tanpa trafik selama seminit akan ditutup oleh proksi. Selain itu, server-sent events akan tiba lewat atau secara berperingkat sehingga anda menetapkan proxy_buffering off; pada lokasi tersebut, kerana nginx menahan respons dalam penimbal (buffer) sementara halaman anda menunggunya.

Caddy melakukan naik taraf dan menukar sambungan kepada terowong dua hala tanpa memerlukan sebarang arahan tambahan. Ia juga melakukan flush serta-merta apabila respons adalah text/event-stream atau tidak mempunyai panjang yang diketahui, jadi penstriman berfungsi tanpa gangguan. Traefik membenarkan naik taraf melepasi proksi dan tidak menimbal respons melainkan anda menambah middleware buffering sendiri. Jika servis anda merangkumi sembang, terminal web, log tails atau papan pemuka langsung, ini merupakan perbezaan ketara dari segi jumlah konfigurasi yang perlu anda tulis dan nyahpepijat.

Blok pelayan 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 perlu diletakkan dalam konteks http, bukan di dalam server, jadi simpan ia dalam failnya sendiri di bawah /etc/nginx/conf.d/. Matikan proxy_buffering hanya pada lokasi yang melakukan penstriman, kerana penimbalan adalah fungsi yang membolehkan nginx melepaskan pekerja backend lebih awal bagi respons biasa. Certbot akan menulis semula blok ini apabila anda menjalankannya, jadi baca semula fail tersebut selepas proses selesai.

Apa yang berlaku apabila anda memerlukan sesuatu yang luar biasa?

Di sinilah Nginx membuktikan nilainya dengan konfigurasi tambahan.

  • Sijil klien, juga dipanggil mTLS (mutual TLS), di mana klien juga perlu mengemukakan sijil. 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 tidak dapat melaksanakannya sama sekali: anda perlu mentakrifkan pilihan TLS dalam penyedia fail dan menghalakan router kepadanya dengan traefik.http.routers.app.tls.options=mtls@file. Model yang meletakkan segala-galanya dalam label akan menemui pengecualian apabila anda memerlukan ciri ini.
  • Muat naik bersaiz besar. Nginx mengehadkan badan permintaan kepada 1 MB secara lalai. Muat naik yang lebih besar akan mengembalikan 413 Request Entity Too Large, dan log ralat akan menyatakan client intended to send too large body. Tingkatkan client_max_body_size. Caddy dan Traefik tidak menetapkan had badan secara lalai, jadi permintaan akan sampai ke aplikasi anda dan had aplikasi anda sendiri yang akan menentukan hasilnya.
  • Caching respons. Nginx mempunyai proxy_cache, dan ia sudah matang. Caddy memerlukan pemalam yang perlu dikompilasi bersama. Binaan sumber terbuka Traefik tidak mempunyai cache HTTP sama sekali, yang mengejutkan pengguna yang menganggap setiap proksi melakukan caching.
  • TCP atau UDP mentah, untuk port pangkalan data atau pelayan permainan. Nginx mempunyai modul stream. Traefik mempunyai router TCP dan UDP pada entrypoint masing-masing. Caddy memerlukan pemalam lain, yang bermaksud binaan tersuai yang lain.
  • Pelayan web yang sudah berada di belakang proksi. Jika servis tersebut adalah aplikasi PHP klasik, maka timbunan LAMP pada Ubuntu 24.04 sudah pun menyertakan Apache, dan meletakkan proksi di hadapannya akan memberikan anda dua lokasi yang menetapkan pengepala (headers) dan dua lokasi yang boleh menulis semula URL. Tentukan yang mana satu akan menamatkan TLS, kemudian kekalkan yang satu lagi pada HTTP biasa yang terikat pada loopback.

Perangkap firewall yang menyusuli pilihan ini

Tujuan reverse proxy adalah untuk memastikan hanya port 80 dan 443 yang dibuka. Docker membatalkan perkara ini secara senyap. Menerbitkan port dengan -p 8080:80 akan menulis peraturan DNAT ke dalam jadual nat, dan peraturan tersebut dinilai sebelum peraturan INPUT yang diuruskan oleh ufw. Oleh itu, ufw deny 8080 tidak akan menyekatnya dan aplikasi anda akan berada di internet awam di samping proksi yang telah anda konfigurasikan dengan teliti. Ikat port yang diterbitkan ke loopback dengan 127.0.0.1:8080:80, atau gugurkan ports: sepenuhnya dan biarkan proksi mencapai container melalui rangkaian Docker, iaitu kaedah yang digunakan dalam contoh Traefik di atas. Mekanisme dan penyelesaiannya terdapat dalam mengapa port yang diterbitkan Docker memintas ufw.

Uji perkara ini daripada mesin yang bukan VPS tersebut, kerana semakan yang dijalankan pada pelayan itu sendiri akan sentiasa berjaya:

curl --max-time 5 http://your.server.address:8080

Connection refused atau tamat masa (timeout) adalah hasil yang anda mahukan. Respons HTTP bermakna aplikasi tersebut boleh dicapai tanpa melalui proksi anda, dan segala konfigurasi yang anda buat di atas hanyalah hiasan semata-mata.

Proksi mana yang patut anda pilih?

Laman statik, serta satu atau dua aplikasi: Caddy. HTTPS automatik menghapuskan tugas berulang yang paling membebankan. Konfigurasinya cukup ringkas untuk dibaca dalam satu skrin, dan laman statik hanya memerlukan satu baris root dan satu baris file_server di dalam blok tapak yang sama. Kekurangannya ialah sumber rujukan atau penyelesaian masalah yang boleh disalin-tampal adalah lebih terhad apabila berlaku ralat yang luar biasa.

Homelab docker-compose yang sentiasa ditambah: Traefik. Selepas servis ketiga, penggunaan label adalah lebih mudah berbanding menyunting fail pusat, dan servis yang dipadam akan turut memadamkan laluan (route) berkaitan. Peruntukkan satu petang untuk penyediaan awal, kerana entrypoints, routers, services dan middlewares adalah istilah baharu yang perlu dipelajari. Ralat taip pada label biasanya akan dipaparkan sebagai ralat 404 daripada Traefik dan bukannya kegagalan untuk bermula, jadi baca docker logs traefik untuk melihat ralat penghuraian (parse error) sebelum anda menganggap aplikasi tersebut rosak.

Konfigurasi Nginx sedia ada, atau sebarang keperluan daripada senarai di atas: Nginx. Ia sudah mempunyai penyelesaian untuk caching respons dan sijil klien, dan hampir setiap panduan pihak ketiga menggunakan Nginx sebagai rujukan. Kekurangannya ialah sijil dan sokongan websocket perlu dikonfigurasikan secara manual dan tidak tersedia secara automatik.

Satu peraturan tetap terpakai tidak kira apa yang anda pilih. Hanya satu proses sahaja yang mendengar pada antara muka awam, manakala semua proses lain perlu mendengar pada loopback atau rangkaian Docker peribadi.

FAQ

Reverse proxy manakah yang terbaik untuk beberapa aplikasi Docker pada satu VPS?

Bagi tiga atau empat servis yang anda tambah dari semasa ke semasa, Traefik sangat berbaloi kerana setiap aplikasi membawa label penghalaan (routing labels) sendiri dan tidak memerlukan penyuntingan pada fail pusat. Jika servis tersebut stabil dan anda hanya mahu menguruskan HTTPS dengan mudah, Caddy lebih ringkas untuk dipelajari dan kurang risiko kerosakan. Pilih Nginx jika anda sudah mahir menggunakannya, atau apabila anda memerlukan ciri yang tiada pada dua pilihan lain, seperti caching respons atau pendengar TCP biasa.

Adakah Caddy benar-benar tidak memerlukan konfigurasi sijil?

Untuk kes biasa, ya. Menamakan hostname awam sebagai alamat tapak adalah keseluruhan konfigurasi: Caddy meminta sijil melalui ACME, menguruskan redirect daripada port 80, dan memperbaharuinya sebelum tamat tempoh. Dua syarat mesti dipenuhi. Port 80 mesti boleh dicapai dari internet untuk cabaran HTTP-01, dan rekod DNS A atau AAAA bagi hostname tersebut mesti sudah menghala ke VPS, kerana pihak berkuasa sijil akan menyelesaikan nama tersebut dan menyambung semula kepadanya.

Bolehkah saya menjalankan Nginx dan Traefik pada VPS yang sama?

Tidak pada port yang sama. Mana-mana yang bermula kedua akan gagal untuk bind, dan nginx akan mengeluarkan bind() to 0.0.0.0:443 failed (98: Address already in use) manakala Traefik mencatatkan ralat bind yang serupa dan berhenti. Jalankan satu proksi pada port 80 dan 443, dan letakkan segala yang lain di belakangnya. Jika anda sedang melakukan migrasi, pindahkan hostname satu demi satu: biarkan proksi hadapan menghalakan trafik ke proksi lama pada port loopback sehingga tapak terakhir selesai dipindahkan.

Mengapa websocket saya terputus selepas 60 saat di belakang Nginx?

proxy_read_timeout menetapkan 60 saat sebagai lalai dan ia terpakai pada tunnel sebaik sahaja naik taraf (upgrade) selesai, jadi sambungan tanpa trafik selama seminit akan ditutup oleh proksi dan bukannya oleh aplikasi anda. Tingkatkan nilainya pada lokasi tersebut dengan proxy_read_timeout 3600s;, atau pastikan aplikasi menghantar ping frame setiap 30 saat. Caddy dan Traefik tidak menutup sambungan yang dinaik taraf dan melahu berdasarkan pemasa satu minit, itulah sebabnya aplikasi yang sama kelihatan stabil di belakangnya tetapi tidak stabil di belakang Nginx.