# Cara Setting Nginx Reverse Proxy untuk Docker di Cloudflare

> Panduan pemula cara setting nginx reverse proxy untuk app Docker di balik Cloudflare: proxy_pass, header, mode SSL, IP asli, dan tabel error 502/504.

URL: https://www.matthewswong.com/id/blog/cara-setting-nginx-reverse-proxy-docker-cloudflare/

- Author: Matthews Wong (https://www.matthewswong.com/id/about/)
- Published: 2026-10-17
- Updated: 2026-10-17
- Category: DevOps

> **Key takeaway:** Cara setting nginx reverse proxy untuk aplikasi Docker: ikat container ke 127.0.0.1, tulis server block dengan proxy_pass ke port itu, teruskan header Host dan X-Forwarded-For, lalu uji dengan nginx -t sebelum reload. Di belakang Cloudflare, pakai mode SSL Full (strict) dan set_real_ip_from agar tidak ada redirect loop dan IP pengunjung tetap asli.

Aplikasimu sudah jalan di container dan bisa dibuka lewat 203.0.113.10:3000. Lalu kamu mengarahkan domain, dan yang muncul adalah halaman Welcome to nginx!, 502 Bad Gateway, atau halaman yang berputar sampai browser menyerah. Di titik ini kebanyakan pemula sadar bahwa memasang nginx baru separuh pekerjaan. Separuh lainnya adalah menyambungkan nginx ke container, ke domain, dan ke Cloudflare dengan benar.

Panduan ini memakai pola yang sama dengan situs ini: docker-compose.yml mengikat aplikasi ke 127.0.0.1 dengan komentar bahwa nginx yang menangani TLS dan meneruskan request ke port itu. Konfigurasi nginx situs ini sendiri tidak ada di repository, jadi semua blok di bawah bersumber dari dokumentasi nginx.org dan Cloudflare, bukan salinan file server saya. Ikuti enam langkahnya berurutan, lalu pakai tabel error di akhir kalau ada yang tidak jalan.

## Ingin paham konsepnya dulu?

Panduan ini fokus pada langkah dan error. Kalau kamu lebih suka belajar urutan dari konsep ke praktik, lesson gratis kami membahas reverse proxy, header, dan cache di Cloudflare:

Reverse Proxy Nginx dan Docker Compose di Balik Cloudflare

Lesson itu juga punya latihan yang bisa dijalankan di laptop tanpa server.

## Yang kamu butuhkan

Siapkan empat hal ini sebelum mulai. Semua nama di bawah adalah placeholder, ganti dengan milikmu.

Sebuah server Linux (Debian atau Ubuntu) dengan akses sudo, misalnya dengan user bernama deploy dan alamat 203.0.113.10.

Docker dan Docker Compose sudah terpasang, dan aplikasimu sudah punya image yang bisa dijalankan (di sini bernama my-app:latest, mendengarkan di port 3000).

Sebuah domain, di sini example.com, yang DNS-nya dikelola di Cloudflare.

Port 80 dan 443 terbuka di firewall server.

## Langkah 1: Jalankan app di container, tapi hanya di loopback

Aturan pertama: container tidak boleh bisa dijangkau langsung dari internet. Di docker-compose.yml situs ini, port aplikasi diikat ke 127.0.0.1 supaya satu-satunya jalan masuk dari luar adalah lewat nginx. Pola yang sama berlaku untuk appmu. Di sisi kiri titik dua pada ports kamu menentukan alamat dan port di host, di sisi kanan port di dalam container.

Output yang diharapkan dari docker compose ps: kolom PORTS menampilkan 127.0.0.1:3000->3000/tcp, bukan 0.0.0.0:3000. Lalu curl -I ke alamat itu membalas status 200 (atau 3xx kalau app-mu me-redirect).

Kesalahan paling umum di langkah ini adalah menulis 3000:3000 tanpa alamat. Port jadi terbuka di semua interface, siapa pun bisa melewati nginx dan Cloudflare, dan semua pengaturan keamanan yang kamu buat di langkah berikutnya tidak berlaku untuk jalur itu.

## Langkah 2: Pasang nginx dan pastikan hidup

Di Debian atau Ubuntu, nginx tersedia dari repository bawaan. Setelah terpasang, service-nya langsung berjalan dan melayani halaman sambutan di port 80. Itu justru berguna: kalau halaman itu muncul, berarti nginx sehat dan sisa masalah ada di konfigurasimu.

Output yang diharapkan: nginx -v mencetak nginx version: nginx/ diikuti nomor versi yang terpasang, systemctl status menampilkan active (running), dan curl ke 127.0.0.1 membalas 200 dengan isi halaman Welcome to nginx!.

## Langkah 3: Tulis server block dengan proxy_pass

Reverse proxy di nginx pada dasarnya satu directive: proxy_pass, yang menentukan protokol dan alamat server tujuan. Letakkan di dalam location di sebuah server block. File konfigurasi utama nginx.conf biasanya berada di /etc/nginx, dan paket Debian atau Ubuntu memuat file di conf.d dari sana. Pastikan dengan nginx -T seperti di bawah, karena tata letak bisa berbeda antar distro.

Selalu jalankan nginx -t sebelum reload. Perintah itu memeriksa sintaks dan mencoba membuka file yang disebut di konfigurasi. Saat reload, master process memeriksa konfigurasi baru dulu; kalau gagal, nginx membatalkan perubahan dan tetap memakai konfigurasi lama, jadi reload yang salah tidak menjatuhkan situsmu.

Output yang diharapkan: nginx -t mencetak dua baris yang berakhir dengan syntax is ok dan test is successful. Setelah reload, curl dengan header Host example.com ke 127.0.0.1 membalas persis seperti app-mu, bukan halaman sambutan.

## Langkah 4: Teruskan header yang benar

Secara default nginx tidak meneruskan header Host dari request asli. Ia mengirim Host berisi alamat upstream-nya sendiri, yaitu 127.0.0.1:3000. Akibatnya app membuat link absolut, cookie, atau redirect ke alamat itu, dan semuanya rusak di browser pengunjung. Tambahkan empat baris proxy_set_header agar app tahu domain, IP, dan protokol yang sebenarnya.

Dua jebakan lagi. Pertama, proxy_set_header hanya diwarisi dari level di atasnya kalau tidak ada proxy_set_header sama sekali di level saat ini, jadi jangan menyebar header di server dan location sekaligus. Kedua, garis miring di akhir proxy_pass mengubah arti: tanpa path, URI dari klien diteruskan apa adanya; dengan path atau garis miring, bagian yang cocok dengan location diganti.

## Langkah 5: Sambungkan domain lewat Cloudflare dan pilih mode SSL yang benar

Di dashboard Cloudflare, buat record A untuk example.com ke 203.0.113.10 dengan proxy aktif. Lalu buka pengaturan SSL/TLS dan pilih mode. Menurut dokumentasi Cloudflare, mode Flexible mengenkripsi pengunjung ke Cloudflare saja, sedangkan Cloudflare ke origin tetap HTTP. Full (strict) memvalidasi sertifikat di origin, yang bisa dari CA publik seperti Let's Encrypt atau dari Cloudflare Origin CA. Cloudflare menyarankan Full atau Full (strict) jika memungkinkan.

Artinya nginx perlu mendengarkan di 443 dengan sertifikat. Cara memasangnya otomatis dibahas di 

perbandingan Caddy dan nginx untuk HTTPS otomatis

. Kalau kamu tidak mau membuka port ke internet sama sekali, ada jalur lain lewat 

Cloudflare Tunnel

. Di sini kita tetap memakai nginx dengan port terbuka.

Mode Flexible plus redirect HTTP ke HTTPS di nginx adalah penyebab klasik ERR_TOO_MANY_REDIRECTS. Cloudflare mengirim HTTP, nginx menjawab redirect ke HTTPS, Cloudflare mengirim HTTP lagi, dan seterusnya. Pilih satu: Full (strict) dengan redirect, atau tanpa redirect di nginx.

## Langkah 6: Pulihkan IP asli pengunjung

Di belakang Cloudflare, setiap koneksi ke nginx datang dari alamat Cloudflare, jadi log, rate limit, dan firewall hanya melihat proxy. Cloudflare menaruh IP asli pengunjung di header CF-Connecting-IP. Modul realip di nginx menukarnya: real_ip_header menyebut nama header, dan set_real_ip_from mendaftar alamat yang dipercaya untuk mengirimnya.

Daftar tepercaya itu bagian keamanannya. Isi hanya dengan rentang yang dipublikasikan Cloudflare di halaman IP mereka, dan perbarui secara rutin, seperti yang disarankan dokumentasinya. Modul realip tidak ikut di-build secara default, jadi cek dulu apakah nginx-mu punya with-http_realip_module.

Output yang diharapkan: perintah pengecekan mencetak with-http_realip_module. Setelah reload, baris baru di access.log menampilkan IP pengunjung, bukan alamat dari rentang Cloudflare.

Output yang diharapkan

## Error umum dan cara memperbaikinya

Hampir semua masalah reverse proxy jatuh ke sembilan gejala ini. Cari gejalamu di kolom pertama, lalu cek sebabnya sebelum mengubah apa pun.

Gejala

Penyebab

Perbaikan

Kalau gejalamu tidak ada di tabel, debug dari dalam ke luar. Urutan ini memisahkan app, nginx, dan Cloudflare satu per satu, jadi kamu tahu lapisan mana yang salah.

> Kalau langkah 1 gagal, masalahnya di app atau container. Kalau langkah 1 lolos tetapi langkah 3 gagal, masalahnya di konfigurasi nginx. Kalau keduanya lolos tetapi domain tetap error, lihat pengaturan DNS dan mode SSL di Cloudflare.

## Checklist sebelum kamu menyebut selesai

Port container hanya terikat ke 127.0.0.1 dan docker compose ps membuktikannya.

sudo nginx -t lulus sebelum setiap reload.

proxy_set_header Host $host dan X-Forwarded-Proto ada di location yang memakai proxy_pass.

Mode SSL Cloudflare adalah Full (strict) dan tidak ada redirect loop.

set_real_ip_from hanya berisi rentang Cloudflare, dan log menampilkan IP pengunjung.

Kamu tahu urutan debug: app, nginx, lalu Cloudflare.

## Belajar lebih dalam

Panduan ini sengaja berupa resep. Kalau kamu ingin tahu kenapa Cloudflare menyimpan file ber-hash selama setahun tetapi tidak pernah menyimpan HTML, dan apa arti DYNAMIC pada header cf-cache-status, lanjutkan ke lesson gratis berikut:

Reverse Proxy Nginx dan Docker Compose di Balik Cloudflare

Lesson itu menjalankan nginx dan app di laptop dengan Docker Compose, jadi kamu bisa mengulang percobaannya tanpa menyentuh server produksi.

## Sumber dan dokumentasi resmi

- Dokumentasi nginx: ngx_http_proxy_module (proxy_pass, proxy_set_header, timeout)
- Dokumentasi nginx: panduan pemula (reload dan konfigurasi)
- Dokumentasi nginx: ngx_http_realip_module
- Dokumentasi Cloudflare: mode enkripsi SSL/TLS
- Dokumentasi Cloudflare: Error 502 dan 504
- Cloudflare: rentang alamat IP

502 Bad Gateway dengan halaman bertuliskan nginx

nginx hidup tetapi tidak bisa menyambung ke app: container mati, belum siap, atau port di proxy_pass salah

Jalankan docker compose ps lalu curl -I http://127.0.0.1:3000. Samakan port di proxy_pass dengan angka sebelum titik dua pada ports. Lihat baris terakhir error.log untuk pesan upstream-nya.

502 atau 504 dengan halaman bermerek Cloudflare

Menurut dokumentasi Cloudflare, ini kasus yang paling umum: error datang dari origin, dan Cloudflare hanya meneruskannya

Debug seperti baris pertama: yang bermasalah nginx atau app di server, bukan Cloudflare. Halaman kosong tanpa merek Cloudflare adalah kasus lain, misalnya kompresi gzip yang rusak di origin.

504 Gateway Timeout setelah menunggu lama

App butuh lebih dari 60 detik untuk menjawab; proxy_read_timeout bawaan nginx adalah 60s

Perbaiki endpoint yang lambat lebih dulu. Kalau memang harus lama, naikkan proxy_read_timeout hanya di location itu, bukan di seluruh server.

ERR_TOO_MANY_REDIRECTS di browser

Mode SSL Cloudflare Flexible mengirim HTTP ke origin, lalu nginx me-redirect HTTP ke HTTPS, dan kedua sisi saling memantulkan request

Pasang sertifikat di nginx dan ganti mode ke Full (strict). Atau, kalau tetap Flexible, hapus redirect HTTP ke HTTPS di nginx.

Error 521: web server is down

Cloudflare tidak bisa menyambung ke origin; biasanya nginx mati, atau port 80 dan 443 tertutup firewall

Di server jalankan sudo systemctl status nginx dan curl -I http://127.0.0.1. Pastikan firewall membuka port 80 dan 443. Dokumentasi Cloudflare juga menyarankan memeriksa log proxy atau firewall di antaranya.

Yang muncul halaman Welcome to nginx!

File konfigurasimu tidak dimuat, server_name tidak cocok dengan domain, atau kamu lupa reload

Jalankan sudo nginx -T dan cari nama domainmu di outputnya. Kalau tidak ada, file-nya ada di folder yang tidak di-include. Setelah memperbaiki, sudo nginx -s reload.

Link atau redirect di app mengarah ke 127.0.0.1:3000

Header Host tidak diteruskan, jadi app menerima nilai bawaan nginx, yaitu alamat upstream-nya sendiri

Tambahkan proxy_set_header Host $host; di location yang memakai proxy_pass, lalu reload.

Log nginx hanya berisi alamat IP Cloudflare

Semua koneksi datang dari Cloudflare, dan IP asli pengunjung ada di header CF-Connecting-IP

Aktifkan modul realip: set_real_ip_from untuk setiap rentang Cloudflare dan real_ip_header CF-Connecting-IP (Langkah 6).

404 di path yang di app jelas ada

proxy_pass memakai URI (ada garis miring di akhir), sehingga nginx mengganti bagian path yang cocok dengan location

Tulis proxy_pass tanpa garis miring dan tanpa path kalau path dari klien harus diteruskan apa adanya.

## Source links

- https://nginx.org/en/docs/http/ngx_http_proxy_module.html
- https://nginx.org/en/docs/beginners_guide.html
- https://nginx.org/en/docs/http/ngx_http_realip_module.html
- https://developers.cloudflare.com/ssl/origin-configuration/ssl-modes/
- https://developers.cloudflare.com/support/troubleshooting/http-status-codes/cloudflare-5xx-errors/error-502-504/
- https://www.cloudflare.com/ips/

## FAQ

### Apa itu nginx reverse proxy dan kenapa perlu untuk Docker?

Reverse proxy menerima setiap request lebih dulu lalu meneruskannya ke aplikasimu, dan di nginx itu dilakukan oleh directive proxy_pass. Dengan Docker, container cukup mendengarkan di 127.0.0.1 sementara nginx mengurus domain dan HTTPS. Jadi tidak ada yang bisa menjangkau container tanpa lewat nginx.

### Kenapa nginx reverse proxy saya error 502 Bad Gateway?

nginx hidup tetapi tidak bisa menyambung ke app di balik proxy_pass. Penyebab umumnya container mati, port di proxy_pass salah, atau app belum siap. Jalankan curl -I http://127.0.0.1:3000 di server dan baca ujung error.log untuk tahu yang mana.

### Kenapa muncul ERR_TOO_MANY_REDIRECTS setelah memasang Cloudflare di depan nginx?

Biasanya Cloudflare berada di mode Flexible, jadi ia menyambung ke origin lewat HTTP sementara nginx me-redirect HTTP ke HTTPS. Request pun memantul tanpa henti. Pasang sertifikat di nginx lalu ganti ke Full (strict), atau hapus redirect di nginx kalau tetap memakai Flexible.

### Apakah proxy_set_header Host wajib di nginx?

Untuk kebanyakan aplikasi, ya. Secara default nginx mengirim Host berisi alamat upstream-nya sendiri, misalnya 127.0.0.1:3000, sehingga app membuat link dan redirect ke tempat yang salah. proxy_set_header Host $host meneruskan domain yang benar-benar diminta pengunjung.

### Bagaimana cara melihat IP asli pengunjung di nginx yang di belakang Cloudflare?

Pakai modul realip dengan real_ip_header CF-Connecting-IP dan satu baris set_real_ip_from untuk setiap rentang yang dipublikasikan Cloudflare. Percayai hanya rentang itu, karena header yang dipercaya dari alamat mana pun membuat siapa saja bisa memilih IP-nya. Modul ini tidak ikut di-build secara default, jadi cek nginx -V untuk with-http_realip_module.

---

- HTML version: https://www.matthewswong.com/id/blog/cara-setting-nginx-reverse-proxy-docker-cloudflare/
- English: https://www.matthewswong.com/en/blog/cara-setting-nginx-reverse-proxy-docker-cloudflare/
- Site index for agents: https://www.matthewswong.com/llms.txt
- Sitemap: https://www.matthewswong.com/sitemap.xml
- API description (OpenAPI): https://www.matthewswong.com/openapi.json
