Visit the CORS tutorial at web.dev for background on how to use CORS.
After configuring CORS on your server, it's important to verify that your configuration works correctly. This guide covers multiple testing approaches to ensure your CORS setup is working as expected.
The curl command-line tool is the quickest way to test CORS headers. Here are common test scenarios:
Test a basic GET request with an Origin header:
# Test with allowed origin
curl -H "Origin: https://example.com" \
-I https://your-api.com/endpoint
# Expected response headers:
# Access-Control-Allow-Origin: https://example.com
# Vary: Origin
For requests with custom headers or methods like PUT/DELETE, browsers send a preflight OPTIONS request:
# Test preflight
curl -H "Origin: https://example.com" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: Content-Type, Authorization" \
-X OPTIONS \
-I https://your-api.com/endpoint
# Expected response headers:
# Access-Control-Allow-Origin: https://example.com
# Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
# Access-Control-Allow-Headers: Content-Type, Authorization
# Access-Control-Max-Age: 86400
After preflight succeeds, test the actual request:
# POST request with custom headers
curl -H "Origin: https://example.com" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer token123" \
-X POST \
-d '{"test":"data"}' \
https://your-api.com/endpoint
Verify that unauthorized origins are rejected:
# Test with disallowed origin
curl -H "Origin: https://evil.com" \
-I https://your-api.com/endpoint
# Should NOT return Access-Control-Allow-Origin header
If your API supports credentials (cookies, auth):
# Test with credentials
curl -H "Origin: https://example.com" \
--cookie "session=abc123" \
-I https://your-api.com/endpoint
# Expected response headers:
# Access-Control-Allow-Origin: https://example.com
# Access-Control-Allow-Credentials: true
# Vary: Origin
Browser developer tools provide detailed CORS debugging information:
Access-Control-Allow-OriginAccess-Control-Allow-MethodsAccess-Control-Allow-HeadersAccess-Control-Allow-CredentialsVary: OriginThe browser console displays helpful CORS error messages. Common patterns:
Quickly test CORS from the browser console:
// Simple GET request
fetch('https://your-api.com/endpoint', {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => console.log('Success:', data))
.catch(error => console.error('CORS Error:', error));
// POST with credentials
fetch('https://your-api.com/endpoint', {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token123'
},
body: JSON.stringify({test: 'data'})
})
.then(response => response.json())
.then(data => console.log('Success:', data))
.catch(error => console.error('CORS Error:', error));
Cause: Server is not sending CORS headers.
Solution: Configure your server to send the Access-Control-Allow-Origin header. See the server-specific guides for your platform.
Cause: CORS headers are being set in multiple places (e.g., both application code and web server config).
Solution: Choose one configuration method and remove the duplicate. Check both your application code and web server configuration.
Cause: Using Access-Control-Allow-Origin: * with Access-Control-Allow-Credentials: true.
Solution: Specify exact origin instead of wildcard when using credentials:
Access-Control-Allow-Origin: https://example.com Access-Control-Allow-Credentials: true
Cause: The HTTP method you're using is not listed in Access-Control-Allow-Methods.
Solution: Add the method to your CORS configuration:
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Cause: Custom header is not listed in Access-Control-Allow-Headers.
Solution: Add the header to your CORS configuration:
Access-Control-Allow-Headers: Content-Type, Authorization, X-Custom-Header
Cause: Server is redirecting the OPTIONS preflight request.
Solution: Ensure preflight OPTIONS requests return 204 No Content without redirects. Check for trailing slash redirects or authentication redirects on OPTIONS requests.
A properly configured CORS server should return these headers:
Access-Control-Allow-Origin: https://example.com Vary: Origin
Access-Control-Allow-Origin: https://example.com Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS Access-Control-Allow-Headers: Content-Type, Authorization Access-Control-Max-Age: 86400 Vary: Origin
Access-Control-Allow-Origin: https://example.com Access-Control-Allow-Credentials: true Vary: Origin
If your API returns custom headers that the client needs to access:
Access-Control-Expose-Headers: X-Custom-Header, X-Total-Count
If CORS isn't working, check these common issues:
Vary: Origin when dynamically setting the origin* with Access-Control-Allow-Credentials: trueThe content on this site stays fresh thanks to help from users like you! If you have suggestions or would like to contribute, fork us on GitHub.
Save 39% on CORS in Action with promotional code hossainco at manning.com/hossain