Skip to content

CORS Request Flow Deep Dive — From Browser to Server and Back

DodaTech Updated 2026-06-28 4 min read

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

  1. What is the first step the browser takes before sending a cross-origin request?
  2. How does the browser determine whether to send a preflight request?
  3. What happens if the preflight response is missing Access-Control-Allow-Origin?
  4. At what point does the browser block JavaScript from reading the response?
  5. Can the server see the actual request if the preflight fails?

Answers:

  1. It checks whether the target origin is the same as the page origin.
  2. It checks if the request qualifies as simple (GET/HEAD/POST with simple content types).
  3. The browser blocks the actual request entirely.
  4. After receiving the response, when it checks the CORS headers.
  5. 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

Does the browser send any CORS headers with simple requests?

Yes. The browser adds the Origin header to all cross-origin requests, including simple ones. The server must respond with Access-Control-Allow-Origin for the response to be readable.

Can a server reject a request after the preflight succeeds?

Yes. The preflight only checks method and headers. The server can still reject the actual request based on content, authentication, or other factors.

How does the browser cache CORS preflight results?

The browser caches preflight results based on the Access-Control-Max-Age header. While cached, no preflight is sent for matching requests.

What happens if the server redirects a cross-origin request?

The browser follows the redirect but checks CORS headers on the redirect response. If the redirect goes to a different origin, CORS headers from that origin must be present.

Does the CORS flow differ for fetch versus XMLHttpRequest?

No. Both fetch and XMLHttpRequest follow the same CORS algorithm. The browser applies identical origin checks and preflight logic.

Can service workers bypass CORS?

Service workers can intercept fetch events and return cached responses, but they are still subject to CORS when making network requests. They cannot bypass the same-origin policy.

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