ถ้าคุณดูแลเว็บให้คนอื่น คุณน่าจะคุ้นกับข้อความทำนองนี้ — “พี่ครับ ฟอร์มติดต่อในเว็บส่งไม่ได้” จบ. ไม่มีรายละเอียด ไม่มี error ไม่มีภาพ. พอผมเปิดเข้าไปดู ก็เจอปุ่มส่งขึ้นข้อความแดงๆ ว่า captcha ยืนยันไม่ผ่าน ทั้งที่ผมไล่เช็คการตั้งค่าแล้ว มันถูกหมดทุกอย่าง
เคสนี้กินเวลาผมไปหลายชั่วโมง เพราะปัญหามันไม่ได้มีชั้นเดียว — มันซ้อนกันอยู่ 4 ชั้น และตัวที่เป็นต้นเหตุจริงๆ ดันเป็นสิ่งที่ผมแทบไม่ได้เหลียวมองตั้งแต่แรก บทความนี้ผมเลยอยากเล่าให้ฟังว่ามันหลอกผมยังไง และเบาะแสเล็กๆ อะไรที่พาผมไปเจอตัวจริง เผื่อวันหนึ่งคุณเจอ captcha ไม่ขึ้น บนเว็บแล้วงงว่ามันหายไปไหน จะได้ไม่ต้องเสียเวลาเท่าผม
อาการ: ทุกอย่างดูถูก แต่กล่อง captcha ไม่ยอมโผล่
เว็บนี้ใช้ปลั๊กอินฟอร์มยอดนิยมตัวหนึ่ง คู่กับระบบ captcha ของ Cloudflare ที่ชื่อ Turnstile (ตัวที่ไม่ต้องให้คนกดเลือกรูปสะพาน-รถเมล์ให้เมื่อยมือ) ลูกค้าตั้งค่าเองมาก่อน แล้วมันใช้ไม่ได้
สิ่งแรกที่ผมทำคือไล่เช็คของพื้นฐานทีละข้อ — key ถูกไหม ประเภท captcha ตั้งตรงไหม ฟอร์มเรียกใช้จริงหรือเปล่า ทุกอย่างผ่านหมด แต่พอเปิดหน้าจริง กล่อง captcha มันไม่โผล่ขึ้นมาเลย ฟอร์มข้ามจากปุ่มอัปโหลดรูปไปปุ่มส่งเลย เหมือนไม่มีตัวตน
ชั้นที่ 1: ค่าเก่าที่ค้างอยู่ในฐานข้อมูล
พอขุดเข้าไปดูค่าในฐานข้อมูล ผมก็เจอชั้นแรก — มีค่า global ตัวหนึ่งที่ตั้ง “บังคับใช้ captcha” ไว้ แต่ดันชี้ไปที่ captcha คนละเจ้า (ตัวเก่าที่เคยใช้) ผลคือทุกครั้งที่ฟอร์มถูกสร้างขึ้นมา มันจะไปดึง captcha ตัวเก่ามาแทน แล้วลบตัวใหม่ทิ้ง
ผมแก้ค่าตรงนั้นให้ชี้มาที่ Turnstile ให้ถูก คิดในใจว่า “เออ น่าจะจบแล้วล่ะ” — แต่เปล่าเลย หน้าเว็บยังเหมือนเดิมเป๊ะ
ชั้นที่ 2: แคชที่ผมลบไม่ได้ (เพราะผมไม่ใช่เจ้าของไฟล์)
ที่หน้าเว็บไม่เปลี่ยน เพราะมันโดนปลั๊กอินแคชเก็บ HTML หน้าเก่าไว้ ผมเลยสั่งล้างแคช — แต่ดันเจอด่านแปลกๆ คือ ลบไฟล์แคชไม่ได้ ขึ้น permission denied
สาเหตุคือไฟล์แคชพวกนั้นมีเจ้าของเป็น user ของตัวเว็บ (ที่ php สร้างไฟล์ขึ้นมา) แต่ตอนผม SSH เข้าไปสั่งลบ ผมล็อกอินด้วยอีก user หนึ่งที่ไม่มีสิทธิ์แตะไฟล์ของเขา — สั่ง rm ก็ไม่ได้ สั่งผ่านคำสั่งของปลั๊กอินก็ลบไม่ขาด
ทางออกที่ผมใช้คือยืมมือ php เอง — เขียนสคริปต์เล็กๆ ฝากไว้ให้เว็บรัน พอมันรันในฐานะ user ตัวเดียวกับที่สร้างไฟล์ ก็ลบแคชหน้านั้นได้สบาย เสร็จแล้วเอาสคริปต์ออก ตรงนี้ผมเสียเวลาไปพอสมควรกว่าจะนึกออกว่าปัญหาคือเรื่องสิทธิ์ ไม่ใช่เรื่องคำสั่งผิด
ชั้นที่ 3: ตัวเร่งความเร็วเว็บที่เร่งจนพัง
พอแคชหายจริง หน้าเว็บส่ง HTML ที่ถูกต้องออกมาแล้ว — โค้ดของ captcha มาครบ สคริปต์โหลดครบ แต่กล่องก็ยัง… ไม่โผล่อยู่ดี
ตัวการชั้นนี้คือปลั๊กอินเร่งความเร็ว (cache + optimize) ที่เปิดฟังก์ชัน “เลื่อนการโหลด JavaScript” กับ “รวมไฟล์ JS” ไว้ ปกติมันดีนะ ทำให้เว็บโหลดเร็วขึ้น — แต่สคริปต์ของ Cloudflare ต้องรันตามลำดับที่ถูกต้องถึงจะ “วาด” กล่อง captcha ออกมา พอโดนเลื่อนการโหลด จังหวะมันเพี้ยน กล่องเลยไม่ถูกสร้าง
ผมพิสูจน์ด้วยการลองปิดตัวเร่งความเร็วชั่วคราว แล้วเทียบโค้ดที่ออกมา — เห็นชัดเลยว่าตอนเปิด สคริปต์โดนยัด attribute “เลื่อนโหลด” เพิ่มเข้ามา ส่วนตอนปิดมันสะอาด ผมเลยปิดการเลื่อนโหลดกับการรวมไฟล์ JS แล้วล้างแคชอีกรอบ
เบาะแสที่พลิกเกม: “แผนที่ก็หายไปด้วย”
ตรงนี้แหละครับที่เป็นจุดเปลี่ยน ระหว่างที่ลูกค้าช่วยทดสอบ เขาพิมพ์มาประโยคหนึ่งที่ผมเกือบมองข้าม — “แผนที่ Google ในหน้าติดต่อก็หายไปด้วยนะ”
หยุดคิดแป๊บ. captcha กับแผนที่ Google ไม่ได้เกี่ยวอะไรกันเลย — คนละระบบ คนละเจ้า แต่ทำไมมันพังพร้อมกัน? สิ่งเดียวที่ทั้งสองอย่างมีเหมือนกันคือ ทั้งคู่เป็น iframe ที่ฝังมาจากเว็บภายนอก (captcha มาจากเซิร์ฟเวอร์ Cloudflare, แผนที่มาจาก Google)
ถ้าของสองอย่างที่ไม่เกี่ยวกันเลยพังพร้อมกัน แปลว่ามันต้องมีอะไรสักอย่างที่บล็อก “iframe จากเว็บนอก” ทั้งหมด ไม่ใช่ปัญหาที่ตัว captcha หรือแผนที่เอง

