Skip to content

Access-Control-Max-Age — Caching Preflight Responses for Performance

DodaTech Updated 2026-06-28 3 min read

In this tutorial, you will learn about Access. We cover key concepts, practical examples, and best practices to help you master this topic.

The Access-Control-Max-Age response header specifies how long the browser should cache the preflight response, eliminating the need for repeated OPTIONS requests for subsequent matching cross-origin requests.

What You'll Learn

  • How preflight Caching improves performance
  • Optimal Max-Age values and browser limits
  • Security considerations for long cache durations

Why It Matters

Each preflight adds 50-200ms latency. Caching eliminates this overhead for subsequent requests. DodaTech's API uses a 2-hour cache to balance performance with security for frequently accessed endpoints.

flowchart TD
    A["First request to origin"] --> B["Send OPTIONS preflight"]
    B --> C["Server responds with Max-Age: 7200"]
    C --> D["Browser caches preflight for 2 hours"]
    D --> E["Subsequent requests to same origin"]
    E --> F{"Cache still valid?"}
    F -->|"Yes"| G["Skip preflight, send request directly"]
    F -->|"No"| B
    style D fill:#fef08a,stroke:#ca8a04
    style G fill:#86efac,stroke:#16a34a

Code Examples

// Preflight caching is automatic in the browser
// You cannot control it from JavaScript
// But you can observe it in the Network tab:
// First OPTIONS request returns Max-Age
// Subsequent requests show no OPTIONS

// Measure cached vs uncached latency
const start = performance.now();
await fetch('https://api.example.com/data');
console.log('First request:', performance.now() - start, 'ms');

const start2 = performance.now();
await fetch('https://api.example.com/data');
console.log('Cached request:', performance.now() - start2, 'ms');
# Flask setting Max-Age
@app.after_request
def set_max_age(response):
    if request.method == 'OPTIONS':
        # Cache preflight for 1 hour
        response.headers['Access-Control-Max-Age'] = '3600'
    return response
// Express setting Max-Age
const cors = require('cors');
const corsOptions = {
  origin: 'https://app.example.com',
  methods: 'GET,POST,PUT,DELETE',
  allowedHeaders: 'Content-Type,Authorization',
  maxAge: 86400  // 24 hours in seconds
};
app.use(cors(corsOptions));
# Check the Max-Age header in a preflight response
curl -X OPTIONS -I \
  -H "Origin: https://app.example.com" \
  -H "Access-Control-Request-Method: POST" \
  https://api.example.com/data | grep -i "max-age"

Common Mistakes

1. Setting Max-Age Too High

Values over 24 hours risk stale permissions if server configuration changes.

2. Setting Max-Age Too Low

Values under 60 seconds negate the caching benefit and increase preflight overhead.

3. Ignoring Browser Max-Age Limits

Chrome caps Max-Age at 600 seconds (10 min). Firefox caps at 86400 seconds (24 hours).

4. Not Setting Max-Age on Preflight Responses

Without Max-Age, the browser may still cache the preflight (default is 5 seconds in some browsers).

5. Changing Server Config Without Considering Cached Preflights

Clients may use stale cached permissions. Deploy configuration changes gradually.

Practice Questions

  1. What does Access-Control-Max-Age control?
  2. What is Chrome's maximum Max-Age value?
  3. What happens when Max-Age expires?
  4. Can JavaScript read or clear the preflight cache?
  5. What is a reasonable Max-Age value for production?

Answers:

  1. How long the browser caches the preflight response in seconds.
  2. 600 seconds (10 minutes).
  3. The browser sends a new OPTIONS preflight request.
  4. No. The preflight cache is managed entirely by the browser.
  5. 600-86400 seconds (10 min to 24 hours), depending on how often your CORS config changes.

Challenge: Measure the performance impact of different Max-Age values in a test application. Create a chart showing request latency vs Max-Age setting, and determine the optimal value for your use case.

FAQ

Does Max-Age apply to all origins or per-origin?

Per-origin. The preflight cache is keyed by the origin and the requested URL. Different origins have separate cache entries.

Can the browser preflight cache be manually invalidated?

No. There is no JavaScript API to clear the preflight cache. The browser manages it internally based on Max-Age.

What is the default Max-Age if the header is not set?

The Fetch specification suggests a default of 5 seconds, but browser implementations vary. Some browsers may not cache preflight responses at all without an explicit Max-Age.

Does Max-Age affect simple requests?

No. Simple requests do not trigger preflight, so Max-Age has no effect on them. It only applies to preflighted requests.

Is there a security risk with long Max-Age values?

Yes. If you change your CORS policy to be more restrictive, clients with cached preflights will continue using the old permissive policy until the cache expires.

Mini Project

Build a preflight cache analyzer: a test page that makes repeated cross-origin requests with different Max-Age values, logs whether each request triggered a preflight, and displays the effective cache duration. Include a recommendation engine that suggests optimal Max-Age based on request frequency and CORS config change history.

What's Next

Explore the wildcard and credentials conflict to understand why these cannot be combined, then study dynamic origin whitelisting for multi-origin APIs.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro