การตั้งค่า DISABLE_WP_CRON = true ไม่ได้ เป็นการปิดช่องโหว่ของ wp-cron.php endpoint อย่างสมบูรณ์ แต่มันเพียงแค่หยุดไม่ให้ WordPress ส่ง loopback request ของตัวเองเท่านั้น ไฟล์นี้ยังคงสามารถเข้าถึงได้จากเว็บ ดังนั้นใครก็ตามยังสามารถเรียกใช้งานมันและบังคับให้เกิดกระบวนการ WordPress bootstrap แบบเต็มรูปแบบได้

จุดเข้าถึงที่ซ่อนอยู่นี้สามารถสิ้นเปลือง CPU, หน่วยความจำ และ PHP workers โดยเฉพาะในเว็บไซต์ที่มีผู้ใช้งานหนาแน่น หากคุณคิดว่าคุณได้ปิดประตูนี้ไปแล้ว คุณยังไม่ได้ปิดมันจริงๆ

ทำไมการตั้งค่านี้จึงมักถูกเข้าใจผิด

เมื่อ WordPress รันงานที่ตั้งเวลาไว้ (scheduled task) ขั้นแรกมันจะพยายามส่ง HTTP request อย่างรวดเร็วไปยังไฟล์ wp-cron.php ของตัวเอง การตั้งค่า DISABLE_WP_CRON เป็น true จะบอกให้ตัว core ข้ามการส่ง request ภายในนั้นไป แต่ตัว core ไม่ได้ เปลี่ยนแปลงการตั้งค่าของ web-server และไม่ได้เพิ่มเลเยอร์การยืนยันตัวตน (authentication layer) ใดๆ ให้กับ wp-cron.php สคริปต์นี้ยังคงเป็น URL สาธารณะที่ส่งสถานะ 200 กลับมา แม้ว่ากระบวนการ PHP จะหยุดทำงานก่อนกำหนดก็ตาม

ผู้โจมตีที่ค้นพบ URL นี้สามารถส่ง ping ซ้ำๆ ซึ่งจะทำให้ WordPress ต้องโหลดปลั๊กอิน ธีม และฐานข้อมูลทั้งหมดใหม่ทุกครั้ง ในโฮสต์แบบแชร์ (shared host) หรือเว็บไซต์ที่มี PHP workers จำกัด endpoint เพียงจุดเดียวนี้สามารถกลายเป็นช่องทางในการโจมตีแบบ denial-of-service ได้

สิ่งที่ต้องทำจริงๆ

เพื่อย้ายงานที่ตั้งเวลาไว้ไปยังตัวจัดการตารางเวลาในระดับระบบ (system-level scheduler เช่น cron, systemd-timer และอื่นๆ) และปิดประตูสาธารณะนี้ ให้ทำตาม 5 ขั้นตอนดังนี้

1. ปิดการทำงานของ internal loopback

เพิ่มบรรทัดนี้ลงใน wp-config.php

define( 'DISABLE_WP_CRON', true );

วิธีนี้จะหยุดไม่ให้ WordPress พยายามส่ง HTTP request ของตัวเอง แต่ไม่ได้หยุดการเรียกใช้งานจากภายนอก

2. บล็อก wp-cron.php ที่ระดับ web-server

ตัวอย่างเช่น หากใช้ Nginx ให้ใส่ location block ที่ส่งค่า 403 Forbidden กลับไป:

location = /wp-cron.php {
    return 403;
}

ตอนนี้เซิร์ฟเวอร์จะปฏิเสธคำขอตั้งแต่ก่อนที่ PHP จะเริ่มทำงาน ซึ่งช่วยกำจัดภาระในการทำ bootstrap ออกไปได้

3. เพิ่ม mu-plugin เพื่อเป็นตาข่ายนิรภัย (safety net)

สร้าง must-use plugin (วางไว้ใน wp-content/mu-plugins) ที่ทำหน้าที่บันทึก log ทุกครั้งที่มี request เข้าถึง wp-cron.php และสั่ง exit ด้วยรหัส 403 วิธีนี้จะช่วยดักจับ request ที่อาจหลุดรอดกฎของเซิร์ฟเวอร์มาได้ เช่น ผ่าน hostname อื่น หรือผ่านกฎของ CDN edge

4. ระงับการเรียกใช้งาน async ของ Action Scheduler

ปลั๊กอินหลายตัวใช้ไลบรารี Action Scheduler ซึ่งอาจสร้าง loopback request ของตัวเองขึ้นมา ให้ทำการ hook เข้ากับ action_scheduler_queue_runner และสั่ง return ออกไปทันทีสำหรับ request ที่ไม่ใช่แบบ CLI เพื่อป้องกันไม่ให้ไลบรารีพยายามเปิดช่องทางของตัวเอง

5. ยกเลิกการเชื่อมต่อ queue runner จาก WP-Cron hook

ลบ default Action Scheduler queue runner ที่เชื่อมต่อกับ wp cron hook ออก เพื่อให้แน่ใจว่าคิวจะทำงานก็ต่อเมื่อคุณสั่งรันผ่าน command line โดยตรงเท่านั้น

การรันงานอย่างถูกวิธี

เมื่อปิด endpoint สาธารณะแล้ว ให้ใช้ system scheduler เพื่อเรียกใช้งาน WordPress หนึ่งครั้งต่อนาที (หรือตามความถี่ที่คุณต้องการ) ผ่าน WP-CLI:

wp cron event run --due-now

คำสั่งเดียวนี้จะประมวลผลทั้งเหตุการณ์ cron ของ core และ คิวของ Action Scheduler ในสภาพแวดล้อมที่ควบคุมได้ โดยใช้กระบวนการ PHP เดียวกันกับที่คุณใช้สำหรับงาน CLI อื่นๆ

ประโยชน์ที่คุณสามารถวัดผลได้

  • ความปลอดภัย – ไม่มีผู้ใช้ที่ไม่ผ่านการยืนยันตัวตนสามารถเริ่มงานเบื้องหลังที่หนักหน่วงได้
  • ประสิทธิภาพ – PHP workers จะว่างพร้อมสำหรับผู้เข้าชมเว็บไซต์จริงๆ และปัญหาหน่วยความจำพุ่งสูง (memory spikes) จากการเรียก cron ที่ผิดพลาดจะหมดไป
  • ความน่าเชื่อถือ – การตั้งเวลาจะกลายเป็นแบบเชิงรุก (proactive) คุณจะรู้แน่ชัดว่างานจะรันเมื่อไหร่ เพราะมันถูกขับเคลื่อนโดย cron ของระบบ ไม่ใช่จากการโหลดหน้าเว็บของผู้เข้าชม

สิ่งที่ต้องเฝ้าระวังหลังการเปลี่ยนแปลง

อย่าพึ่งพา HTTP status code ของ wp-cron.php เพียงอย่างเดียว เพราะมันอาจยังส่งค่า 200 กลับมาได้แม้จะถูกบล็อกโดย PHP แล้วก็ตาม แต่ให้ทำดังนี้แทน:

  • เฝ้าติดตามขนาดของคิวใน Action Scheduler หากคิวใหญ่ขึ้นเรื่อยๆ มักเป็นสัญญาณว่ามีการรันงานพลาดไป
  • ตรวจสอบ server logs เพื่อหา entry “403” ที่มาจาก location block ของ wp-cron.php
  • สังเกตจำนวน process ของ PHP-FPM หรือ FastCGI ในช่วงที่มีทราฟฟิกสูงสุด เพื่อยืนยันว่าภาระงาน (load) ลดลงจริง

ข้อควรระวัง

ผู้ดูแลระบบบางคนยังคงตั้งค่า DISABLE_WP_CRON เป็น false เพราะพวกเขาพึ่งพาการที่เว็บไซต์ที่มีทราฟฟิกต่ำจะช่วยกระตุ้น cron ให้ทำงานโดยธรรมชาติ วิธีการข้างต้นจะตัดการพึ่งพาในส่วนนั้นออกไปทั้งหมด แต่ก็เป็นการเพิ่มขั้นตอนการดูแลรักษา: คุณต้องมั่นใจว่า system scheduler ทำงานได้อย่างน่าเชื่อถือ หาก cron daemon ของเซิร์ฟเวอร์ล้มเหลว งานที่ตั้งเวลาไว้ก็จะหยุดทำงาน ดังนั้นควรใช้การตั้งค่านี้ควบคู่ไปกับการเฝ้าติดตามบริการ system cron เพื่อหลีกเลี่ยงปัญหานี้

สรุป: การตั้งค่า DISABLE_WP_CRON เป็น true เพียงแค่หยุดไม่ให้ WordPress เรียกใช้งานตัวเองเท่านั้น แต่มันไม่ได้ปิด endpoint สาธารณะของ wp-cron.php ดังนั้นควรบล็อกไฟล์นี้ที่ระดับ web-server, เพิ่ม mu-plugin เป็นแผนสำรอง และส่งงานที่ตั้งเวลาไว้ทั้งหมดผ่าน system scheduler โดยใช้ WP-CLI การทำเช่นนี้จะช่วยรักษาความปลอดภัยให้กับเว็บไซต์, ลดภาระงาน PHP ที่ไม่จำเป็น และช่วยให้คุณควบคุมเวลาการทำงานของงานเบื้องหลังได้อย่างแม่นยำ