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.
| Knob | What it changes | What it does not | Takeaway |
|---|---|---|---|
| Access-Control-Max-Age | How long OPTIONS may be reused | Who is allowed | Cache, not an allowlist |
| Access-Control-Allow-Origin | Which origin may read the response | Server authentication | See wildcard vs allowlist |
| Access-Control-Allow-Credentials | Whether cookies may be included | Whether * is legal | Cannot pair with * |
| Timing-Allow-Origin | Resource Timing visibility | CORS access to the body | Different 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 nowFAQ
- 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
Vulnerabilities
CORS Wildcard vs Allowlist for Public APIsVulnerabilities
CORS Credentials vs Wildcard OriginsVulnerabilities
Timing-Allow-Origin vs CORS for Cross-Origin Telemetry