CORS Preflight Requests — Understanding the OPTIONS Handshake
In this tutorial, you will learn about CORS Preflight Requests. We cover key concepts, practical examples, and best practices to help you master this topic.
A CORS preflight request is an OPTIONS request that browsers send automatically before certain cross-origin requests to ask the server whether the actual request is allowed.
What You'll Learn
- When browsers send preflight requests and why
- The structure of OPTIONS preflight requests and responses
- How to configure servers to handle preflight correctly
Why It Matters
Preflight requests add an extra round-trip to every cross-origin API call. Misconfigured preflight handling is the most common cause of CORS errors.
sequenceDiagram
Browser->>Server: OPTIONS /api/data
Browser->>Server: Origin: https://myapp.com
Browser->>Server: Access-Control-Request-Method: POST
Browser->>Server: Access-Control-Request-Headers: Content-Type
Server-->>Browser: 200 OK
Server-->>Browser: Access-Control-Allow-Origin: https://myapp.com
Server-->>Browser: Access-Control-Allow-Methods: POST, GET
Server-->>Browser: Access-Control-Allow-Headers: Content-Type
Browser->>Server: POST /api/data (actual request)
Server-->>Browser: 200 OK (response with data)
Code Examples
// This complex request triggers preflight
fetch('https://api.example.com/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'abc123' // custom header
},
body: JSON.stringify({name: 'test'})
});
# Server handling preflight (Flask)
from flask import Flask, jsonify, make_response
app = Flask(__name__)
@app.route('/api/data', methods=['OPTIONS', 'POST'])
def handle_data():
if request.method == 'OPTIONS':
response = make_response()
response.headers['Access-Control-Allow-Origin'] = 'https://myapp.com'
response.headers['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS'
response.headers['Access-Control-Allow-Headers'] = 'Content-Type, X-API-Key'
response.headers['Access-Control-Max-Age'] = '86400'
return response
# Handle actual POST
return jsonify({"status": "ok"})
# Simulate a preflight with curl
curl -X OPTIONS https://api.example.com/data \
-H "Origin: https://myapp.com" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: Content-Type" \
-v
Common Mistakes
1. Not Handling OPTIONS Requests at All
If the server doesn't respond to OPTIONS, the browser blocks the actual request.
2. Returning 404 for Preflight
Preflight requests must return 200. A 404 causes the browser to block the request.
3. Missing the Requested Method in Allow-Methods
If the browser asks for POST but Allow-Methods only includes GET, the actual request is blocked.
4. Setting Max-Age Too High or Too Low
High values risk stale permissions; low values increase preflight frequency.
5. Forgetting to Include Credentials Header
When credentials: 'include' is used, the server must also allow credentials.
Practice Questions
- What HTTP method does a preflight request use?
- What headers does the browser include in a preflight request?
- What must the server include in the preflight response?
- How long can a preflight response be cached?
- What happens if the preflight response doesn't include the requested method?
Answers:
- OPTIONS.
Origin,Access-Control-Request-Method,Access-Control-Request-Headers.Access-Control-Allow-Origin,Access-Control-Allow-Methods,Access-Control-Allow-Headers.- Up to the value of
Access-Control-Max-Age(in seconds). - The browser blocks the actual cross-origin request.
Challenge: Set up an Express server that handles OPTIONS preflight requests for POST with custom headers. Test it with a browser fetch.
FAQ
What's Next
Learn all CORS Headers in detail, then explore Credentials Mode with CORS.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro