Nginx

Nginx กับ Node.js Application — Reverse Proxy สำหรับ Node.js

Nginx (Engine X) เป็นหนึ่งในเว็บเซิร์ฟเวอร์ที่นิยมมากที่สุดในการจัดการ Node.js application แม้ว่า Node.js สามารถทำหน้าที่เป็นเว็บเซิร์ฟเวอร์ได้ด้วยตัวเอง แต่การใช้ Nginx เป็น reverse proxy ข้างหน้า Node.js มีข้อดีมากมาย เช่น การจัดการ SSL/TLS, load balancing, caching, และการจัดการความปลอดภัย บทความนี้จะสอนวิธีการตั้งค่า Nginx กับ Node.js application อย่างเหมาะสม พร้อมเทคนิคต่างๆ ที่ช่วยให้ application ของคุณทำงานได้อย่างเสถียรและมีประสิทธิภาพสูง

ทำไมต้องใช้ Nginx กับ Node.js

Node.js ถูกออกแบบมาให้เป็น runtime environment สำหรับ JavaScript ไม่ใช่เว็บเซิร์ฟเวอร์สมบูรณ์รูปแบบ การใช้ Nginx เป็น reverse proxy นอกหน้า Node.js นั้นเป็นแนวทางปฏิบัติที่ดี (best practice) ในอุตสาหกรรม เนื่องจากมีข้อดีดังต่อไปนี้

  • SSL/TLS Termination: Nginx สามารถจัดการการเข้ารหัส HTTPS ได้ ลดภาระจาก Node.js application
  • Load Balancing: กระจายการร้องขอไปยัง Node.js instances หลายตัว เพิ่มความสามารถในการรับจำนวนผู้ใช้งานมากขึ้น
  • Static File Serving: Nginx ดีที่สุดในการให้บริการไฟล์ static (CSS, JavaScript, images) โดยไม่ต้องให้ Node.js ทำงาน
  • Caching: เก็บ cache ของผลการตอบสนองเพื่อลดการเรียกใช้งาน Node.js application
  • Security: ทำหน้าที่เป็น gateway ป้องกัน Node.js จากการโจมตีโดยตรง
  • Gzip Compression: บีบอัดข้อมูลโดยอัตโนมัติเพื่อลดขนาดการส่งข้อมูล
  • Rate Limiting: ควบคุมจำนวนการร้องขออัตราการเข้าถึง API

การตั้งค่า Nginx Reverse Proxy พื้นฐาน

เมื่อต้องการตั้งค่า Nginx เป็น reverse proxy สำหรับ Node.js application ขั้นแรกต้องแก้ไขไฟล์ config ของ Nginx ที่เส้นทาง /etc/nginx/nginx.conf หรือสร้างไฟล์ config ใหม่ใน /etc/nginx/sites-available/

ตัวอย่างการตั้งค่า Basic Reverse Proxy

server {
    listen 80;
    server_name example.com www.example.com;

    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

ในการตั้งค่านี้ Nginx จะรับการร้องขอทั้งหมดที่มาที่พอร์ต 80 (HTTP) แล้วส่งต่อไปยัง Node.js application ที่รันอยู่บนพอร์ต 3000 (localhost:3000)

Proxy Headers และการส่งข้อมูล Client

เมื่อใช้ Nginx เป็น reverse proxy สำคัญที่สุดคือต้องส่งข้อมูล HTTP headers ที่สำคัญไปยัง Node.js เพื่อให้ application สามารถได้รับข้อมูลจริงของ client เช่น IP address และข้อมูล HTTPS ต้นแบบ

Headers ที่สำคัญ

location / {
    proxy_pass http://localhost:3000;
    proxy_http_version 1.1;

    # ส่ง client IP address จริง
    proxy_set_header X-Real-IP $remote_addr;

    # ส่ง chain ของ IP addresses
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    # ส่ง protocol ต้นแบบ (http หรือ https)
    proxy_set_header X-Forwarded-Proto $scheme;

    # ส่ง Host header
    proxy_set_header Host $host;

    # สำหรับ WebSocket
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

ในโค้ด Express.js หรือ Node.js framework อื่นๆ สามารถเข้าถึง client IP address ได้จากค่าเหล่านี้ด้วยการตั้งค่า trust proxy

การให้บริการไฟล์ Static กับ Nginx

หนึ่งในประโยชน์ใหญ่ของการใช้ Nginx กับ Node.js คือการให้บริการไฟล์ static เช่น CSS, JavaScript, และรูปภาพได้อย่างมีประสิทธิภาพสูงกว่า ไม่ต้องให้ Node.js application ทำงาน

server {
    listen 80;
    server_name example.com;

    # ให้บริการไฟล์ static โดยตรง
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
        root /var/www/myapp/public;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }

    # ส่งต่อไปยัง Node.js
    location / {
        proxy_pass http://localhost: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;
    }
}

Load Balancing หลาย Node.js Instances

เมื่อ Node.js application รับจำนวน traffic มากขึ้น เราสามารถรัน Node.js หลายตัวบนพอร์ตต่างกัน และให้ Nginx ทำหน้าที่ load balancer เพื่อกระจายการร้องขออย่างเท่าเทียม

upstream nodejs_app {
    server localhost:3000;
    server localhost:3001;
    server localhost:3002;
    server localhost:3003;
}

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://nodejs_app;
        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_cache_bypass $http_upgrade;
    }
}

