ข้ามไปยังเนื้อหา

CORS

ถ้ามี JavaScript บน browser ที่อยู่คนละ origin เรียก API ของคุณ คุณจะได้เจอกับ CORS แน่นอน นี่คือเรื่องที่คนเข้าใจผิดกันมากที่สุดเรื่องหนึ่งของ web API เรามาทำความเข้าใจให้ชัดกันว่า CORS คืออะไร และไม่ใช่อะไร

same-origin policy ของ browser จะบล็อกหน้าเว็บบน https://app.example.com ไม่ให้อ่าน response จาก https://api.example.com เว้นแต่ API จะอนุญาตอย่างชัดเจนผ่าน header ของ Cross-Origin Resource Sharing ตัวที่บังคับใช้ CORS คือ browser ซึ่งทำแทน user ไม่ใช่การควบคุมการเข้าถึงฝั่ง server

สำหรับ request แบบ “non-simple” (ส่วนใหญ่คือ JSON POST/PUT/DELETE และ header ที่กำหนดเอง) browser จะส่ง request preflight แบบ OPTIONS ไปก่อนเพื่อขออนุญาต:

sequenceDiagram
  participant B as Browser
  participant S as API
  B->>S: OPTIONS /articles (preflight)
  S-->>B: 204 + Access-Control-Allow-Origin/Methods/Headers
  B->>S: POST /articles (actual request)
  S-->>B: 201 Created + Access-Control-Allow-Origin
browser ตรวจสอบสิทธิ์ด้วย OPTIONS ก่อนจะส่ง request จริง

header ใน response ที่สำคัญ: Access-Control-Allow-Origin (origin ใดบ้างที่อ่าน response ได้), -Allow-Methods, -Allow-Headers และ -Allow-Credentials (อนุญาตให้ส่ง cookie/credentials หรือไม่)

JavaScript

จงหลีกเลี่ยงความผิดพลาดคลาสสิกของการสะท้อน origin ใดก็ได้ (Access-Control-Allow-Origin: *) ควบคู่กับ Allow-Credentials: true — spec ห้ามใช้คู่กันอยู่แล้ว และที่สำคัญกว่านั้นคือเท่ากับเปิดเผย response ที่ผ่าน authentication ให้เว็บไซต์ใดก็ได้

CORS Headerความหมายตัวอย่าง
Access-Control-Allow-Originorigin ที่อนุญาตhttps://app.example.com หรือ *
Access-Control-Allow-MethodsHTTP method ที่อนุญาตGET, POST, PATCH, DELETE
Access-Control-Allow-Headersheader ที่ client ส่งได้Content-Type, Authorization
Access-Control-Max-Agecache preflight นานแค่ไหน86400 (1 วัน)

Access-Control-Allow-Origin: * กับ Credentials อาการ:

  • ตั้ง Access-Control-Allow-Origin: * แต่ต้องการส่ง cookie หรือ Authorization header
  • browser block request — * ไม่ work กับ credentials
  • ต้องระบุ origin จริง เช่น https://app.example.com เมื่อใช้ credentials

Preflight ทุก Request เพราะ Cache ไม่ได้ อาการ:

  • ไม่ตั้ง Access-Control-Max-Age — browser ส่ง OPTIONS preflight ทุกครั้ง
  • เพิ่ม latency ทุก request
  • ตั้ง Access-Control-Max-Age: 86400 เพื่อ cache preflight 1 วัน

💡 ตัวอย่างจากของจริง

GitHub API:

  • CORS ที่อนุญาตเฉพาะ origin ที่ register ผ่าน OAuth App
  • ไม่อนุญาต * สำหรับ authenticated endpoint

Stripe.js:

  • browser SDK เรียก Stripe API โดยตรง — CORS config อนุญาตเฉพาะ Stripe domain
  • API Key ที่ใช้ใน browser เป็น publishable key เท่านั้น (ไม่มีสิทธิ์ charge)
ใครเป็นผู้บังคับใช้ CORS?
preflight request คืออะไร?
ทำไม `Allow-Origin: *` ร่วมกับ `Allow-Credentials: true` จึงเป็นปัญหา?