CORS Request Flow Deep Dive — From Browser to Server and Back
In this tutorial, you will learn about CORS Request Flow Deep Dive. We cover key concepts, practical examples, and best practices to help you master this topic.
A CORS request flow involves the browser constructing the request, checking the same-origin policy, conditionally sending a preflight, and validating server response headers before allowing JavaScript access to the response.
What You'll Learn
- The step-by-step CORS request lifecycle
- How the browser decides between simple and preflight requests
- How response headers are validated and enforced
Why It Matters
Every cross-origin request goes through this flow. Understanding each step helps you debug CORS errors and configure servers correctly. Doda Browser uses this knowledge to implement secure cross-origin resource loading.
flowchart TD
A["Page at https://app.example.com"] --> B["JavaScript calls fetch()"]
B --> C{"Same origin?"}
C -->|"Yes"| D["Request sent directly"]
C -->|"No"| E{"Simple request?"}
E -->|"Yes"| F["Send actual request"]
E -->|"No"| G["Send OPTIONS preflight"]
G --> H["Server responds 200 with CORS headers"]
H --> I{"Headers valid?"}
I -->|"Yes"| F
I -->|"No"| J["Blocked by browser"]
F --> K["Server responds with data"]
K --> L{"CORS headers present and valid?"}
L -->|"Yes"| M["Response exposed to JS"]
L -->|"No"| J
style A fill:#dbeafe,stroke:#2563eb
style M fill:#86efac,stroke:#16a34a
style J fill:#fecaca,stroke:#dc2626
Code Examples
// The browser handles the CORS flow automatically
// You only see the result
fetch('https://api.other.com/data', {
method: 'GET',
mode: 'cors' // default
}).then(response => {
// Browser already validated CORS headers
return response.json();
}).catch(err => {
// CORS error reaches here
console.error('CORS blocked the request');
});
# Server-side logging to trace CORS flow
import logging
logging.basicConfig(level=logging.INFO)
@app.before_request
def log_cors_flow():
origin = request.headers.get('Origin', 'none')
method = request.method
logging.info(f'Request from origin={origin} method={method}')
if method == 'OPTIONS':
logging.info('Preflight request detected')
# Trace the CORS flow manually with curl
curl -v -H "Origin: https://app.example.com" \
https://api.other.com/data 2>&1 | grep -i "access-control"
Common Mistakes
1. Expecting the Browser to Send the Actual Request First
The browser sends preflight first. No actual request is sent if preflight fails.
2. Confusing Server-Side and Client-Side CORS
CORS headers need to be on the server response. Client-side JavaScript cannot set them.
3. Thinking CORS Blocks Request Sending
CORS does not block request sending. It blocks JavaScript from reading the response.
4. Forgetting That Non-Simple Methods Always Trigger Preflight
PUT, DELETE, PATCH always trigger preflight regardless of headers.
5. Assuming CORS Errors Show in the Response Body
CORS errors appear in the browser console, not in the response body.
Practice Questions
- What is the first step the browser takes before sending a cross-origin request?
- How does the browser determine whether to send a preflight request?
- What happens if the preflight response is missing Access-Control-Allow-Origin?
- At what point does the browser block JavaScript from reading the response?
- Can the server see the actual request if the preflight fails?
Answers:
- It checks whether the target origin is the same as the page origin.
- It checks if the request qualifies as simple (GET/HEAD/POST with simple content types).
- The browser blocks the actual request entirely.
- After receiving the response, when it checks the CORS headers.
- No. The browser never sends the actual request if preflight fails.
Challenge: Set up a local Express server and an HTML page on a different port. Add logging at every step of the CORS flow and observe the request sequence in both browser dev tools and server logs.
FAQ
Mini Project
Build a CORS flow visualizer: a simple page at localhost:3000 that makes cross-origin requests to a server at localhost:3001. Add console logging at every step, display the preflight request in the Network tab, and show whether each request was simple or preflighted. Include buttons for GET, POST with JSON, and DELETE to trigger different flow paths.
What's Next
Learn about the difference between simple and preflight requests, then explore the complete CORS headers reference.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro