Nginx Reverse Proxy สำหรับ Node.js และ Python บน VPS
แอปพลิเคชัน Node.js, Python Flask/Django หรือ Go ส่วนใหญ่รันบน port สูง เช่น 3000 หรือ 8000 ซึ่งผู้ใช้ต้องพิมพ์ port ในชื่อโดเมน Nginx Reverse Proxy แก้ปัญหานี้โดยรับ request จาก port 80/443 แล้วส่งต่อไปยังแอป
Reverse Proxy คืออะไร
Reverse Proxy ทำหน้าที่เป็นตัวกลางระหว่าง client กับ backend application:
- Client ส่ง request มาที่ Nginx port 443 (HTTPS)
- Nginx ส่งต่อไปยัง Node.js ที่รันบน port 3000 ของ localhost
- Nginx รับ response จาก Node.js แล้วส่งกลับให้ client
ผลคือ client เห็นแค่ https://myapp.com ไม่รู้ว่า backend รันบน port อะไร
Config พื้นฐาน: Node.js บน port 3000
สร้างไฟล์ /etc/nginx/sites-available/myapp:
server {
listen 80;
server_name myapp.com www.myapp.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name myapp.com www.myapp.com;
ssl_certificate /etc/letsencrypt/live/myapp.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/myapp.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
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_cache_bypass $http_upgrade;
}
}
ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
nginx -t
systemctl reload nginx
Config สำหรับ Python Gunicorn
Python app (Flask/Django) มักรันผ่าน Gunicorn บน Unix socket ซึ่งเร็วกว่า TCP port:
location / {
proxy_pass http://unix:/run/gunicorn/myapp.sock;
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;
}
รัน Multiple Apps บน Domain เดียว
สามารถแยก path ไปยังหลาย backend ได้:
server {
listen 443 ssl;
server_name myapp.com;
# Frontend React บน port 3000
location / {
proxy_pass http://127.0.0.1:3000;
}
# API Node.js บน port 4000
location /api/ {
proxy_pass http://127.0.0.1:4000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# Static files serve โดย Nginx โดยตรง
location /static/ {
alias /var/www/myapp/static/;
expires 30d;
}
}
Timeout Settings สำหรับแอปที่ประมวลผลนาน
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
ถ้าแอปทำงานนานกว่า 60 วินาที (เช่น video processing) ต้องเพิ่มค่านี้ ไม่งั้น Nginx จะ timeout ก่อน
Header ที่สำคัญสำหรับ IP Logging
แอปที่ต้องการ IP จริงของ client ต้องอ่านจาก X-Real-IP หรือ X-Forwarded-For แทน เพราะ Nginx เป็นตัวที่เชื่อมต่อกับ backend ไม่ใช่ client โดยตรง
สรุป
Nginx Reverse Proxy คือ pattern มาตรฐานสำหรับ deploy web application บน VPS รวม SSL, compression, static file caching และ load balancing ไว้ในจุดเดียว โดยที่ application ไม่ต้องรู้เรื่องเหล่านี้เลย
ตรวจสอบว่า Reverse Proxy ทำงานถูกต้อง
# ทดสอบ config ก่อน reload
nginx -t
# reload config
systemctl reload nginx
# ตรวจว่า request ถูก proxy ไปถูกต้อง
curl -I https://myapp.com
# ดู header X-Powered-By หรือ Server เพื่อยืนยัน backend
# ตรวจ error log
tail -f /var/log/nginx/error.log
X-Forwarded-For หรือ X-Real-IP ถูกต้องแก้ปัญหาที่พบบ่อย
Error: "502 Bad Gateway"
Backend app ไม่ได้รัน หรือ port/socket ไม่ตรง:
# ตรวจว่า app รันอยู่ไหม
ss -tlnp | grep :3000 # Node.js บน port 3000
ps aux | grep node
# ทดสอบ connect ตรงโดยไม่ผ่าน Nginx
curl http://127.0.0.1:3000
# ดู error log
tail -50 /var/log/nginx/error.log
Error: "504 Gateway Timeout"
Backend ตอบกลับช้าเกิน proxy timeout ที่ตั้ง (default 60s):
# เพิ่ม timeout ใน nginx config
proxy_connect_timeout 120s;
proxy_send_timeout 120s;
proxy_read_timeout 120s;
nginx -t && systemctl reload nginx
Error: "413 Request Entity Too Large" ตอน upload ไฟล์
# เพิ่มใน server block หรือ location
client_max_body_size 50M;
nginx -t && systemctl reload nginx
WebSocket connection ถูกตัดทุก 60 วินาที
Nginx ต้องตั้ง Connection upgrade สำหรับ WebSocket โดยเฉพาะ:
location /ws/ {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 3600s; # ไม่ timeout
}
SSL certificate error หลัง renew Certbot
# ทดสอบ renew
certbot renew --dry-run
# reload nginx หลัง renew
systemctl reload nginx
# ตรวจ cert expiry
echo | openssl s_client -connect myapp.com:443 2>/dev/null | openssl x509 -noout -dates
คำถามที่พบบ่อย
หลาย domain ชี้ไป app เดียวได้ไหม?
ได้ ตั้ง server_name domain1.com www.domain1.com domain2.com รวมกันใน server block เดียว หรือสร้างหลาย server block แยกกัน แต่ละตัว proxy ไป backend เดิม
จะรู้ได้ยังไงว่า Nginx proxy ไปถูก backend?
ดู $upstream_addr ใน access log หรือเพิ่ม header debug ชั่วคราว add_header X-Debug-Upstream $upstream_addr; แล้วดู response header ด้วย curl
Nginx และ Apache รันบนเครื่องเดียวกันได้ไหม?
ได้ ให้ Nginx ฟัง port 80/443 และ Apache ฟัง port 8080 Nginx รับ request แล้ว proxy ไป Apache ที่อยู่หลัง ทำให้ได้ประโยชน์ทั้งสองอย่าง