RoastMyOpsec

News

Access-Control-Max-Age vs CORS Preflight Caching

January 18, 2022

Access-Control-Max-Age vs CORS preflight caching is how long a browser may skip repeating an OPTIONS check, not whether the API is safe. Access-Control-Max-Age is a seconds budget on a successful preflight. A large value cuts OPTIONS traffic; it also delays browsers from seeing a tighter allowlist after you ship a fix. Wildcard origins, credentialed echoes, and missing methods stay wrong no matter the cache. Browsers cap the value — Chrome has historically clamped around two hours. RoastMyOpsec app mode sends a bounded untrusted Origin and reads ACAO and credentials. It does not score Max-Age, does not send your users' cookies, and does not attempt to skip or forge preflights.

The practical difference

Preflight is the browser asking 'may this cross-origin request use these methods and headers?' Max-Age is the answer's TTL. Access-Control-Allow-Origin and Allow-Credentials are the policy. Mixing them up is how teams 'optimize CORS' and leave * with cookies.

Simple GETs without custom headers may skip preflight entirely. Max-Age never runs for those. Policy still matters.

How to choose a Max-Age

If the allowlist is stable and public, a moderate Max-Age (minutes to the browser cap) is fine. If you are rolling back a bad ACAO, expect cached preflights until TTL or a browser restart. Do not use Max-Age as a WAF. Do not set it to years and assume clients obey.

KnobWhat it changesWhat it does notTakeaway
Access-Control-Max-AgeHow long OPTIONS may be reusedWho is allowedCache, not an allowlist
Access-Control-Allow-OriginWhich origin may read the responseServer authenticationSee wildcard vs allowlist
Access-Control-Allow-CredentialsWhether cookies may be includedWhether * is legalCannot pair with *
Timing-Allow-OriginResource Timing visibilityCORS access to the bodyDifferent header; see TAO

What the roast can prove

A roast can show ACAO reflecting an untrusted Origin and credentials behavior. Absence of Max-Age is not a finding. A huge Max-Age on a public API is an ops note, not an F. Pair with CORS credentials vs wildcard if cookies are in play.

Common mistakes

The first mistake is setting Max-Age to 86400 and thinking Chrome will honor a full day.

The second mistake is tightening ACAO and wondering why old SPAs still succeed until the preflight cache dies.

The third mistake is treating a missing Max-Age like a missing CSP.

Free audit the URL you own

RoastMyOpsec is a defensive public-surface roast: headers, cookies, sensitive paths, and more — no exploit payloads. Start with the free audit, then open the vault if the blurred findings look expensive.

Free audit now

FAQ

Does a high Access-Control-Max-Age weaken CORS?
It slows policy changes in browsers that already cached a preflight. It does not grant new origins. Fix Allow-Origin first.
Should every API set Max-Age?
Only if preflights are hot and the allowlist is stable. Public static assets that never preflight do not need it.
Does RoastMyOpsec score Access-Control-Max-Age?
No. The scan reads ACAO and credentials from a bounded Origin. Max-Age is not a scored finding.

Sources

Related guides