Darslik · 2026-08-16 · ~6 daqiqa o‘qiladi · o'rta
Nginx amaliyotda: reverse proxy, statika va kelajakdagi 502 lar
Mundarija
Avvalgi qismda gunicorn'ni systemd ostida
ishga tushirdik, lekin u ataylab 127.0.0.1:8001ga bog'langan — tashqaridan
unga kirib bo'lmaydi. Bu tasodif emas: Python-serverni to'g'ridan-to'g'ri
internetga ochish yomon odat. Uning oldida nginx turadi va uchta ishni
o'z zimmasiga oladi: statik fayllarni Python'ni bezovta qilmasdan beradi,
sekin mijozlarning so'rovlarini o'zi qabul qilib gunicorn'ni band qilmaydi,
keyinchalik esa TLS'ni ham o'zi ko'taradi.
Konfiguratsiya fayli qayerda yashaydi#
Ubuntu'da nginx saytlari ikki papka bilan boshqariladi: sites-availableda
fayllar turadi, sites-enabledda esa ularga symlink qo'yiladi. Sayt
o'chirish — symlink'ni olib tashlash, fayl esa joyida qoladi. Yangi sayt
uchun fayl ochamiz:
sudo nano /etc/nginx/sites-available/mysite
# /etc/nginx/sites-available/mysite
server {
listen 80;
server_name mysite.uz www.mysite.uz;
# yuklanadigan fayl hajmi chegarasi (default atigi 1M — formalar uchun kam)
client_max_body_size 10M;
# ---- statika: Python'ga umuman tegmaydi ----
location /static/ {
alias /opt/mysite/staticfiles/;
# fayl nomlarida hash bor (Django ManifestStaticFilesStorage),
# shuning uchun ularni "abadiy" keshlash xavfsiz: nom o'zgarsa,
# bu boshqa fayl degani
expires 1y;
add_header Cache-Control "public, immutable";
}
location /media/ {
alias /opt/mysite/media/;
expires 7d;
}
# ---- qolgan hamma narsa gunicorn'ga ----
location / {
proxy_pass http://127.0.0.1:8001;
# bu header'larsiz Django "hamma 127.0.0.1'dan kelyapti" deb o'ylaydi
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;
}
}
Yoqamiz va tekshiramiz:
sudo ln -s /etc/nginx/sites-available/mysite /etc/nginx/sites-enabled/
sudo nginx -t # sintaksisni ALBATTA tekshiramiz — xato bo'lsa aytadi
sudo systemctl reload nginx
nginx -tni odat qiling: reload xatoli konfiguratsiya bilan chaqirilsa,
nginx eski holatida ishlashda davom etadi, lekin siz buni sezmay "nega
o'zgarish tushmayapti" deb yarim soat yo'qotasiz.
Header'lar nega bunchalik muhim#
proxy_set_header qatorlari shunchaki rasm-rusum emas. Ularsiz Django'ga
yetib borgan so'rovda mijozning haqiqiy IP'si ham, so'ralgan domen ham,
protokol ham yo'q — hammasi nginx'niki bilan almashib ketgan bo'ladi. Buning
amaliy oqibatlari esa juda seziladi: log'da hamma mehmon 127.0.0.1 bo'lib
chiqadi, rate limiting hammani bitta
odam deb hisoblab bloklab qo'yadi, keyinchalik HTTPS yoqilganda esa Django
redirect'lari noto'g'ri ishlaydi.
Django tomonida bunga mos ikki sozlama bor:
# config/settings.py
ALLOWED_HOSTS = ["mysite.uz", "www.mysite.uz"]
# X-Forwarded-Proto'ga ishonamiz — TLS qismida kerak bo'ladi
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
SECURE_PROXY_SSL_HEADER — faqat proxy ortida
Bu sozlama "X-Forwarded-Proto: https ko'rsam, so'rov xavfsiz deb hisoblayman" degani. Ilova to'g'ridan-to'g'ri internetga ochiq bo'lsa, istalgan mijoz shu header'ni o'zi yozib yuborishi mumkin. Bizning sxemada xavfsiz, chunki tashqaridan kelgan header'larni nginx o'ziniki bilan almashtiradi — lekin sxemani o'zgartirsangiz, buni yodda tuting.
Statika: collectstatic bilan kelishuv#
Yuqoridagi location /static/ ishlashi uchun Django fayllarni bitta papkaga
yig'ib bergan bo'lishi kerak:
# config/settings.py
STATIC_URL = "/static/"
STATIC_ROOT = "/opt/mysite/staticfiles"
STORAGES = {
"staticfiles": {
# fayl nomiga kontent hash'i qo'shiladi: tailwind.4527ba3c.css
"BACKEND": "django.contrib.staticfiles.storage.ManifestStaticFilesStorage",
},
}
cd /opt/mysite && .venv/bin/python manage.py collectstatic --noinput
Hash'li nomlar va nginx'dagi immutable juftligi chiroyli ishlaydi: brauzer
CSS'ni bir yilga keshlab oladi, deploy'da esa fayl nomi o'zgargani uchun
yangi versiya darhol tushadi. "Foydalanuvchida eski dizayn qolibdi, Ctrl+F5
bosing" degan suhbatlar shu ikki qator bilan tarixga aylanadi.
WebSocket'ni o'tkazish#
Loyihada Django Channels bo'lsa, alohida location kerak bo'ladi, chunki WebSocket oddiy proxy orqali o'tmaydi — ulanishni "upgrade" qilish kerak:
location /ws/ {
proxy_pass http://127.0.0.1:8001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
# WebSocket ulanishlari uzoq yashaydi — default 60s uzib qo'ymasin
proxy_read_timeout 3600s;
}
Channels maqolasida va'da qilingan qism aynan shu edi — endi to'liq sxema qo'lingizda.
502 chiqdi: qayerga qaraymiz#
Har kim hayotida ko'p marta ko'radigan xato — 502 Bad Gateway. Ma'nosi oddiy: nginx so'rovni qabul qildi, lekin orqadagi gunicorn'dan javob ololmadi. Demak muammo deyarli har doim nginx'da emas, orqada. Tekshirish tartibi:
# 1. Xizmat tirikmi?
systemctl status mysite-web
# 2. Port to'g'rimi? (unit'dagi --bind bilan nginx'dagi proxy_pass bir xilmi)
ss -tlnp | grep 8001
# 3. Nginx aniq nima deyapti?
sudo tail -20 /var/log/nginx/error.log
# "connection refused" — xizmat yotibdi yoki port noto'g'ri
# "upstream timed out" — xizmat tirik, lekin javob bermayapti (qotib qolgan)
Shu uch buyruq 502'larning to'qson foizini yechadi. Qolgan holatlarga — loglarni tizimli o'qishga — alohida qism bag'ishlaymiz.
Yakuniy holat#
Endi zanjir to'liq: foydalanuvchi 80-portga keladi, statikani nginx beradi, dinamikani gunicorn'ga uzatadi, gunicorn'ni systemd qo'riqlaydi, hammasi esa 1-qismda qurilgan firewall ortida. Bitta katta kamchilik qoldi — trafik hali ochiq HTTP'da yurmoqda. Keyingi qismda buni yopamiz: Let's Encrypt bilan bepul TLS sertifikat olish, avtomatik yangilanishi va HTTPS'ga to'g'ri ko'chish.