Rekan ArtonLabs, hampir semua aplikasi yang Anda jalankan di server akan berada di belakang Nginx, jadi memahami konfigurasinya adalah investasi yang selalu kembali.
Hampir semua aplikasi yang kami jalankan berada di belakang Nginx. Aplikasi mendengarkan di localhost, Nginx yang menghadap internet: ia mengurus HTTPS, menyajikan berkas statis, mengompresi, meng-cache, dan menyaring permintaan sampah sebelum menyentuh kode Anda. Tulisan ini menyusun konfigurasi reverse proxy yang kami anggap standar, lengkap dengan beberapa kesalahan klasik yang pernah kami buat.
Daftar Isi
- Reverse Proxy Dasar
- Header yang Harus Diteruskan
- Berkas Statis dan Jebakan alias
- Kompresi
- Cache Sisi Proxy
- HTTPS dengan Let's Encrypt
- Menyaring Permintaan Sampah
- Penutup
Reverse Proxy Dasar
server {
listen 80;
server_name contoh.com www.contoh.com;
location / {
proxy_pass http://127.0.0.1:8000;
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_connect_timeout 60s;
proxy_read_timeout 120s;
}
}
Simpan di /etc/nginx/sites-available/contoh.com.conf, tautkan ke sites-enabled, lalu selalu nginx -t sebelum systemctl reload nginx. Satu konfigurasi yang salah sintaks akan menjatuhkan semua situs di server itu kalau Anda me-restart tanpa menguji.
Header yang Harus Diteruskan
Empat header di atas bukan hiasan. Host membuat aplikasi tahu domain mana yang diminta. X-Real-IP dan X-Forwarded-For membawa alamat pengunjung asli, yang tanpa itu akan selalu terbaca 127.0.0.1 di log aplikasi dan di pembatas brute-force. X-Forwarded-Proto memberi tahu aplikasi bahwa sambungan aslinya HTTPS; tanpa ini, Django dengan SECURE_PROXY_SSL_HEADER akan menolak mengirim cookie aman dan login berputar tanpa henti. Untuk WebSocket, tambahkan proxy_set_header Upgrade $http_upgrade; dan proxy_set_header Connection "upgrade";. Untuk respons streaming seperti keluaran LLM, tambahkan proxy_buffering off; di lokasi itu.
Berkas Statis dan Jebakan alias
location /static/ { alias /home/webapp/app/staticfiles/; expires 30d; }
location /media/ { alias /home/webapp/app/media/; expires 7d; }
Nginx menyajikan berkas statis jauh lebih efisien daripada aplikasi. Perhatikan garis miring di akhir jalur alias: kalau lokasinya /media/ dan alias-nya /home/webapp/app/media tanpa garis miring, permintaan /media/x.jpg dipetakan ke /home/webapp/app/mediax.jpg dan mendapat 404. Kami pernah menghabiskan waktu mencari bug ini di aplikasi, padahal letaknya di satu karakter konfigurasi.
Log Per Situs
access_log /home/webapp/logs/contoh.access.log;
error_log /home/webapp/logs/contoh.error.log;
Log terpisah per situs membuat pemilik situs bisa membaca lognya sendiri tanpa akses ke log server lain, dan memudahkan mencari masalah satu situs tanpa menyaring ribuan baris milik situs lain. Pastikan folder lognya bisa ditulis Nginx dan dirotasi oleh logrotate; untuk Nginx, rotasi dengan sinyal USR1 (cara bawaan paket) aman. Format log bawaan tidak memuat durasi permintaan; tambahkan $request_time dan $upstream_response_time ke log_format sendiri, dan Anda akan tahu apakah lambatnya di Nginx atau di aplikasi.
Kompresi
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_min_length 1024;
gzip_comp_level 5;
gzip_static on;
gzip_types text/plain text/css text/javascript application/javascript application/json application/xml image/svg+xml;
Dua baris yang sering terlewat. gzip_proxied any: tanpa ini, respons yang datang dari upstream aplikasi tidak dikompresi sama sekali, hanya berkas yang disajikan Nginx sendiri. Dan gzip_static on: kalau alat build Anda (WhiteNoise, Vite) sudah membuat berkas .gz, Nginx tinggal menyajikannya tanpa mengompresi ulang. Di sebuah situs kami, menambahkan keduanya memangkas kunjungan pertama dari 243 KB menjadi 58 KB.
Cache Sisi Proxy
Untuk halaman yang sama bagi semua pengunjung dan jarang berubah, Nginx bisa menyimpan respons upstream:
proxy_cache_path /var/cache/nginx/contoh levels=1:2 keys_zone=contoh:10m max_size=500m inactive=60m;
location /publik/ {
proxy_cache contoh;
proxy_cache_valid 200 10m;
proxy_cache_use_stale error timeout updating;
add_header X-Cache $upstream_cache_status;
proxy_pass http://127.0.0.1:8000;
# header lain seperti di atas
}
Jangan meng-cache halaman yang bergantung pada login atau cookie, kecuali Anda benar-benar memahami kuncinya; kesalahan di sini berarti pengguna melihat data pengguna lain. Header X-Cache memudahkan memeriksa apakah cache bekerja (HIT, MISS, atau EXPIRED).
HTTPS dengan Let's Encrypt
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d contoh.com -d www.contoh.com
sudo certbot renew --dry-run
Certbot menyunting blok server Anda, menambahkan listen 443 ssl, jalur sertifikat, dan pengalihan dari 80. Pembaruannya dijalankan timer systemd otomatis; --dry-run memastikan jalurnya berfungsi. Pastikan semua nama di -d sudah mengarah ke server, termasuk www; satu nama yang belum resolve menggagalkan seluruh permintaan. Kalau situs berada di balik Cloudflare dengan mode SSL Full, server asal tetap harus melayani HTTPS untuk domain itu, kalau tidak permintaan jatuh ke blok server bawaan dan menghasilkan galat yang membingungkan.
Menyaring Permintaan Sampah
location ~* (^|/)wp-(admin|includes|content|login|config) { access_log off; return 444; }
location ~* (^|/)(xmlrpc\.php|wlwmanifest\.xml) { access_log off; return 444; }
location ~ /\.(env|git|svn)(/|$) { access_log off; return 444; }
location = /favicon.ico { access_log off; return 204; }
Pada situs Django kami, 37 persen permintaan suatu hari adalah pemindai WordPress mencari wp-admin. Kode 444 memutus sambungan tanpa jawaban, sehingga tidak ada proses Python yang terpakai. Batasi pola pada penanda yang mustahil bertabrakan dengan rute aplikasi Anda; jangan memblokir kata umum seperti "admin" kalau aplikasi Anda memakainya.
Membatasi Laju Permintaan
# di blok http
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
# di blok server
location /masuk/ {
limit_req zone=login burst=5 nodelay;
proxy_pass http://127.0.0.1:8000;
}
Lima permintaan per menit per alamat untuk halaman login menghentikan sebagian besar tebakan sandi otomatis sebelum sampai ke aplikasi. Untuk API, zona terpisah dengan laju yang lebih longgar. Perhatikan bahwa di belakang Cloudflare, $binary_remote_addr adalah alamat Cloudflare kecuali Anda memasang modul real_ip dengan daftar alamat mereka.
Beberapa Situs di Satu Server
Setiap domain mendapat blok server sendiri, dan Nginx memilih berdasarkan server_name. Buat satu blok default_server yang menolak permintaan tanpa nama yang dikenal dengan return 444;, supaya pemindai yang mengakses alamat IP langsung tidak mendarat di situs pertama secara kebetulan. Tanpa blok ini, permintaan ke alamat IP atau ke domain yang salah diarahkan ke blok pertama menurut abjad, dan kami pernah melihat aplikasi Django menolak ribuan permintaan dengan galat DisallowedHost hanya karena itu.
nginx -t yang terlewat.
Semoga bermanfaat. Kalau ada baris konfigurasi yang membuat Anda bertanya-tanya, kirim lewat halaman kontak dan kami bahas di tulisan berikutnya.