Skip to content
← All Skills
🌐

Nginx

InfrastructureOfficial Site ›

Web server & reverse proxy — load balancing, SSL termination, static file serving สำหรับ production

What I Can Do

  • ตั้งค่า Nginx เป็น reverse proxy สำหรับ backend services
  • Configure SSL/TLS termination ด้วย Let's Encrypt
  • ออกแบบ load balancing สำหรับ multiple upstream servers
  • Serve static files และ SPA (Single Page Application)
  • Optimize performance ด้วย caching, compression, connection tuning

Commands I Use Daily

bash
# ตรวจ config syntax ก่อน reload
nginx -t

# reload config โดยไม่ drop connections
nginx -s reload

# start / stop / restart
sudo systemctl start nginx
sudo systemctl stop nginx
sudo systemctl restart nginx

# ดู error log แบบ follow
tail -f /var/log/nginx/error.log

# ดู access log
tail -f /var/log/nginx/access.log

# ดู nginx version และ modules
nginx -V

Reverse Proxy

ใช้ Nginx รับ request จาก client แล้ว forward ไป backend service — client ไม่เห็น backend ตรงๆ, Nginx จัดการ SSL, caching, load balancing ให้

nginx
server {
    listen 80;
    server_name api.example.com;

    location / {
        proxy_pass http://localhost:8080;
        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_set_header สำคัญ — ถ้าไม่ set backend จะเห็น IP ของ Nginx แทน client จริง

Server Blocks (Virtual Hosts)

หนึ่ง Nginx serve ได้หลาย domains — แยก config เป็น server block ต่อ domain:

  • /etc/nginx/sites-available/ — เก็บ config ทั้งหมด
  • /etc/nginx/sites-enabled/ — symlink เฉพาะที่ต้องการ enable
  • server_name match domain — Nginx เลือก server block ที่ตรงกับ request

Location Blocks

กำหนด behavior ตาม URL path — match จาก specific ไป general:

  • location = /health — exact match
  • location ^~ /api/ — prefix match (หยุดหา regex)
  • location ~ \.php$ — regex match (case-sensitive)
  • location ~* \.(jpg|png)$ — regex match (case-insensitive)
  • location / — default prefix match

Load Balancing

กระจาย traffic ไปหลาย backend servers:

nginx
upstream backend {
    least_conn;
    server 10.0.0.1:8080;
    server 10.0.0.2:8080;
    server 10.0.0.3:8080 backup;
}

server {
    listen 80;
    location / {
        proxy_pass http://backend;
    }
}
  • round-robin — default, กระจายเท่ากัน
  • least_conn — ส่งไป server ที่มี connections น้อยสุด
  • ip_hash — ส่ง IP เดิมไป server เดิม (sticky sessions)
  • backup — ใช้เมื่อ servers อื่นล่มหมด

SSL/TLS Termination

Nginx จัดการ SSL แทน backend — backend รับแค่ HTTP:

nginx
server {
    listen 443 ssl http2;
    server_name example.com;

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

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

    location / {
        proxy_pass http://localhost:8080;
    }
}

server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;
}

ใช้ Certbot (Let's Encrypt) สำหรับ free SSL certificates — auto renew ด้วย cron

Static File Serving

Serve static files ตรงจาก Nginx — เร็วกว่าผ่าน application server:

nginx
server {
    listen 80;
    root /var/www/html;
    index index.html;

    location / {
        try_files $uri $uri/ /index.html;
    }

    location ~* \.(js|css|png|jpg|svg|woff2)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
}

try_files $uri $uri/ /index.html — สำคัญสำหรับ SPA (React, Vue) ที่ใช้ client-side routing

Gzip Compression

ลด response size 60-80% สำหรับ text-based content:

nginx
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml image/svg+xml;

Caching

Proxy cache — cache response จาก upstream ลด load:

nginx
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=api_cache:10m max_size=1g inactive=60m;

location /api/ {
    proxy_cache api_cache;
    proxy_cache_valid 200 10m;
    proxy_cache_valid 404 1m;
    proxy_pass http://backend;
    add_header X-Cache-Status $upstream_cache_status;
}

X-Cache-Status header บอก client ว่า response มาจาก cache (HIT) หรือ upstream (MISS)

Rate Limiting

จำกัด requests ป้องกัน abuse:

nginx
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;

location /api/ {
    limit_req zone=api burst=20 nodelay;
    proxy_pass http://backend;
}
  • rate=10r/s — 10 requests per second per IP
  • burst=20 — อนุญาต burst สูงสุด 20 requests
  • nodelay — process burst ทันทีไม่ queue

WebSocket Proxy

Nginx proxy WebSocket connections ต้อง upgrade header:

nginx
location /ws {
    proxy_pass http://localhost:8080;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_read_timeout 86400s;
}

proxy_read_timeout ต้องตั้งสูง — ไม่งั้น Nginx จะ timeout idle WebSocket connections

Security Headers

เพิ่ม security headers ที่ Nginx layer:

nginx
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header Content-Security-Policy "default-src 'self'" always;

Access Control

จำกัด access ตาม IP หรือ basic auth:

nginx
location /admin {
    allow 10.0.0.0/8;
    deny all;
    proxy_pass http://backend;
}

location /internal {
    auth_basic "Restricted";
    auth_basic_user_file /etc/nginx/.htpasswd;
    proxy_pass http://backend;
}

Nginx with Docker

nginx
# Dockerfile
FROM nginx:alpine
COPY nginx.conf /etc/nginx/nginx.conf
COPY dist/ /usr/share/nginx/html/
EXPOSE 80

ใช้ nginx:alpine image — เล็ก (~40MB), มี Nginx พร้อมใช้

Performance Tuning

  • worker_processes auto; — ใช้ CPU cores ทั้งหมด
  • worker_connections 1024; — connections per worker
  • keepalive_timeout 65; — reuse connections
  • sendfile on; — efficient file transfer
  • tcp_nopush on; — ส่ง headers + body รวมกัน
  • tcp_nodelay on; — ลด latency สำหรับ small packets

ปัญหาที่เจอบ่อย & วิธีแก้

502 Bad Gateway — upstream ไม่ตอบ

Nginx return 502 ทุก request

สาเหตุ: backend service ล่ม, proxy_pass URL ผิด, backend listen คนละ port, Docker network ไม่ตรงกัน

วิธีแก้:

  • ตรวจ backend service ว่า running: curl http://localhost:8080/health จาก server ตรงๆ
  • ดู error log: tail -f /var/log/nginx/error.log — จะบอกว่า connect ไป upstream ไหนแล้ว fail
  • ตรวจ port ให้ตรง: backend listen port เดียวกับที่ proxy_pass ชี้ไป
  • Docker: ตรวจว่า Nginx กับ backend อยู่ network เดียวกัน

413 Request Entity Too Large — upload ไม่ได้

Upload file แล้ว Nginx reject ด้วย 413

สาเหตุ: Nginx มี default client_max_body_size = 1MB — file ที่ใหญ่กว่าจะถูก reject ก่อนถึง backend

วิธีแก้:

  • เพิ่ม client_max_body_size 50m; ใน server หรือ location block
  • ตั้งให้เหมาะกับ use case: 10m สำหรับ images, 100m สำหรับ video uploads
  • ตั้งที่ http block ถ้าต้องการ apply ทั้ง server

SPA routing ไม่ทำงาน — refresh แล้ว 404

เข้า /dashboard ตรงๆ หรือ refresh หน้าได้ 404

สาเหตุ: Nginx หา file /dashboard ไม่เจอเพราะ SPA มีแค่ index.html — ไม่มี try_files fallback

วิธีแก้:

  • ใส่ try_files $uri $uri/ /index.html; ใน location block
  • Nginx จะ try หา file ก่อน ถ้าไม่เจอก็ serve index.html ให้ client-side router จัดการ

SSL certificate ไม่ renew — site ล่ม

Certificate หมดอายุแล้ว browser block access

สาเหตุ: Certbot auto-renew ไม่ทำงาน, cron job ไม่ได้ตั้ง, renew สำเร็จแต่ไม่ได้ reload Nginx

วิธีแก้:

  • ตรวจ cron: sudo crontab -l ดูว่ามี certbot renew หรือไม่
  • ตั้ง cron ให้ renew + reload: 0 0 * * * certbot renew --quiet && nginx -s reload
  • ทดสอบ renew: sudo certbot renew --dry-run
  • ตรวจ certificate expiry: echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates

WebSocket ตัดทุก 60 วินาที

WebSocket connection ถูก close หลัง idle ไม่กี่นาที

สาเหตุ: proxy_read_timeout default = 60 วินาที — ถ้าไม่มี data ส่งภายใน 60 วินาที Nginx จะ close connection

วิธีแก้:

  • เพิ่ม proxy_read_timeout 86400s; (24 ชั่วโมง) สำหรับ WebSocket location
  • ตรวจว่า set proxy_http_version 1.1; และ Upgrade/Connection headers ครบ
  • implement ping/pong ที่ application level เป็น keep-alive เพิ่มเติม

Config syntax ถูกแต่ไม่ทำงานตามที่คิด

nginx -t ผ่านแต่ behavior ไม่ตรง

สาเหตุ: location block priority ไม่ตรงที่คิด (exact > prefix > regex > default), ลืม reload หลังแก้ config, มี config ซ้ำใน sites-enabled หลายไฟล์ conflict กัน

วิธีแก้:

  • ใช้ nginx -T (ตัวใหญ่) ดู full merged config ที่ Nginx เห็นจริง
  • ทดสอบด้วย curl -i ดู response headers ว่ามาจาก location ไหน
  • ตรวจ sites-enabled ว่ามี server block ซ้ำกันไหม — ls -la /etc/nginx/sites-enabled/
  • อย่าลืม nginx -s reload หลังแก้ config ทุกครั้ง

Related Skills

  • Docker — containerize Nginx + backend services
  • Kong Gateway — API gateway ที่ทำงานร่วมกับ Nginx
  • Linux — server ที่ run Nginx
  • AWS — deploy Nginx บน EC2
  • Gin — Go backend หลัง Nginx reverse proxy