Nginx จะใช้ round-robin load balancing โดยค่าเริ่มต้น ส่งการร้องขอแต่ละรายการไปยัง Node.js instance ต่อไปตามลำดับ เราสามารถปรับเป็น least_conn (เลือก instance ที่มีการเชื่อมต่อน้อยที่สุด) ได้โดยเพิ่ม least_conn; ลงในบล็อก upstream

PM2 Cluster Mode กับ Nginx

PM2 เป็นตัวจัดการ process สำหรับ Node.js ที่รองรับ cluster mode ซึ่งช่วยให้ application ใช้ประโยชน์จากหลายตัวประมวลผล (CPU cores) ได้อย่างเต็มที่ สามารถรวมกับ Nginx ได้อย่างสมบูรณ์แบบ

# ecosystem.config.js
module.exports = {
  apps: [{
    name: 'nodejs-app',
    script: './app.js',
    instances: 'max',  // ใช้จำนวน CPU cores
    exec_mode: 'cluster',
    env: {
      NODE_ENV: 'production',
      PORT: 3000
    }
  }]
};

เมื่อรัน PM2 ในโหมด cluster พร้อมกับการตั้งค่า Nginx reverse proxy ข้างบน application จะสามารถรับจำนวน traffic มากขึ้นได้อย่างมีประสิทธิภาพ

WebSocket Support สำหรับ Socket.IO

Socket.IO ใช้ WebSocket เพื่อสื่อสารแบบ real-time การตั้งค่า Nginx เพื่อรองรับ WebSocket ต้องเพิ่ม headers เพิ่มเติม โดยเฉพาะ Upgrade และ Connection headers

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;

        # สำคัญสำหรับ WebSocket
        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;

        # เพิ่ม timeout สำหรับ long-lived connections
        proxy_read_timeout 86400;
    }
}

SSL/TLS Configuration กับ Let’s Encrypt

การตั้งค่า HTTPS ด้วย SSL/TLS certificate ถือเป็นสิ่งจำเป็น บ่อยครั้งหลายท่านใช้ Certbot เพื่อขอ certificate ฟรีจาก Let’s Encrypt

# ติดตั้ง Certbot สำหรับ Nginx
sudo apt-get install certbot python3-certbot-nginx
sudo certbot certonly --nginx -d example.com -d www.example.com

