502 Bad Gateway: ไล่หาสาเหตุบน Nginx และ PHP-FPM ทีละจุด

แก้ 502 Bad Gateway บน Nginx ทีละจุด อ่าน error.log ให้รู้ว่า PHP-FPM หยุด สิทธิ์ socket ผิด pm.max_children ไม่พอ บัฟเฟอร์เล็กเกิน หรือแอป Node.js ล่ม

โดย ทีมงาน THAI DATA CLOUD อ่าน 14 นาที

แชร์FacebookXLINELinkedIn
502 Bad Gateway — 502 Bad Gateway: ไล่หาสาเหตุบน Nginx และ PHP-FPM ทีละจุด
502 Bad Gateway: ไล่หาสาเหตุบน Nginx และ PHP-FPM ทีละจุด

คำตอบสั้น ๆ: แก้ 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
แผนภาพเส้นทางคำขอจากเบราว์เซอร์ผ่าน Cloudflare หรือ Load Balancer ไปยัง Nginx แล้วต่อไป PHP-FPM ผ่าน socket หรือแอป Node.js ผ่านพอร์ต
Nginx แสดง 502 Bad Gateway เมื่อช่วงต่อจาก Nginx ไปแอปด้านหลังล้มเหลว ไม่ใช่เมื่อ Nginx เองเสีย

ขั้นตอนที่ 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

ตารางจับคู่ข้อความใน error.log ของ Nginx กับสาเหตุของ 502 Bad Gateway และจุดที่ต้องแก้ เช่น socket ไม่มี สิทธิ์ผิด หรือ worker เต็ม
รหัสในวงเล็บหลังคำว่า failed บอกเกือบทุกอย่าง ถ้าเห็น timed out คือ 504 ไม่ใช่ 502

ขั้นตอนที่ 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

ขั้นตอนที่ 5: เพิ่ม pm.max_children เมื่อ worker ไม่พอ

ถ้า 502 Bad Gateway เกิดเฉพาะช่วงคนเข้าเยอะแล้วหายเอง ให้ดูล็อกของ PHP-FPM ที่ /var/log/php8.3-fpm.log หากพบข้อความ server reached pm.max_children setting แปลว่า worker ทุกตัวไม่ว่าง คำขอใหม่ต้องรอคิว และเมื่อคิวของ socket เต็ม Nginx จะได้ (11: Resource temporarily unavailable) และส่ง 502 กลับ ค่าเริ่มต้นของ pool www บน Ubuntu และ Debian คือ pm.max_children = 5 ซึ่งน้อยมากสำหรับเว็บจริง

คำนวณจาก RAM แทนการใส่ตัวเลขสูง ๆ ดูขนาดเฉลี่ยของ worker ก่อน

ps --no-headers -o rss -C php-fpm8.3 | awk '{s+=$1; n++} END {printf "%.0f MB avg\n", s/n/1024}'

ถ้าเครื่องมี RAM 4 GB เหลือให้ PHP ประมาณ 2.5 GB หลังหักฐานข้อมูลและระบบ และ worker เฉลี่ย 60 MB ค่าที่รับได้คือประมาณ 40 แก้ไฟล์ pool ดังนี้

pm = dynamic
pm.max_children = 40
pm.start_servers = 8
pm.min_spare_servers = 4
pm.max_spare_servers = 12
pm.max_requests = 500

ค่า pm.start_servers ต้องอยู่ระหว่าง min และ max spare servers ไม่เช่นนั้น PHP-FPM จะไม่ยอมเริ่ม ทดสอบด้วย sudo php-fpm8.3 -t ก่อนรีสตาร์ต ถ้าตั้งสูงเกิน RAM ปัญหาจะเปลี่ยนจาก 502 Bad Gateway เป็นเครื่องหน่วยความจำหมดแทน ความหมายของแต่ละค่าดูได้ใน เอกสาร FPM Configuration ของ PHP

ผลคำสั่งอ่าน error.log ของ Nginx ที่ฟ้อง Resource temporarily unavailable และล็อก PHP-FPM ที่เตือน server reached pm.max_children
ค่าเริ่มต้น pm.max_children = 5 ไม่พอสำหรับเว็บจริง ให้คำนวณจาก RAM และขนาดเฉลี่ยของ worker

ขั้นตอนที่ 6: แก้บัฟเฟอร์และ worker ที่ถูกตัดกลางทาง

ข้อความกลุ่มสุดท้ายแปลว่าเชื่อมต่อได้แล้ว แต่คำตอบมาไม่ครบหรืออ่านไม่ได้ Nginx จึงตอบ 502 Bad Gateway แทน

  • upstream sent too big header while reading response header from upstream: แอปส่ง header ใหญ่กว่าบัฟเฟอร์ มักมาจากคุกกี้จำนวนมากหรือ header ที่ปลั๊กอินเพิ่ม เพิ่มใน location ของ PHP ว่า fastcgi_buffer_size 32k; และ fastcgi_buffers 16 16k; ถ้าเป็น proxy ไป Node.js ใช้ proxy_buffer_size และ proxy_buffers แทน
  • recv() failed (104: Connection reset by peer) หรือ upstream prematurely closed connection: worker ถูกปิดระหว่างทำงาน ดูล็อกของ PHP-FPM ว่ามี execution timed out ซึ่งมาจาก request_terminate_timeout หรือ exited on signal 11 (SIGSEGV) ซึ่งมักมาจาก extension ที่มีปัญหา ฝั่ง Node.js ให้ดูว่าแอป crash แล้วเริ่มใหม่ตรงเวลาที่เกิดหรือไม่

