คำตอบสั้น ๆ: แก้ 502 Bad Gateway บน Nginx ทีละจุด อ่าน error.log ให้รู้ว่า PHP-FPM หยุด สิทธิ์ socket ผิด pm.max_children ไม่พอ บัฟเฟอร์เล็กเกิน หรือแอป Node.js ล่ม
คู่มือนี้อธิบาย 502 Bad Gateway ทีละขั้นตอน พร้อมคำสั่งที่ใช้ได้จริงและปัญหาที่พบบ่อย
502 Bad Gateway คือรหัสที่ Nginx ส่งกลับเมื่อตัว Nginx เองทำงานปกติ แต่ติดต่อโปรแกรมด้านหลัง (upstream) ไม่สำเร็จหรือได้คำตอบที่ใช้ไม่ได้ upstream ที่พบบ่อยคือ PHP-FPM สำหรับเว็บ PHP อย่าง WordPress และ Laravel หรือแอป Node.js ที่รันอยู่หลัง reverse proxy ดังนั้นหน้า 502 Bad Gateway จึงไม่ได้บอกว่า Nginx เสีย แต่บอกว่าทางเชื่อมระหว่าง Nginx กับแอปขาดที่จุดใดจุดหนึ่ง
คู่มือนี้เหมาะกับผู้ดูแลเว็บเซิร์ฟเวอร์ Linux ที่ใช้ Nginx คู่กับ PHP-FPM หรือ Node.js เมื่อทำตามครบ คุณจะอ่านข้อความใน error.log แล้วรู้ทันทีว่า 502 Bad Gateway มาจากบริการหยุด สิทธิ์ของ socket จำนวน worker ไม่พอ บัฟเฟอร์เล็กเกิน หรือ SELinux และแก้ได้ตรงจุดโดยไม่ต้องเดาเปลี่ยนค่าทีละอย่าง
สิ่งที่ต้องเตรียม
- สิทธิ์ root หรือ sudo บนเซิร์ฟเวอร์ที่รัน Nginx
- URL ที่เกิด 502 Bad Gateway และเวลาที่เกิด ถ้าเกิดเป็นช่วง ๆ ให้จดช่วงเวลาไว้ เพราะต้องใช้เทียบกับล็อก
- รู้ว่าแต่ละเว็บใช้ upstream แบบใด ดูได้จากไฟล์ server block ใน
/etc/nginx/sites-enabled/(Ubuntu, Debian) หรือ/etc/nginx/conf.d/(AlmaLinux, Rocky Linux) ถ้ายังไม่คุ้นกับโครงสร้างนี้ ดู คู่มือตั้งค่า Nginx Server Block - เวอร์ชัน PHP ที่ใช้ เช่น 8.3 ตัวอย่างในคู่มือนี้ใช้ชื่อบริการ
php8.3-fpmตามแบบ Ubuntu บน AlmaLinux ชื่อบริการคือphp-fpm
ขั้นตอนที่ 1: ยืนยันว่า 502 Bad Gateway มาจาก Nginx บนเครื่องนี้
ถ้าเว็บอยู่หลัง Cloudflare หรือ Load Balancer หน้า 502 Bad Gateway อาจมาจากชั้นนั้น ไม่ใช่จากเซิร์ฟเวอร์ของคุณ หน้าของ Cloudflare จะมีชื่อ Cloudflare และ Ray ID กำกับ ให้ทดสอบตรงที่เครื่องโดยข้าม DNS
curl -sI -H "Host: example.com" http://127.0.0.1/ | head -5
curl -sI --resolve example.com:443:127.0.0.1 https://example.com/ | head -5
ถ้าบรรทัดแรกเป็น HTTP/1.1 502 Bad Gateway และมี Server: nginx แปลว่าปัญหาอยู่ที่เครื่องนี้ ไปต่อขั้นตอนที่ 2 ถ้าได้ 200 แต่ผู้ใช้ภายนอกยังเห็น 502 ให้ตรวจชั้นที่อยู่หน้าเครื่องแทน ตรวจด้วยว่าคอนฟิก Nginx ไม่มีข้อผิดพลาด
sudo nginx -t
ขั้นตอนที่ 2: อ่าน error.log ของ Nginx
ทุกครั้งที่เกิด 502 Bad Gateway Nginx จะเขียนสาเหตุไว้ใน error log เสมอ ไฟล์รวมอยู่ที่ /var/log/nginx/error.log แต่ถ้า server block กำหนด error_log แยก ให้เปิดไฟล์ของเว็บนั้น
sudo tail -n 30 /var/log/nginx/error.log
sudo grep -E "upstream|connect\(\)" /var/log/nginx/error.log | tail -n 20
อ่านข้อความในวงเล็บหลัง failed ซึ่งเป็นรหัสข้อผิดพลาดของระบบ แล้วจับคู่กับสาเหตุของ 502 Bad Gateway ตามตารางในภาพด้านล่าง ข้อความที่ลงท้ายด้วย (110: Connection timed out) หรือ upstream timed out ไม่ใช่ 502 แต่เป็น 504 Gateway Timeout ซึ่งแก้คนละทาง ความหมายของรหัส HTTP อื่น ๆ ดูได้ที่ คู่มือ HTTP Error 403, 404, 500, 502, 503 และ 504
ขั้นตอนที่ 3: ตรวจว่า PHP-FPM หรือแอปยังทำงานอยู่
ข้อความ (2: No such file or directory) กับ socket หรือ (111: Connection refused) แปลว่าไม่มีใครรออยู่ปลายทาง สาเหตุที่พบบ่อยที่สุดของ 502 Bad Gateway คือบริการด้านหลังหยุด
systemctl status php8.3-fpm --no-pager
ls -l /run/php/
sudo ss -xlp | grep php
sudo journalctl -u php8.3-fpm --since "1 hour ago" --no-pager | tail -20
ถ้าบริการหยุด ให้ทดสอบคอนฟิกด้วย sudo php-fpm8.3 -t แก้ตามบรรทัดที่ฟ้อง แล้วเริ่มใหม่ด้วย sudo systemctl restart php8.3-fpm ถ้าบริการหยุดเองซ้ำ ๆ ให้ค้น kernel log ว่าถูกปิดเพราะ RAM หมดหรือไม่ ตาม คู่มือ OOM Killer
อีกกรณีที่ทำให้เกิด 502 Bad Gateway บ่อยหลังอัปเกรด PHP คือ Nginx ยังชี้ไป socket ของเวอร์ชันเก่า เช่น fastcgi_pass unix:/run/php/php8.1-fpm.sock; ขณะที่เครื่องเหลือแต่ php8.3-fpm.sock ให้แก้พาธใน server block ให้ตรงกับไฟล์ที่ ls -l /run/php/ แสดง แล้วรัน sudo nginx -t && sudo systemctl reload nginx
สำหรับแอป Node.js ให้ตรวจว่าพอร์ตที่ proxy_pass ชี้ไปมีโปรเซสรับอยู่จริง และเรียกแอปตรงโดยไม่ผ่าน Nginx
sudo ss -ltnp | grep ':3000'
curl -sI http://127.0.0.1:3000/ | head -3
pm2 status
pm2 logs app --lines 50
ถ้า curl ตรงเข้าแอปก็ไม่ได้ ต้นเหตุของ 502 Bad Gateway อยู่ที่แอป ไม่ใช่ Nginx ดูล็อกของแอปว่าหยุดเพราะ error อะไร การตั้ง PM2 ให้เปิดแอปกลับเองหลังรีบูตดูได้ใน คู่มือ Node.js กับ PM2 และ Nginx
ขั้นตอนที่ 4: แก้สิทธิ์ของ socket
ข้อความ connect() to unix:/run/php/php8.3-fpm.sock failed (13: Permission denied) แปลว่า socket มีอยู่ แต่ผู้ใช้ที่รัน Nginx เปิดไม่ได้ จึงตอบ 502 Bad Gateway ทุกคำขอ เกิดบ่อยเมื่อสร้าง pool ใหม่โดยคัดลอกไฟล์มาแล้วเปลี่ยนเจ้าของ ตรวจผู้ใช้ของ Nginx ด้วย ps -o user= -C nginx | sort -u แล้วแก้ไฟล์ pool เช่น /etc/php/8.3/fpm/pool.d/www.conf
listen = /run/php/php8.3-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
บน AlmaLinux ผู้ใช้ของ Nginx คือ nginx และไฟล์ /etc/php-fpm.d/www.conf ใช้บรรทัด listen.acl_users = apache,nginx ควบคุมสิทธิ์ ให้แน่ใจว่ามีชื่อ nginx อยู่ในรายการ จากนั้นรีสตาร์ต PHP-FPM แล้วตรวจสิทธิ์ของไฟล์ socket ด้วย ls -l อีกครั้ง
ถ้าเป็นการ proxy ไปพอร์ต TCP บน AlmaLinux หรือ Rocky Linux แล้วได้ 502 Bad Gateway พร้อมข้อความ (13: Permission denied) ทั้งที่แอปทำงานอยู่ ต้นเหตุมักเป็น SELinux ที่ไม่อนุญาตให้ Nginx เชื่อมต่อออกไปพอร์ตอื่น แก้ด้วย sudo setsebool -P httpd_can_network_connect 1 แทนการปิด SELinux ทั้งระบบ รายละเอียดอยู่ใน คู่มือ SELinux บน AlmaLinux และ Rocky Linux