Nginx Reverse Proxy कॉन्फिगरेशन कसे करावे?
Ubuntu 24.04 वर Nginx reverse proxy सेट करण्याची संपूर्ण माहिती. proxy_pass, आवश्यक headers, websockets आणि file uploads साठी अचूक कॉन्फिगरेशन कोड येथे पहा.
Nginx reverse proxy configuration काय करते
Nginx reverse proxy पोर्ट 80 आणि पोर्ट 443 वर येणाऱ्या विनंत्या स्वीकारते आणि त्या स्थानिक पोर्टवर (local port) कार्यरत असलेल्या ॲप्लिकेशनकडे पाठवते, त्यानंतर ॲप्लिकेशनचे उत्तर ब्राउझरला परत करते. याची कॉन्फिगरेशन एकच server ब्लॉक असते आणि हा ब्लॉक लहान असतो. यातील बहुतेक गुंतागुंत पाच ते सहा ओळींमध्ये असते, ज्या ॲप्लिकेशनला खरा क्लायंट कोण होता आणि त्याने कोणता प्रोटोकॉल वापरला होता, याची माहिती देतात.
खालील सर्व माहिती Ubuntu 24.04 वर शून्यापासून तयार केली आहे, ज्यासाठी डिस्ट्रिब्युशनमधील nginx पॅकेज वापरले आहे. याची सुरुवात अशा ॲप्लिकेशनपासून होते जे आधीच 127.0.0.1:3000 वर प्रतिसाद देत आहे. जर तुम्ही अद्याप प्रॉक्सी निवडली नसेल, तर nginx ची Caddy आणि Traefik सोबत तुलना हा लेख आधी वाचावा. खाली nginx चे उत्तर ओळीनुसार कसे दिसते, हे दिले आहे.
या कॉन्फिगरेशन्स तुमच्या स्वतःच्या सर्व्हरवर चालवा. प्रत्येक बदल लागू करण्यापूर्वी sudo nginx -t ने तपासा आणि ते काय आउटपुट देते ते वाचा.
Ubuntu वर Nginx चे कॉन्फिगरेशन कोठे असते
sudo apt update
sudo apt install -y nginx
ls -l /etc/nginx/sites-enabled/मुख्य फाईल /etc/nginx/nginx.conf मध्ये असते. ती http { } ब्लॉकच्या आत जागतिक (global) पर्याय सेट करते आणि त्यानंतर दोन डिरेक्टरीज समाविष्ट (pull) करते: /etc/nginx/conf.d/*.conf आणि /etc/nginx/sites-enabled/*. Ubuntu आणि Debian वर तुम्ही प्रत्येक साईटसाठी /etc/nginx/sites-available/ मध्ये एक फाईल लिहिता आणि /etc/nginx/sites-enabled/ मध्ये सिम्बॉलिक लिंक (symlink) तयार करून ती कार्यान्वित करता. सिम्बॉलिक लिंक हटवल्यास साईट बंद होते, परंतु फाईल सुरक्षित राहते.
नंतर वापरले जाणारे दोन डिरेक्टिव्ह फक्त http कॉन्टेक्स्टमध्येच काम करतात, ते कधीही server ब्लॉकच्या आत चालत नाहीत: map आणि upstream. त्यांना /etc/nginx/conf.d/ अंतर्गत त्यांच्या स्वतःच्या फाईलमध्ये ठेवा, कारण ती डिरेक्टरी http स्तरावर समाविष्ट केली जाते.
पॅकेजसोबत default नावाची एक कार्यान्वित साईट येते. ती default_server म्हणून चिन्हांकित केलेली असते, याचा अर्थ असा की ज्या विनंतीचा Host हेडर तुमच्या कॉन्फिगरेशनमधील कोणत्याही server_name शी जुळत नाही, त्याला ही साईट उत्तर देते. जोपर्यंत ती कार्यान्वित असते, तोपर्यंत तुमच्या नावाशी न जुळणारी विनंती तुमच्या ॲपऐवजी त्यावर जाते. तुमची स्वतःची साईट व्यवस्थित काम करू लागल्यावर ती सिम्बॉलिक लिंक काढून टाका.
sudo rm /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginxएका ॲपला प्रॉक्सी करणारा सर्वात लहान सर्व्हर ब्लॉक
server {
listen 80;
listen [::]:80;
server_name app.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
}
}हे /etc/nginx/sites-available/app.example.com म्हणून सेव्ह करा, त्यानंतर ते इनेबल करून लोड करा.
sudo ln -s /etc/nginx/sites-available/app.example.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
curl -sI -H 'Host: app.example.com' http://127.0.0.1/listen 80; हे IPv4 ला बाइंड करते आणि listen [::]:80; हे IPv6 ला बाइंड करते. दुसरी ओळ वगळल्यास, ज्या अभ्यागताच्या DNS (domain name system) लूकअपमध्ये तुमच्या सर्व्हरसाठी AAAA रेकॉर्ड येतो, त्याला कनेक्शन नाकारले जाईल, तर IPv4 वरील प्रत्येकाला साइट व्यवस्थित दिसेल. तुम्हाला मिळणाऱ्या बग रिपोर्टमध्ये "it works for me" असे लिहिले असेल.
server_name ची तुलना ब्राउझरद्वारे पाठवल्या जाणाऱ्या Host हेडरशी केली जाते. स्पेसने वेगळी करून अनेक नावे सूचीबद्ध केली जाऊ शकतात. जर कोणताही ब्लॉक मॅच झाला नाही, तर nginx जो ब्लॉक default_server आहे त्याचा वापर करतो, म्हणूनच पॅकेज केलेली साइट काढून टाकणे आवश्यक होते.
location / हे रिक्वेस्ट पाथवर प्रीफिक्स मॅच करते आणि / प्रत्येक पाथशी मॅच होते. proxy_pass हा तो पत्ता आहे ज्यावर nginx कनेक्शन उघडते. ॲपला 127.0.0.1 वर बाइंड केलेले ठेवा, जेणेकरून आत येण्याचा एकमेव मार्ग nginx द्वारेच असेल. जर ॲप कंटेनरमध्ये चालत असेल, तर ते 127.0.0.1:3000:3000 म्हणून पब्लिश करा, 3000:3000 म्हणून नको, कारण Docker स्वतःचे नियम लिहिते आणि ufw ला बायपास करून पोर्ट थेट पब्लिश करते, त्यामुळे तुमचा फायरवॉल काहीही असला तरी उघडा पब्लिश केलेला पोर्ट इंटरनेटवरून पोहोचण्यायोग्य असतो.
curl ओळ सर्व्हरकडूनच योग्य Host हेडर पाठवते, जेणेकरून DNS कुठेही पॉइंट करण्यापूर्वी तुम्ही ब्लॉकची चाचणी घेऊ शकता.
जेव्हा तुम्ही काहीही लिहित नाही तेव्हा nginx अपस्ट्रीमकडे काय पाठवते
proxy_pass स्वतःहून तुमच्या ॲप्लिकेशनपासून चार गोष्टी लपवते.
nginx डीफॉल्टनुसार बॅकएंडशी HTTP/1.0 प्रोटोकॉलमध्ये संवाद साधते आणि Connection: close पाठवते, त्यामुळे प्रत्येक विनंती एक नवीन अपस्ट्रीम कनेक्शन उघडते आणि कोणताही प्रोटोकॉल अपग्रेड शक्य होत नाही.
Host हेडरचे मूल्य बदलून proxy_pass केले जाते, जे 127.0.0.1:3000 असते. जे ॲप Host वरून ॲब्सोल्युट लिंक्स तयार करते, ते आता अशा लिंक्स बनवते ज्या सर्व्हरबाहेरील कोणीही उघडू शकत नाही.
ॲपपर्यंत पोहोचणारे कनेक्शन nginx कडून येते, त्यामुळे ॲपला क्लायंटचा पत्ता 127.0.0.1 असा दिसतो. परिणामी, ॲपमधील प्रत्येक लॉग लाईन आणि प्रत्येक रेट लिमिट अभ्यागताऐवजी प्रॉक्सीची नोंद करते.
ब्राउझरने HTTPS वापरले होते हे ॲपला समजू शकत नाही, कारण त्याला मिळालेले कनेक्शन लूपबॅक ॲड्रेसवर साधे HTTP असते.
चार ओळी हे सर्व प्रश्न सोडवतात.
सेट करायचे चार हेडर्स आणि प्रत्येक हेडर बॅकएंडला काय दाखवतो
location / {
proxy_pass http://127.0.0.1:3000;
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;
}Host अभ्यागताने टाईप केलेले नाव वाहून नेतो. $host हे विनंतीमधील नाव असते, ज्यातून पोर्ट काढून टाकलेले असते आणि अक्षरे लोअरकेस केलेली असतात. हे सेट केल्यास तुमचे ॲप अचूक ॲब्सोल्युट URL तयार करते: जसे की लॉगिननंतरचे रिडायरेक्ट किंवा पासवर्ड रिसेट ईमेलमधील लिंक. हे न वापरल्यास त्या URL 127.0.0.1:3000 कडे निर्देश करतात, ज्यामुळे लॉगिन करताना ब्राउझर अशा पत्त्यावर जातो जो कनेक्शन नाकारतो. जर तुमच्या ॲपला पोर्टचीही गरज असेल, कारण तुम्ही ते 8080 वर सर्व्ह करत असाल, तर $http_host वापरा, जे क्लायंटने पाठवल्याप्रमाणे नेमके हेडर्स असते.
X-Real-IP एक मूल्य वाहून नेतो: $remote_addr, ज्या पत्त्यावरून nginx ने कनेक्शन स्वीकारले आहे. ॲप्स हे त्यांच्या स्वतःच्या ॲक्सेस लॉगसाठी आणि रेट लिमिटिंगसाठी वाचतात.
X-Forwarded-For एक यादी वाहून नेतो. $proxy_add_x_forwarded_for क्लायंटने आधीच त्या हेडर्समध्ये टाकलेल्या माहितीमध्ये $remote_addr जोडतो, त्यामुळे मूल्य स्वल्पविरामाने वेगळे केलेले असते आणि nginx ने जोडलेली एन्ट्री शेवटची असते. हा तपशील हेडर्सवर विश्वास ठेवता येईल की नाही हे ठरवतो: क्लायंट त्याला हवे ते X-Forwarded-For पाठवू शकतो, त्यामुळे पहिली एन्ट्री वाचणाऱ्या ॲपला कोणताही खोटा पत्ता सांगितला जाऊ शकतो. जेव्हा nginx हा एज सर्व्हर असतो, तेव्हा $remote_addr लिहा आणि क्लायंटची आवृत्ती काढून टाका. जेव्हा समोर CDN किंवा दुसरा प्रॉक्सी असतो, तेव्हा realip मॉड्यूल मधील set_real_ip_from आणि real_ip_header वापरा, जेणेकरून $remote_addr स्वतः खरा क्लायंट पत्ता बनेल.
X-Forwarded-Proto http किंवा https वाहून नेतो. फ्रेमवर्क्स हे वाचून ठरवतात की कुकीज Secure म्हणून मार्क करायच्या आहेत का आणि HTTPS वर रिडायरेक्ट करायचे आहे का. TLS साइटवर हे न वापरल्यास, HTTPS सक्ती करणारे ॲप http पाहते, HTTPS पत्त्यावर रिडायरेक्टसह उत्तर देते, nginx द्वारे पुढची विनंती प्राप्त करते, तरीही http पाहते आणि पुन्हा रिडायरेक्ट करते. ब्राउझर प्रयत्न सोडून देतो आणि ERR_TOO_MANY_REDIRECTS दाखवतो.
प्रत्येक location मध्ये हे चार ओळी पुन्हा पुन्हा लिहिणे म्हणजे त्यांच्यात तफावत निर्माण करणे होय. त्यांना एका फाईलमध्ये ठेवा आणि ती include करा.
# /etc/nginx/snippets/proxy-headers.conf
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;location / {
include snippets/proxy-headers.conf;
proxy_pass http://127.0.0.1:3000;
}येथे इनहेरिटन्समध्ये एक धोका आहे. एखादे location त्याच्या server ब्लॉककडून proxy_set_header डिरेक्टिव्ह तेव्हाच इनहेरिट करते जेव्हा त्या location मध्ये स्वतःचे कोणतेही डिरेक्टिव्ह नसतात. location च्या आत एकही proxy_set_header जोडल्यास, सर्व्हर स्तरावर परिभाषित केलेले सर्व हेडर्स त्या location साठी काढून टाकले जातात. त्यामुळे त्या सर्वांना एकाच स्तरावर ठेवा, किंवा प्रत्येक प्रॉक्सी करणाऱ्या location मध्ये स्निपेट include करा.
माझे WebSocket अॅप कनेक्ट होऊन डिस्कनेक्ट का होते?
कारण डीफॉल्ट सेटिंग्ज अपग्रेडला परवानगी देत नाहीत आणि डीफॉल्ट रीड टाइमआउट 60 सेकंदांनंतर निष्क्रिय टनेल बंद करतो. WebSocket ची सुरुवात Upgrade: websocket आणि Connection: Upgrade असलेल्या HTTP विनंतीने होते. हे hop-by-hop हेडर्स आहेत, ज्याचा अर्थ असा की प्रॉक्सीने ते पुढे पाठवण्याऐवजी स्वतः वापरणे अपेक्षित असते आणि HTTP/1.0 मध्ये अपग्रेडची कोणतीही यंत्रणा नसते. त्यामुळे हे दोन्ही हेडर्स हाताने पुन्हा जोडावे लागतात.
मॅप http कॉन्टेक्स्टमध्ये, त्याच्या स्वतःच्या फाईलमध्ये असावा.
# /etc/nginx/conf.d/websocket.conf
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}त्यानंतर लोकेशन.
location / {
include snippets/proxy-headers.conf;
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}मॅप यासाठी असतो की एकच लोकेशन दोन्ही प्रकारच्या ट्रॅफिकला सर्व्ह करू शकेल. सामान्य विनंतीवर $http_upgrade रिकामे असते, म्हणून $connection_upgrade हे close बनते. अपग्रेड विनंतीवर त्यात websocket असते, म्हणून अपस्ट्रीम पाठवलेला हेडर Connection: upgrade असतो. proxy_set_header Connection "upgrade"; हार्ड-कोड केल्यास तो हेडर प्रत्येक साध्या पेज विनंतीवरही पाठवला जातो आणि काही बॅकएंड अशा विनंतीला 400 एररने उत्तर देतात.
proxy_read_timeout मुळेच "ते लोड होते, पण नंतर अपडेट होणे थांबते" असे अहवाल मिळतात. हे डीफॉल्टनुसार 60 सेकंदांवर असते आणि ते बॅकएंडकडून दोन रीड्समधील अंतर मोजते, कनेक्शनचे एकूण आयुष्य नाही. 60 सेकंद शांत राहणारे WebSocket nginx द्वारे बंद केले जाते आणि ब्राउझर कन्सोलमध्ये सॉकेट 1006 कोडसह बंद झाल्याचे दिसते. जी अॅप्स मिनिटापेक्षा कमी अंतराने स्वतःचे हार्टबीट पाठवतात, त्यांना ही समस्या जाणवत नाही. जी अॅप्स तसे करत नाहीत, ती मिनिटाच्या आत बंद होतात. लाइव्ह एडिटर्स आणि डॅशबोर्डवर ही समस्या प्रथम दिसून येते, ज्याचे एक सामान्य उदाहरण म्हणजे HTTPS च्या मागे असलेले self-hosted n8n instance हे आहे.
proxy_pass मधील ट्रेलिंग स्लॅश (trailing slash) माझ्या URL मध्ये बदल का करतो?
हा नियम एका वाक्याचा आहे. जर proxy_pass मध्ये URI (uniform resource identifier) असेल, अगदी केवळ एक / जरी असेल, तरी nginx विनंतीच्या पाथचा तो भाग काढून टाकतो जो location प्रीफिक्सशी जुळला होता आणि त्या जागी तो URI ठेवतो. जर proxy_pass फक्त होस्ट आणि पोर्टवर थांबत असेल, तर विनंतीचा पाथ जसाच्या तसा पुढे पाठवला जातो.
location /app/ {
proxy_pass http://127.0.0.1:3000/;
}/app/status साठीची विनंती बॅकएंडला /status म्हणून पोहोचते.
location /app/ {
proxy_pass http://127.0.0.1:3000;
}/app/status साठीची विनंती बॅकएंडला /app/status म्हणून पोहोचते.
तुम्हाला कोणता प्रकार हवा आहे हे तुमच्या ॲपवर अवलंबून असते. ज्या ॲपमध्ये बेस-पाथ किंवा सब-फोल्डर सेटिंग असते, त्याला दुसरा प्रकार हवा असतो आणि त्या सेटिंगला /app बद्दल माहिती दिलेली असते. ज्या ॲपला प्रीफिक्सबद्दल काहीही माहिती नसते, त्याला पहिल्या प्रकारची गरज असते. पहिल्या प्रकाराचा एक परिणाम तुम्हाला लगेच दिसून येतो: ॲप जे HTML परत करते त्यात अजूनही /static/main.css सारखे ॲब्सोल्युट पाथ असतात, ब्राउझर त्यांच्यासाठी साइट रूटला विचारणा करतो, तिथे कोणतेही लोकेशन मॅच होत नाही आणि पेज स्टाईलिंगशिवाय दिसते. ब्राउझरच्या नेटवर्क टॅबमध्ये त्या ॲसेट विनंत्या 404 म्हणून परत येतात. यावर उपाय म्हणजे ॲपची स्वतःची बेस-पाथ सेटिंग किंवा त्याच बॅकएंडला पॉइंट करणारा दुसरा location /static/.
regex लोकेशनमध्ये proxy_pass मध्ये URI असू शकत नाही. sudo nginx -t अशा कॉन्फिगरेशनला नाकारते आणि त्याचे कारण स्पष्ट करते: "proxy_pass" cannot have URI part in location given by regular expression, or inside named location, or inside "if" statement, or inside "limit_except" block.
जेव्हा प्रत्येक ॲपला स्वतःचे नाव, app.example.com मिळते, जे location / वरून प्रॉक्सी केले जाते, तेव्हा या प्रकारच्या सर्व समस्या दूर होतात. जेव्हा तुम्ही DNS रेकॉर्ड जोडू शकत नाही, तेव्हाच सब-पाथ वापरणे फायदेशीर ठरते.
एकाच नावामागे एकापेक्षा जास्त बॅकएंड कसे ठेवावेत?
upstream ब्लॉक वापरून हे करता येते. हा ब्लॉक http कॉन्टेक्स्टचा भाग असल्याने, तो त्याच फाईलमध्ये server ब्लॉकच्या वर लिहा किंवा /etc/nginx/conf.d/ मध्ये समाविष्ट करा.
upstream app_backend {
least_conn;
server 127.0.0.1:3000 max_fails=3 fail_timeout=30s;
server 127.0.0.1:3001 max_fails=3 fail_timeout=30s;
keepalive 32;
}त्यानंतर लोकेशनमध्ये त्याचे नाव द्या: proxy_pass http://app_backend;.
डीफॉल्ट पद्धत राउंड रॉबिन (round robin) आहे. least_conn प्रत्येक विनंती अशा बॅकएंडकडे पाठवते ज्यावर सर्वात कमी सक्रिय कनेक्शन आहेत, जे असमान लांबीच्या विनंत्यांसाठी योग्य ठरते. ip_hash एका क्लायंटचा पत्ता एका विशिष्ट बॅकएंडशी जोडते (pin). जेव्हा ॲप स्वतःच्या मेमरीमध्ये सेशन साठवते, तेव्हा तुम्हाला ip_hash ची गरज पडते; कारण अशा दोन बॅकएंड्सवर राउंड रॉबिन वापरल्यास, विनंत्या ज्या इन्स्टन्सवर सेशन नाही तिथे गेल्यामुळे वापरकर्ते अचानक लॉग-आउट होतात. सेशन शेअर केलेल्या स्टोरेजमध्ये हलवणे हा अधिक चांगला उपाय आहे.
max_fails=3 fail_timeout=30s चा अर्थ असा की 30 सेकंदात तीन वेळा अपयशी प्रयत्न झाल्यास, तो सर्व्हर 30 सेकंदांसाठी काढून टाकला जातो. जेव्हा ब्लॉकमधील सर्व सर्व्हर या स्थितीत असतात, तेव्हा क्लायंटला 502 एरर मिळते आणि एरर लॉगमध्ये no live upstreams while connecting to upstream असे दिसते.
keepalive 32 प्रति वर्कर प्रोसेस 32 आयडल (idle) कनेक्शन बॅकएंडकडे उघडे ठेवते, ज्यामुळे बहुतेक विनंत्यांसाठी TCP हँडशेकची गरज उरत नाही. हे फक्त proxy_http_version 1.1 सोबत आणि अपस्ट्रीममध्ये Connection: close नसल्यासच काम करते. जर त्याच लोकेशनमध्ये WebSocket मॅप वापरला असेल, तर रिकाम्या केसला close वरून रिकाम्या स्ट्रिंगमध्ये बदला, जेणेकरून सामान्य विनंत्यांमध्ये Connection हेडर नसेल आणि पूल्ड कनेक्शन पुन्हा वापरता येईल.
map $http_upgrade $connection_upgrade {
default upgrade;
'' '';
}upstream ब्लॉकमधील नावे Nginx सुरू होताना रिझॉल्व्ह केली जातात. जर तुमचा बॅकएंड एक कंटेनर असेल ज्याला रीस्टार्ट झाल्यावर नवीन पत्ता मिळतो, तर Nginx तोपर्यंत जुनाच पत्ता वापरत राहील जोपर्यंत तुम्ही ते रीलोड करत नाही. Docker नेटवर्कमध्ये तुम्ही एम्बेड केलेल्या रिझॉल्व्हरचा वापर करून ही प्रक्रिया विनंतीच्या वेळी (request time) करू शकता.
resolver 127.0.0.11 valid=10s;
set $backend http://app:3000;
proxy_pass $backend;जेव्हा कंटेनर वारंवार तयार होतात आणि नष्ट होतात, तेव्हा Nginx मध्ये सतत बदल करण्याऐवजी कंटेनर लेबल्स वाचणारा प्रॉक्सी वापरणे अधिक सोयीचे ठरते. अनेक Docker Compose ॲप्सच्या पुढे Traefik हे कंटेनरवरूनच स्वतःचे रूट्स तयार करते.
अपलोड करताना 413 Request Entity Too Large ही त्रुटी का येते?
client_max_body_size डीफॉल्टनुसार 1 megabyte इतके असते. तुमच्या ॲप्लिकेशनपर्यंत विनंती पोहोचण्यापूर्वीच nginx मोठ्या आकाराच्या request body ला नाकारते आणि एरर लॉगमध्ये client intended to send too large body अशी नोंद होते. ही मर्यादा वाढवण्यासाठी server block किंवा ज्या location वर अपलोड होतात तिथे बदल करा.
client_max_body_size 512m;0 हे मूल्य सेट केल्यास ही तपासणी पूर्णपणे बंद होते. ॲप्लिकेशनची स्वतःची मर्यादा देखील असते, त्यामुळे हा बदल केल्यानंतरही 413 त्रुटी येत असेल, तर ती backend कडून येत आहे असे समजावे आणि ॲप्लिकेशनच्या अपलोड सेटिंग्ज तपासाव्यात.
डीफॉल्टनुसार, nginx अपस्ट्रीम कनेक्शन उघडण्यापूर्वी संपूर्ण request body वाचते आणि मोठ्या फाईल्स आधी डिस्कवरील temporary फाईलमध्ये लिहिते. यामुळे ॲप्लिकेशनला संथ क्लायंटपासून संरक्षण मिळते, कारण backend ला अपलोड पूर्ण स्थानिक वेगाने प्राप्त होते. खूप मोठ्या अपलोडसाठी तुम्ही त्याऐवजी स्ट्रीमिंग वापरू शकता.
proxy_request_buffering off;अशा वेळी backend ला body जशी येते तशी प्राप्त होते आणि ती हाताळण्याची क्षमता backend मध्ये असणे आवश्यक आहे. तसेच, body आधीच पाठवली गेल्यामुळे nginx ला ती विनंती दुसऱ्या अपस्ट्रीमवर पुन्हा पाठवण्याची (retry) क्षमता राहत नाही.
client_body_timeout, जे डीफॉल्टनुसार 60 seconds असते, ते संपूर्ण अपलोडसाठी नसून body च्या दोन सलग वाचनांमधील वेळेसाठी लागू होते. संथ पण सातत्यपूर्ण अपलोड या मर्यादेत टिकून राहतात. मात्र, प्रक्रिया थांबल्यास (stalled) कनेक्शन तोडले जाते.
रिस्पॉन्स बफरिंग आणि लाइव्ह आउटपुट खंडित करणारी सेटिंग
proxy_buffering हे डीफॉल्टनुसार सुरू असते आणि सामान्यतः तुम्हाला तेच हवे असते. Nginx तुमच्या ॲपकडून येणारा रिस्पॉन्स ॲपच्या वेगाप्रमाणे वाचते, तो साठवून ठेवते आणि संथ क्लायंटला त्यांच्या गतीनुसार पुरवते. यामुळे ॲप वर्कर पूर्ण डाउनलोड संपेपर्यंत व्यस्त न राहता लवकर मोकळा होतो.
हे स्ट्रीमिंग रिस्पॉन्स खंडित करते. Server-sent events आणि लाइव्ह लॉग आउटपुटमध्ये बफर भरेपर्यंत वाचकाला काहीही दिसत नाही. अशा ठिकाणी बफरिंग बंद करा.
proxy_buffering off;जर तुमचे ॲपवर नियंत्रण असेल, तर स्ट्रीमिंग रिस्पॉन्ससाठी फक्त X-Accel-Buffering: no हेडर पाठवणे अधिक चांगले ठरते. Nginx प्रत्येक रिस्पॉन्ससाठी ते हेडर वाचते आणि फक्त त्याच रिस्पॉन्ससाठी बफरिंग बंद करते, ज्यामुळे सामान्य पेजेसना बफरिंगचा फायदा मिळत राहतो.
जेव्हा एरर लॉगमध्ये upstream sent too big header while reading response header from upstream असा संदेश येतो, तेव्हा रिस्पॉन्स हेडर्स एका बफरमध्ये मावत नाहीत. proxy_buffer_size हे डीफॉल्टनुसार एका मेमरी पेजच्या आकाराचे असते (प्लॅटफॉर्मनुसार 4 किंवा 8 किलोबाइट्स), आणि लांब कुकीज किंवा मोठे ऑथेंटिकेशन हेडर्समुळे ते ओव्हरफ्लो होते. या दोन्ही व्हॅल्यूज वाढवा.
proxy_buffer_size 16k;
proxy_buffers 8 16k;या कॉन्फिगरेशनमध्ये TLS कोठे असावे?
Nginx वर, वरील सर्व गोष्टींच्या समोर. TLS (transport layer security) हे प्रॉक्सीवर टर्मिनेट होते आणि Nginx कडून ॲपकडे जाणारे कनेक्शन लूपबॅक ॲड्रेसवर साध्या HTTP स्वरूपात राहते, जिथे नेटवर्कवरील इतर कोणतीही गोष्ट ते वाचू शकत नाही. अभ्यागताने HTTPS वापरले आहे हे ॲपला X-Forwarded-Proto या चार हेडर्सपैकी चौथ्या हेडरावरून समजते.
सर्टिफिकेटचे पाथ स्वतः लिहू नका. DNS रेकॉर्ड सर्व्हरकडे पॉइंट करा, फायरवॉल उघडा आणि Certbot ला हाच सर्व्हर ब्लॉक एडिट करू द्या: ते ssl_certificate पाथसह listen 443 ssl ओळ जोडते, तसेच पोर्ट 80 वरून रिडायरेक्ट करते. Certbot वापरून Nginx साठी Let's Encrypt सर्टिफिकेट जारी करणे मध्ये इश्यूअन्स आणि रिन्यूअल टायमरची माहिती दिली आहे.
sudo ufw allow 'Nginx Full'
sudo ufw statusNginx Full हे एक ॲप्लिकेशन प्रोफाइल आहे जे Nginx पॅकेज इन्स्टॉल करते आणि ते पोर्ट 80 आणि पोर्ट 443 दोन्ही एकत्र उघडते. प्रत्येक अभ्यागताला HTTPS वर रिडायरेक्ट केल्यानंतरही, HTTP-01 रिन्यूअल चॅलेंजसाठी पोर्ट 80 उघडे ठेवणे आवश्यक आहे.
कॉन्फिगरेशन तपासा आणि मग रीलोड करा
sudo nginx -t
sudo systemctl reload nginxnginx -t प्रत्येक समाविष्ट केलेली फाईल पार्स करते आणि चाचणी यशस्वी झाल्याचा अहवाल देते किंवा ज्या फाईलमध्ये आणि ओळीवर प्रक्रिया थांबली आहे, ती दर्शवते. रीलोड करण्यापूर्वी ते आउटपुट वाचा. सदोष कॉन्फिगरेशनसह रीलोड केल्यास ते लागू होत नाही: nginx मागील कॉन्फिगरेशननुसारच सेवा देत राहते, त्यामुळे तुमचे बदल काहीही परिणाम करत नाहीत आणि साईट सुरू राहते. systemctl restart वेगळ्या आणि अधिक त्रासदायक पद्धतीने काम करते, कारण रीस्टार्ट केल्यास चालू असलेला सर्व्हर आधी बंद केला जातो. त्यामुळे कॉन्फिगरेशनमध्ये चूक असल्यास nginx पूर्णपणे बंद पडते. शक्यतो रीलोड करा आणि केवळ आवश्यक असेल तेव्हाच रीस्टार्टचा वापर करा.
sudo tail -f /var/log/nginx/error.log
sudo ss -lntp | grep -E ':(80|443|3000)'ss ओळ कोणता प्रोसेस कोणत्या पोर्टवर कार्यरत आहे हे दर्शवते, जेणेकरून proxy_pass ज्या ठिकाणी निर्देशित करते, तिथेच ॲप खरोखर ऐकत (listening) आहे याची तुम्ही खात्री करू शकता.
तुम्हाला प्रत्यक्षपणे जाणवणारे बिघाड
502 Bad Gateway, एरर लॉगमध्ये connect() failed (111: Connection refused) while connecting to upstream सह. proxy_pass वरील पत्त्यावर कोणतीही सेवा विनंती स्वीकारत नाही (listening). ॲप बंद आहे, किंवा ते दुसऱ्या पोर्टवर कार्यरत आहे, किंवा ते अशा कंटेनर-अंतर्गत पत्त्यावर बाइंड झाले आहे जिथे होस्ट पोहोचू शकत नाही.
no live upstreams while connecting to upstream सह 502 एरर. upstream ब्लॉकमधील प्रत्येक सर्व्हर सध्या max_fails द्वारे 'failed' म्हणून चिन्हांकित केला आहे. बॅकएंड दुरुस्त करा. fail_timeout ची मुदत संपल्यावर Nginx पुन्हा प्रयत्न करेल.
504 Gateway Time-out, upstream timed out (110: Connection timed out) while reading response header from upstream सह. बॅकएंडने कनेक्शन स्वीकारले, परंतु proxy_read_timeout सेकंदांपर्यंत कोणताही प्रतिसाद दिला नाही. जर रिपोर्ट खरोखरच संथ असेल तर टाइमआउट वाढवणे योग्य आहे, परंतु जर ॲप अडकले असेल तर ते चुकीचे आहे.
प्रत्येक पाथवरून ॲपकडून 404 एरर मिळणे. ट्रेलिंग स्लॅशच्या नियमाने पाथ पुन्हा लिहिला (rewrite) आहे. ॲप लॉगमध्ये दिसणारा पाथ आणि तुम्ही विनंती केलेला पाथ यांची तुलना करा.
दुसरीच साईट प्रतिसाद देत आहे. server_name हे Host हेडरशी जुळत नाही, त्यामुळे विनंती default_server ब्लॉकमध्ये गेली आहे.
पेज लोड होते, पण एका मिनिटानंतर इंटरफेस फ्रीझ होतो. हे WebSocket चे प्रकरण आहे: Upgrade हाताळणी (handling) गहाळ आहे, किंवा proxy_read_timeout अजूनही 60 सेकंदांवर सेट आहे.
FAQ
मी proxy_pass जोडल्यानंतर nginx 502 Bad Gateway एरर का देतो?
nginx proxy_pass मध्ये दिलेल्या पत्त्यावर कनेक्शन प्रस्थापित करू शकत नाही. /var/log/nginx/error.log वरील एरर लॉगमध्ये याचे कारण असते: connect() failed (111: Connection refused) while connecting to upstream चा अर्थ तिथे कोणतीही सेवा सुरू नाही, आणि no live upstreams चा अर्थ upstream ब्लॉक मधील सर्व सर्व्हर्स 'failed' म्हणून चिन्हांकित केले आहेत. कोणता प्रोसेस पोर्ट वापरत आहे आणि तो कोणत्या पत्त्यावर बाइंड झाला आहे हे पाहण्यासाठी sudo ss -lntp | grep 3000 चालवा. जर एखादे ॲप कंटेनरच्या अंतर्गत पत्त्यावर किंवा तुम्ही लिहिलेल्या पोर्टपेक्षा वेगळ्या पोर्टवर बाइंड असेल, तर ही एरर येते.
nginx च्या मागे माझे ॲप एका मिनिटानंतर डिस्कनेक्ट का होते?
हे कनेक्शन WebSocket असते आणि proxy_read_timeout ची डीफॉल्ट वेळ 60 सेकंद असते, जी बॅकएंडकडून येणाऱ्या दोन रीड्समधील अंतर मोजते. शांत सॉकेट nginx द्वारे बंद केले जाते आणि ब्राउझर कन्सोल 1006 हा क्लोज कोड दाखवतो. proxy_http_version 1.1 सेट करा, Upgrade आणि Connection हे हेडर map वापरून $http_upgrade वर पास करा आणि proxy_read_timeout ची वेळ वाढवून 3600s करा. Upgrade हेडरशिवाय अपग्रेड होत नाही, त्यामुळे ॲप पोलिंगवर परत जाते किंवा लाइव्ह अपडेट्स दाखवत नाही.
proxy_pass मधील शेवटचा स्लॅश (trailing slash) महत्त्वाचा आहे का?
हो, तो बॅकएंडला मिळणारा पाथ बदलतो. location /app/ आणि proxy_pass http://127.0.0.1:3000/ सह, /app/status साठीची विनंती बॅकएंडला /status म्हणून मिळते, कारण होस्ट आणि पोर्टनंतरचा कोणताही URI मॅच झालेला लोकेशन प्रीफिक्स बदलतो. तो शेवटचा स्लॅश काढून टाकल्यास तीच विनंती /app/status म्हणून पोहोचते. प्रीफिक्स काढून टाकल्यामुळे अनेकदा ॲपच्या ॲसेट लिंक्स तुटतात, ज्या ॲब्सोल्युट राहतात आणि साइट रूटवर 404 एरर देतात. त्यामुळे ज्या ॲपमध्ये बेस-पाथ सेटिंग आहे, तिथे पाथ जसाच्या तसा पास करणारा फॉरमॅट वापरणे अधिक चांगले असते.
माझे ॲप्लिकेशन प्रत्येक अभ्यागताचा IP पत्ता 127.0.0.1 असा का लॉग करते?
कारण ॲपला मिळणारे कनेक्शन प्रत्यक्षात लूपबॅक पत्त्यावरून nginx कडून येते. अभ्यागताचा पत्ता ॲपला फक्त तुम्ही सेट केलेल्या हेडरद्वारे मिळतो: एका व्हॅल्यूसाठी proxy_set_header X-Real-IP $remote_addr; आणि अपेंडेड चेनसाठी proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;. त्यानंतर ॲपला हे हेडर ट्रस्ट करण्यासाठी कॉन्फिगर करावे लागते. लक्षात ठेवा की क्लायंट स्वतःचे X-Forwarded-For हेडर पाठवू शकतो, म्हणून जेव्हा nginx एज सर्व्हर असतो, तेव्हा ते अपेंड करण्याऐवजी $remote_addr ने ओव्हरराईट करा.
nginx आणि माझ्या ॲपमधील कनेक्शनसाठी मला TLS ची गरज आहे का?
जेव्हा ॲप त्याच सर्व्हरवर चालते आणि 127.0.0.1 वर बाइंड असते, तेव्हा गरज नसते, कारण ते ट्रॅफिक मशीनच्या बाहेर जात नाही. TLS चे टर्मिनेशन nginx वर करा, proxy_pass लूपबॅकवर प्लेन HTTP ठेवा आणि X-Forwarded-Proto $scheme पाठवा जेणेकरून ॲपला समजेल की अभ्यागताने HTTPS वापरले आहे. जर बॅकएंड तुमच्या नियंत्रणाबाहेरील नेटवर्कवर दुसऱ्या होस्टवर असेल, तर त्या कनेक्शनला स्वतःच्या सुरक्षेची गरज असते, मग ती बॅकएंडसाठी HTTPS असो किंवा दोन मशीनमधील प्रायव्हेट टनेल.