Skip to content

CORS Preflight Requests — Understanding the OPTIONS Handshake

DodaTech Updated 2026-06-28 2 min read

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

  1. What HTTP method does a preflight request use?
  2. What headers does the browser include in a preflight request?
  3. What must the server include in the preflight response?
  4. How long can a preflight response be cached?
  5. What happens if the preflight response doesn't include the requested method?

Answers:

  1. OPTIONS.
  2. Origin, Access-Control-Request-Method, Access-Control-Request-Headers.
  3. Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Headers.
  4. Up to the value of Access-Control-Max-Age (in seconds).
  5. 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

Does every cross-origin request send a preflight?

: No. Simple requests (GET, HEAD, POST with basic content types) skip preflight.

Can I avoid preflight requests?

: Use simple requests where possible, or use techniques like JSONP (legacy).

How long does a preflight request take?

: One additional round-trip to the server, typically 50-200ms.

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