ชั้นที่ 4 (ตัวจริง): นโยบายความปลอดภัยที่บล็อก iframe ทุกตัว
ผมเปิดเครื่องมือ developer ในเบราว์เซอร์ดู console แล้วก็เจอ — บรรทัดสีแดงที่ตามหามาทั้งวัน:
Framing 'https://challenges.cloudflare.com/' violates the following
Content Security Policy directive: "frame-src 'self' blob:".
The request has been blocked.
(และอีกบรรทัด บล็อก maps.google.com ด้วยเหตุผลเดียวกัน)
ตัวการคือ Content Security Policy (CSP) — เป็น header ความปลอดภัยที่บอกเบราว์เซอร์ว่า “อนุญาตให้โหลดอะไรจากที่ไหนได้บ้าง” ในเคสนี้มันตั้งกฎ frame-src 'self' blob: ไว้ ซึ่งแปลว่า “ฝัง iframe ได้เฉพาะจากเว็บตัวเองเท่านั้น” ผลคือมันบล็อกทั้ง captcha ของ Cloudflare และแผนที่ของ Google รวด — เพราะทั้งคู่มาจากโดเมนนอก
ที่ตลกร้ายคือ CSP ตัวนี้ถูกตั้งไว้ที่ฝั่ง Cloudflare เอง (ไม่ใช่ในตัว WordPress) — มันเลยบล็อก captcha ของ Cloudflare เองด้วย ผมยืนยันด้วยการเทียบ header ระหว่างเรียกผ่าน Cloudflare กับเรียกตรงเข้าเซิร์ฟเวอร์ — เรียกตรงไม่มี CSP ตัวนี้เลย แปลว่ามันถูกฉีดที่ชั้น Cloudflare ชัดเจน
วิธีแก้คือเข้าไปแก้กฎ CSP ตัวนั้น ให้ frame-src อนุญาตโดเมนที่จำเป็นเพิ่ม — ของ Cloudflare และของ Google แค่นั้นเอง ส่วนที่เหลือคงไว้เหมือนเดิม
// เดิม — บล็อกทุก iframe จากนอก
frame-src 'self' blob:
// แก้เป็น — เปิดเฉพาะเท่าที่จำเป็น
frame-src 'self' blob: https://challenges.cloudflare.com https://*.google.com
เซฟ รอ header อัปเดต แล้วรีเฟรชแบบล้างแคชเบราว์เซอร์ — คราวนี้กล่อง captcha ขึ้นเครื่องหมายถูกเขียว “Success!” และแผนที่กลับมาแสดงพร้อมกัน

ก่อน-หลัง และบทเรียนที่ผมได้
ปัญหาทั้งหมดมันซ้อนกัน 4 ชั้น และทุกชั้นต้องแก้จริง — แต่ตัวที่ “บล็อก” จริงๆ คือชั้นสุดท้ายที่อยู่ไกลตัวที่สุด สรุปสั้นๆ ให้เห็นภาพ
| ชั้น | อาการที่เห็น | ต้นเหตุจริง |
|---|---|---|
| 1 | ใส่ค่าใหม่แล้วโดนเขียนทับ | ค่า global เก่าค้างในฐานข้อมูล |
| 2 | แก้แล้วหน้าไม่เปลี่ยน | แคชหน้าเก่า + ลบไม่ได้เพราะสิทธิ์ไฟล์ |
| 3 | โค้ดถูกแต่กล่องไม่วาด | ตัวเร่งความเร็วเลื่อนการโหลด JS |
| 4 | captcha + แผนที่หายพร้อมกัน | CSP frame-src บล็อก iframe นอก |
บทเรียนที่ผมจะเก็บไว้ใช้ตลอดไปคือ — ถ้า captcha กับแผนที่ (หรือ iframe ฝังอื่นๆ) พังพร้อมกัน ให้สงสัย CSP เป็นอันดับแรก อย่าเพิ่งไปวนแก้ที่ key หรือปลั๊กอินเหมือนผม เปิด console ในเบราว์เซอร์ดูก่อนเลย ถ้าเห็นคำว่า “violates Content Security Policy” ก็ตัดจบได้เร็วขึ้นเป็นชั่วโมง
อีกเรื่องคือ — อย่ามองข้ามคำบ่นของผู้ใช้ ประโยค “แผนที่ก็หายด้วยนะ” ที่ฟังดูเหมือนไม่เกี่ยว กลับเป็นเบาะแสที่พาผมไปเจอต้นเหตุจริง บางทีคนที่ใช้งานจริงเขาเห็นภาพรวมที่เรามองไม่เห็น เพราะเรามัวแต่จ้องอยู่ที่จุดเดียว
ถ้าใครสนใจเรื่องการดูแลเว็บหลายๆ เว็บพร้อมกัน หรือการใช้เครื่องมือช่วย debug แบบนี้ ผมเคยเขียนไว้สองเรื่อง — จัดการเว็บ WordPress 90 กว่าเว็บ จากที่เดียว กับ ควบคุม Server ด้วย AI ผ่าน Coolify MCP ลองอ่านดูได้ครับ
หวังว่าเคสนี้จะเป็นประโยชน์กับคนที่กำลังนั่งงงว่าทำไม captcha ในเว็บถึงหายไปเฉยๆ นะครับ — บางทีปัญหาที่เห็นบนผิวน้ำ มันก็แค่ยอดของภูเขาน้ำแข็งจริงๆ
เครื่องมือหลักที่ใช้ในเคสนี้
- SSH + WP-CLI สำหรับขุดค่าในฐานข้อมูลและล้างแคช
- Developer Tools (Console) ในเบราว์เซอร์ — ตัวที่ชี้เป้า CSP ให้เจอ
- Cloudflare API สำหรับแก้กฎ Response Header (CSP)

