For requests with custom headers, non-simple methods (PUT/DELETE), or JSON bodies, the browser first sends an automatic OPTIONSpreflight asking "may I?". The server must answer with Access-Control-Allow-Methods/-Headers (and -Origin). Only if approved does the real request go. Simple GETs skip preflight. Misconfigured preflight responses are the #1 CORS headache.
Power-ups you unlock
Browser auto-sends OPTIONS preflight first
Triggered by custom headers / PUT/DELETE / JSON
Server must allow methods + headers + origin
Simple GETs skip preflight
The 401 Bandit attacks — common mistakes
Server not handling the OPTIONS method
Missing Allow-Headers for a custom header
Expecting GET behavior to match a JSON POST
Boss battleName three things that trigger a CORS preflight.
Example code
<!doctype html><html><head><meta charset="utf-8"></head>
<body style="background:#06040d;color:#e6e0ff;font-family:monospace;padding:20px"><pre>OPTIONS /data (preflight)
→ Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: Content-Type
then the real POST proceeds</pre></body></html>