ตั้งเวลาทั้งสามชั้นให้สอดคล้องกัน คือ max_execution_time ของ PHP น้อยกว่า request_terminate_timeout ของ pool และน้อยกว่า fastcgi_read_timeout ของ Nginx (ค่าเริ่มต้น 60 วินาที) งานที่ต้องใช้เวลานานกว่าหนึ่งนาที เช่น ออกรายงานหรือนำเข้าไฟล์ ควรย้ายไปทำในคิวเบื้องหลังแทนการยืดเวลา เพื่อไม่ให้ worker ถูกตัดกลางทางจนเกิด 502 Bad Gateway หลังแก้ทุกครั้งให้รัน sudo nginx -t && sudo systemctl reload nginx

ตรวจว่าแก้ 502 Bad Gateway สำเร็จ

เปิด error.log ค้างไว้ด้วย sudo tail -f /var/log/nginx/error.log แล้วเรียกหน้าที่เคยเสียซ้ำหลายครั้ง ถ้าปัญหาเกิดเฉพาะช่วงโหลดสูง ให้ทดสอบด้วยเครื่องมือยิงโหลดเบา ๆ ในช่วงที่ผู้ใช้น้อย และตั้งระบบเฝ้าดูเว็บให้แจ้งเตือนทันทีที่ 502 Bad Gateway กลับมา

เช็กลิสต์ยืนยันว่าแก้ 502 Bad Gateway สำเร็จ ทั้งผล curl ล็อก Nginx ล็อก PHP-FPM ค่า pm.max_children และ timeout
ทดสอบซ้ำในช่วงโหลดสูง ไม่ใช่แค่เปิดหน้าเว็บได้ครั้งเดียว

ปัญหาที่พบบ่อยเกี่ยวกับ 502 Bad Gateway

รีสตาร์ต PHP-FPM แล้วหาย แต่กลับมาอีกในไม่กี่ชั่วโมง ควรทำอย่างไร?

การรีสตาร์ตแค่ล้างอาการ ให้ดูล็อกของ PHP-FPM ช่วงก่อนเกิด ถ้ามี pm.max_children ให้คำนวณใหม่ตามขั้นตอนที่ 5 ถ้า RAM ค่อย ๆ เพิ่มจน worker ถูกปิด ให้ตั้ง pm.max_requests และหาโค้ดที่รั่ว และถ้ามี slow request ให้เปิด slowlog ของ pool เพื่อดูว่าสคริปต์ใดค้าง

502 Bad Gateway กับ 504 Gateway Timeout ต่างกันอย่างไร?

502 คือ Nginx ได้คำตอบที่ใช้ไม่ได้หรือเชื่อมต่อไม่ได้เลย ส่วน 504 คือเชื่อมต่อได้แต่รอคำตอบนานเกิน fastcgi_read_timeout หรือ proxy_read_timeout ดูง่าย ๆ จาก error.log ถ้ามีคำว่า timed out คือ 504

เว็บ WordPress ขึ้น 502 Bad Gateway เฉพาะหน้า wp-admin เกิดจากอะไร?

มักเป็น header ใหญ่เกินจากคุกกี้ของผู้ดูแลที่ล็อกอิน หรือหน้าแอดมินที่ใช้หน่วยความจำมากจน worker ถูกปิด ตรวจ error.log ว่าเป็น too big header หรือ Connection reset แล้วแก้ตามขั้นตอนที่ 6

ใช้ Docker แล้วได้ 502 Bad Gateway ตลอด ตรวจอะไรก่อน?

ตรวจว่า proxy_pass ชี้ไปที่ชื่อบริการหรือพอร์ตที่ถูกต้อง ถ้า Nginx อยู่ใน container เดียวกับเครือข่ายของแอป ให้ใช้ชื่อบริการใน Compose แทน 127.0.0.1 เพราะ 127.0.0.1 ใน container หมายถึงตัว container เอง และแอปต้องฟังที่ 0.0.0.0 ไม่ใช่ localhost ภายใน container

ถ้าต้องการให้ทีมงานช่วยอ่านล็อก ปรับ Nginx และ PHP-FPM ให้รับโหลดจริงของเว็บ ติดต่อทีมงาน THAI DATA CLOUD ได้ และสำหรับเว็บที่ต้องการเครื่องแรงขึ้นหรือมีทีมดูแลให้ ดูรายละเอียดได้ที่หน้า Cloud Linux Server

อ่านเพิ่มเติมเกี่ยวกับ 502 Bad Gateway

แชร์FacebookXLINELinkedIn

มีคำถามเรื่องบริการ? ติดต่อทีมงาน