# ไฟล์ config Nginx ที่มี HTTPS
server {
    listen 443 ssl http2;
    server_name example.com www.example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # ตั้งค่า SSL best practices
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;

    location / {
        proxy_pass http://localhost:3000;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

# Redirect HTTP to HTTPS
server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$server_name$request_uri;
}

Caching Static Assets และ API Responses

Nginx สามารถเก็บ cache ของการตอบสนอง HTTP ได้ ซึ่งช่วยลดการโหลด Node.js application อย่างมีนัยสำคัญ โดยเฉพาะสำหรับข้อมูล static หรือ API responses ที่เปลี่ยนแปลงไม่บ่อย

http {
    # สร้าง cache zone
    proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=api_cache:10m max_size=1g inactive=60m;

    upstream nodejs_app {
        server localhost:3000;
    }

    server {
        listen 80;
        server_name example.com;

        # Cache API responses
        location /api/ {
            proxy_pass http://nodejs_app;
            proxy_cache api_cache;
            proxy_cache_valid 200 10m;
            proxy_cache_valid 404 1m;

            # เพิ่ม cache status ใน response header
            add_header X-Cache-Status $upstream_cache_status;

            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }

        # Cache static assets
        location ~* \.(js|css|png|jpg|gif|ico)$ {
            root /var/www/myapp/public;
            expires 30d;
            add_header Cache-Control "public, immutable";
        }
    }
}

Gzip Compression สำหรับ Performance

การบีบอัดข้อมูลด้วย gzip จะลดขนาดของการส่งข้อมูลถึง 70% ได้ Nginx สามารถบีบอัดข้อมูลได้อัตโนมัติจากการตอบสนองของ Node.js

http {
    gzip on;
    gzip_vary on;
    gzip_min_length 1000;
    gzip_proxied any;
    gzip_types text/plain text/css text/xml text/javascript
               application/x-javascript application/xml+rss
               application/json application/javascript;

    # Disable gzip สำหรับ old browsers
    gzip_disable "MSIE [1-6]\.(?!.*SV1)";

    server {
        listen 80;
        server_name example.com;

        location / {
            proxy_pass http://localhost:3000;
            proxy_set_header Host $host;
        }
    }
}

Rate Limiting สำหรับ API Protection

Rate limiting ช่วยป้องกัน API จากการใช้งานมากเกินไป หรือการโจมตี DDoS ด้วยการจำกัดจำนวน requests ต่อ IP address ต่อช่วงเวลา

http {
    # สร้าง rate limit zone
    limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
    limit_req_zone $binary_remote_addr zone=general_limit:10m rate=30r/s;

    server {
        listen 80;
        server_name example.com;

        # Rate limit API endpoints
        location /api/ {
            limit_req zone=api_limit burst=20 nodelay;
            proxy_pass http://localhost:3000;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }

        # Rate limit ทั่วไป
        location / {
            limit_req zone=general_limit burst=50 nodelay;
            proxy_pass http://localhost:3000;
            proxy_set_header Host $host;
        }
    }
}

Health Checks สำหรับ Load Balancing

เมื่อใช้ load balancing กับหลาย Node.js instances จำเป็นต้องตรวจสอบว่า instance ใดยังทำงานอยู่หรือไม่ Nginx จะลบ instance ที่ down ออกจากการไหลเวียนโดยอัตโนมัติ

upstream nodejs_app {
    server localhost:3000 max_fails=3 fail_timeout=30s;
    server localhost:3001 max_fails=3 fail_timeout=30s;
    server localhost:3002 max_fails=3 fail_timeout=30s;
}

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://nodejs_app;
        proxy_connect_timeout 5s;
        proxy_send_timeout 5s;
        proxy_read_timeout 5s;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Zero-Downtime Deployments

ด้วย Nginx load balancing สามารถทำ zero-downtime deployment ได้โดยทำการ deploy ไปยัง Node.js instances ทีละตัว ขณะที่ Nginx ยังคงส่งการร้องขอไปยัง instances ที่เหลือ

# ขั้นตอน:
# 1. Deploy code ไปยัง instance 1 แล้ว restart
# 2. ส่วน instance 2,3,4 ยังทำงานต่อและรับการร้องขอ
# 3. Deploy ไปยัง instance 2 แล้ว restart
# 4. และสำเร็จไปทีละตัว

# ใช้ PM2 สำหรับ zero-downtime reload
pm2 reload all

# หรือใช้ PM2 watch mode
pm2 start app.js --watch

Docker + Nginx + Node.js Architecture

การใช้ Docker ร่วมกับ Nginx และ Node.js ถือเป็นวิธีการ deploy ที่ยืดหยุ่นและนำมาใช้ได้ง่ายในสภาพแวดล้อมที่แตกต่างกัน

version: '3.8'

services:
  nginx:
    image: nginx:latest
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./ssl:/etc/nginx/ssl:ro
    depends_on:
      - app1
      - app2
      - app3

  app1:
    build: .
    environment:
      - PORT=3000
    ports:
      - "3000:3000"

  app2:
    build: .
    environment:
      - PORT=3001
    ports:
      - "3001:3001"

  app3:
    build: .
    environment:
      - PORT=3002
    ports:
      - "3002:3002"

Monitoring และ Logging

การ monitor performance ของ Nginx และ Node.js application เป็นสิ่งสำคัญเพื่อให้สามารถตรวจจับปัญหาได้เร็วและแก้ไขอย่างทันท่วงที

# เปิด Nginx stub_status สำหรับ monitoring
server {
    listen 127.0.0.1:8080;

    location /nginx_status {
        stub_status on;
        access_log off;
        allow 127.0.0.1;
        deny all;
    }
}

# ตั้งค่า Nginx access log format ให้ส่วนประเมินได้
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                '$status $body_bytes_sent "$http_referer" '
                '"$http_user_agent" "$http_x_forwarded_for" '
                'rt=$request_time uct="$upstream_connect_time" '
                'uht="$upstream_header_time" urt="$upstream_response_time"';

access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log warn;

ปัญหาทั่วไปและวิธีแก้ไข

502 Bad Gateway Error

ข้อผิดพลาด 502 Bad Gateway เกิดขึ้นเมื่อ Nginx ไม่สามารถติดต่อ Node.js application ได้ สาเหตุอาจเป็น

  • Node.js application ไม่ทำงาน — ตรวจสอบด้วย ps aux หรือ pm2 list
  • Nginx เชื่อมต่อ port ผิด — ตรวจสอบที่ proxy_pass
  • Timeout — เพิ่ม timeout values ใน Nginx config

X-Forwarded-For Header หายไป

ถ้า Node.js app ไม่ได้ IP address จริงของ client โปรแกรมเมอร์มักลืมตั้งค่า trust proxy ใน Express.js

// Express.js
app.set('trust proxy', 1);

// ตอนนี้ req.ip จะได้ค่า client IP จริง
app.get('/', (req, res) => {
  console.log('Client IP:', req.ip);
});

WebSocket Connection Failed

ถ้า Socket.IO ไม่สามารถเชื่อมต่อ WebSocket ได้ ต้องตรวจสอบว่า Upgrade และ Connection headers ถูกตั้งค่าอย่างถูกต้องในการ config Nginx

// ตัวอย่าง Nginx config ที่ผิด (ขาดหาย headers)
location / {
    proxy_pass http://localhost:3000;
    # หาก Upgrade และ Connection headers ขาดหาย WebSocket จะ downgrade เป็น polling
}

// ถูกต้อง
location / {
    proxy_pass http://localhost:3000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

Best Practices สำหรับ Nginx + Node.js

  • ใช้ upstream blocks: จัดการ backend servers ได้ง่ายขึ้นผ่าน upstream blocks แทนการใช้ proxy_pass โดยตรง
  • ตั้งค่า timeouts อย่างเหมาะสม: หากไม่ตั้งค่า timeout Nginx อาจสะดุดเมื่อการตอบสนองช้า
  • ใช้ gzip compression: บีบอัดข้อมูลเพื่อลดการใช้ bandwidth
  • Implement caching strategy: กำหนดกลยุทธ์ cache ที่เหมาะสมสำหรับ API responses
  • Monitor performance: ติดตั้ง monitoring tools เพื่อติดตามประสิทธิภาพ
  • ตั้งค่า rate limiting: ป้องกัน abuse และ DDoS attacks
  • ใช้ HTTPS เสมอ: ในสภาพแวดล้อม production ต้องใช้ SSL/TLS certificates
  • ทำ load balancing: กระจายการร้องขอไปยัง Node.js instances หลายตัว
  • ตั้งค่า health checks: สำหรับให้ Nginx รู้ว่า backend server ใดทำงานหรือไม่
  • ใช้ PM2 Cluster Mode: เพิ่มประสิทธิภาพของ Node.js ด้วย multi-core processing

สรุป

การใช้ Nginx กับ Node.js application นั้นให้ประโยชน์มากมาย ตั้งแต่ SSL termination, load balancing, caching, จนถึง security protection Nginx มีความสามารถสูงในการจัดการ traffic และส่งต่อไปยัง Node.js application ได้อย่างมีประสิทธิภาพ ด้วยการตั้งค่าที่ถูกต้องและการปฏิบัติตามแนวทาง best practices application ของคุณจะสามารถรับจำนวน users มากขึ้น ทำงานได้เร็วขึ้น และปลอดภัยมากขึ้น

แนะนำบริการ DE Cloud VPS

สำหรับการ deploy Node.js Application ร่วมกับ Nginx Reverse Proxy บน production DE Cloud VPS จาก Dot Enterprise เป็นตัวเลือกที่ยอดเยี่ยม ด้วย SSD NVMe Storage, Network 1 Gbps และ Full Root Access ที่ให้คุณติดตั้ง Node.js, PM2 และ Nginx ได้ตามต้องการ พร้อมทีม Support 24/7

สำหรับผู้ที่ไม่ต้องการจัดการเซิร์ฟเวอร์เอง DE Cloud Hosting มี Nginx ติดตั้งและปรับแต่งพร้อมใช้งาน รวมถึง automatic backup, SSL certificate และ control panel ที่ใช้งานง่าย

ดูรายละเอียดเพิ่มเติมได้ที่ de.co.th/cloud-vps และ de.co.th/cloud-hosting หรือติดต่อ DE Support Team ได้ตลอด 24 ชั่วโมง