Introduction
Before making cross-origin requests with non-simple methods or headers, browsers send an OPTIONS preflight request to check which methods are allowed. The response includes Access-Control-Max-Age, which tells the browser how long to cache the preflight result. When new API methods are added but the cached preflight response does not include them, the browser blocks the new requests until the cache expires.
Symptoms
- New API endpoint returns
CORS errorin the browser console - OPTIONS request shows old
Access-Control-Allow-Methodslist missing the new method - Direct API call works in curl/Postman but fails in the browser
- Issue resolves after clearing browser cache or after the max-age expires
- Error message:
Method DELETE is not allowed by Access-Control-Allow-Methods in preflight response
Common Causes
Access-Control-Max-Ageset to a long duration (e.g., 86400 seconds = 24 hours)- New API method deployed but preflight response was not updated
- API gateway CORS configuration not including all available methods
- Browser caching the preflight response across different paths
- CDN caching the OPTIONS response with a long TTL
Step-by-Step Fix
- 1.Check the current preflight response headers: See what methods are allowed.
- 2.```bash
- 3.curl -X OPTIONS https://api.example.com/resource \
- 4.-H "Origin: https://app.example.com" \
- 5.-H "Access-Control-Request-Method: DELETE" \
- 6.-H "Access-Control-Request-Headers: Content-Type" \
- 7.-I
- 8.# Check:
- 9.# Access-Control-Allow-Methods: GET, POST, PUT
- 10.# Access-Control-Max-Age: 86400
- 11.# DELETE is missing from the allowed methods
- 12.
` - 13.Update the CORS configuration to include all methods: Fix the allowed methods list.
- 14.```nginx
- 15.# Nginx CORS configuration
- 16.add_header 'Access-Control-Allow-Origin' 'https://app.example.com' always;
- 17.add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PATCH, OPTIONS' always;
- 18.add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization' always;
- 19.add_header 'Access-Control-Max-Age' 3600; # Reduced from 86400
- 20.
` - 21.Reduce the preflight cache duration: Allow faster propagation of changes.
- 22.
` - 23.# Set Access-Control-Max-Age to a reasonable value:
- 24.# Production: 3600 (1 hour)
- 25.# Development: 0 (no caching)
- 26.# Avoid values above 86400 (24 hours) - browser maximum in Chrome
- 27.
` - 28.Invalidate the cached preflight response: Force browsers to re-check.
- 29.
` - 30.# Options to invalidate cached preflight:
- 31.# 1. Change the CORS headers (different Max-Age triggers re-validation)
- 32.# 2. Add a query parameter to the URL (changes the preflight cache key)
- 33.# 3. Wait for the Max-Age to expire
- 34.# 4. Clear browser cache (not practical for end users)
- 35.
` - 36.Verify the preflight response includes all methods: Confirm the fix.
- 37.```bash
- 38.curl -X OPTIONS https://api.example.com/resource \
- 39.-H "Origin: https://app.example.com" \
- 40.-H "Access-Control-Request-Method: DELETE" \
- 41.-I | grep "Access-Control-Allow-Methods"
- 42.# Should include DELETE in the response
- 43.
`
Prevention
- Set
Access-Control-Max-Ageto 1-4 hours in production, not 24 hours - Include all current and planned HTTP methods in the CORS allowed methods list
- Test CORS preflight responses after deploying new API methods
- Monitor browser CORS error rates in application error tracking tools
- Document CORS configuration requirements in the API deployment checklist
- Use a consistent CORS configuration across all API endpoints and environments