คำตอบสั้น ๆ: ตั้ง Nginx รับคำขอแทน Backend แล้วเก็บผลลัพธ์ไว้ตอบซ้ำ พร้อมกฎว่าอะไรควรแคชและไม่ควรแคช การล้างแคช และการตอบต่อเมื่อ Backend ล่ม
คู่มือนี้อธิบาย Reverse Proxy ทีละขั้นตอน พร้อมคำสั่งที่ใช้ได้จริงและปัญหาที่พบบ่อย
เมื่อเว็บมีผู้ใช้มากขึ้น คอขวดมักไม่ได้อยู่ที่ Nginx แต่อยู่ที่ Backend ที่ต้องประมวลผลและคิวรีฐานข้อมูลทุกคำขอ ทั้งที่หน้าเว็บจำนวนมากให้ผลเหมือนกันกับทุกคน การให้ Nginx เก็บผลลัพธ์ไว้ตอบซ้ำจึงลดภาระได้มากโดยไม่ต้องแก้โค้ด
คู่มือนี้ตั้ง Nginx เป็น Reverse Proxy หน้า Backend ไม่ว่าจะเป็น PHP-FPM, Node.js หรือ Container แล้วเปิดแคชอย่างปลอดภัย พร้อมกฎที่ต้องระวังเพื่อไม่ให้ผู้ใช้เห็นข้อมูลของคนอื่น
สิ่งที่ต้องเตรียม (Reverse Proxy)
- Nginx บน Linux และสิทธิ์
sudo - Backend ที่ทำงานอยู่แล้ว เช่น แอปที่ฟังพอร์ต 3000 บน localhost
- พื้นที่ดิสก์สำหรับเก็บแคช เริ่มที่ 1-2 GB ก็เพียงพอสำหรับเว็บทั่วไป
ขั้นตอนที่ 1: ตั้ง Reverse Proxy พื้นฐานก่อน
sudo nano /etc/nginx/sites-available/example.com
upstream app_backend {
server 127.0.0.1:3000;
keepalive 32;
}
server {
listen 443 ssl;
http2 on;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
location / {
proxy_pass http://app_backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
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_read_timeout 60s;
}
}
sudo nginx -t && sudo systemctl reload nginx
ส่วน keepalive 32 คู่กับ proxy_set_header Connection "" ทำให้ Nginx ใช้การเชื่อมต่อเดิมซ้ำกับ Backend แทนที่จะเปิดใหม่ทุกคำขอ ซึ่งช่วยลดภาระได้มากในเว็บที่มีทราฟฟิกสูง
ขั้นตอนที่ 2: ประกาศพื้นที่เก็บแคช (Reverse Proxy)
ประกาศในบล็อก http ไม่ใช่ใน server
sudo nano /etc/nginx/nginx.conf
proxy_cache_path /var/cache/nginx/app
levels=1:2
keys_zone=app_cache:20m
max_size=2g
inactive=60m
use_temp_path=off;
sudo mkdir -p /var/cache/nginx/app
sudo chown www-data:www-data /var/cache/nginx/app
ความหมายของค่าแต่ละตัว keys_zone=app_cache:20m คือพื้นที่หน่วยความจำสำหรับเก็บกุญแจ ซึ่ง 20 MB เก็บได้ราวหนึ่งแสนหกหมื่นรายการ max_size=2g คือเพดานพื้นที่บนดิสก์ และ inactive=60m คือรายการที่ไม่ถูกเรียกเลยใน 60 นาทีจะถูกลบทิ้ง
ขั้นตอนที่ 3: เปิดแคชอย่างปลอดภัย (Reverse Proxy)
map $http_cookie $skip_cache {
default 0;
# ผู้ใช้ที่ล็อกอินอยู่ต้องไม่ถูกแคช
~*session_id 1;
~*wordpress_logged_in 1;
~*laravel_session 1;
}
map $request_method $skip_cache_method {
default 1;
GET 0;
HEAD 0;
}
server {
# ... ส่วนเดิม ...
location / {
proxy_pass http://app_backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
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 app_cache;
proxy_cache_key "$scheme$request_method$host$request_uri";
proxy_cache_valid 200 301 302 10m;
proxy_cache_valid 404 1m;
proxy_cache_bypass $skip_cache $skip_cache_method $arg_nocache;
proxy_no_cache $skip_cache $skip_cache_method;
# ใช้แคชเดิมตอบต่อได้หาก Backend ล่มหรือช้า
proxy_cache_use_stale error timeout updating
http_500 http_502 http_503 http_504;
proxy_cache_background_update on;
proxy_cache_lock on;
add_header X-Cache-Status $upstream_cache_status always;
}
}
sudo nginx -t && sudo systemctl reload nginx
ขั้นตอนที่ 4: เข้าใจสามบรรทัดที่สำคัญที่สุด
- proxy_cache_use_stale ทำให้เว็บยังเปิดได้แม้ Backend ล่ม โดยตอบจากแคชเดิมที่หมดอายุแล้ว ผู้ใช้เห็นข้อมูลเก่าเล็กน้อยดีกว่าเห็นหน้า 502
- proxy_cache_lock ป้องกันปัญหาที่เมื่อแคชหมดอายุพร้อมกัน คำขอหลายร้อยรายการวิ่งไป Backend พร้อมกันจนล่ม เมื่อเปิดไว้จะมีคำขอเดียวที่ไปดึงของใหม่ ที่เหลือรอผลนั้น
- X-Cache-Status คือ header ที่ให้คุณตรวจสอบได้ว่าคำขอนั้นถูกตอบจากแคชหรือไม่ ควรมีไว้เสมอตอนตั้งค่า
ขั้นตอนที่ 5: ทดสอบว่าแคชทำงาน
curl -sI https://example.com/ | grep -i x-cache-status
curl -sI https://example.com/ | grep -i x-cache-status
ครั้งแรกควรได้ MISS ครั้งที่สองควรได้ HIT ค่าอื่นที่พบได้คือ BYPASS เมื่อตรงเงื่อนไขข้าม EXPIRED เมื่อหมดอายุและกำลังดึงใหม่ และ STALE เมื่อตอบด้วยของเก่าเพราะ Backend มีปัญหา
ทดสอบว่าผู้ใช้ที่ล็อกอินไม่โดนแคช
curl -sI -H 'Cookie: wordpress_logged_in_abc=1' https://example.com/ | grep -i x-cache-status
ควรได้ BYPASS หากได้ HIT แปลว่ากฎยังไม่ถูกต้องและมีความเสี่ยงที่ผู้ใช้จะเห็นหน้าของคนอื่น ต้องแก้ทันที