Access to fetch at 'https://api.example.com' from origin 'https://app.example.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present. Browsers block JavaScript from reading responses from another origin unless that server explicitly allows it. The fix always belongs on the API server, not in the frontend.
Before you start
- The exact origin of your frontend (scheme + host + port, e.g.
https://app.example.com). - Access to the API's code or its web server configuration.
How CORS works in short
- An origin is scheme + host + port;
https://example.comandhttps://www.example.comare different origins. - For simple GET requests the browser sends the request and checks the response for
Access-Control-Allow-Origin. - For requests with JSON bodies, custom headers (e.g.
Authorization) or methods like PUT/DELETE, the browser first sends an OPTIONS preflight; the server must answer it with the allowed methods and headers. - Tools like curl and Postman ignore CORS, which is why the API "works" there.
Preferred: configure CORS in the application
Frameworks have built-in or standard CORS support that handles preflights correctly:
- Laravel:
config/cors.php(allowed_origins,paths). - Express: the
corsmiddleware, e.g.app.use(cors({ origin: ['https://app.example.com'] })). - Django: django-cors-headers with
CORS_ALLOWED_ORIGINS. - Flask: Flask-CORS. Strapi:
config/middlewaresCORS settings.
Alternatively: Nginx
location /api/ {
if ($request_method = OPTIONS) {
add_header Access-Control-Allow-Origin "https://app.example.com" always;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
add_header Access-Control-Allow-Headers "Authorization, Content-Type" always;
add_header Access-Control-Max-Age 86400 always;
return 204;
}
add_header Access-Control-Allow-Origin "https://app.example.com" always;
add_header Vary Origin always;
proxy_pass http://127.0.0.1:3000;
}always makes Nginx add the headers to error responses too, so you see the real error instead of a misleading CORS one. Do not set CORS headers in both Nginx and the app: duplicated headers are rejected by browsers.
Alternatively: Apache / .htaccess
<IfModule mod_headers.c>
Header always set Access-Control-Allow-Origin "https://app.example.com"
Header always set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
Header always set Access-Control-Allow-Headers "Authorization, Content-Type"
Header always append Vary Origin
</IfModule>Make sure OPTIONS requests are answered with a 2xx status; add a rewrite rule returning 204 for OPTIONS if the app rejects them.
Cookies and credentials
If the frontend sends cookies (credentials: 'include'):
- The server must send
Access-Control-Allow-Credentials: true. Access-Control-Allow-Originmust be the exact origin, not*.- Cookies need
SameSite=None; Securefor cross-site use.
Allowing several origins
The header accepts only one origin. To allow several, the app (or an Nginx map on $http_origin) must check the request's Origin against an allowlist and echo it back, with Vary: Origin. Avoid reflecting any origin blindly, especially with credentials.
Check the fix
curl -si -X OPTIONS https://api.example.com/api/items -H 'Origin: https://app.example.com' -H 'Access-Control-Request-Method: POST' -H 'Access-Control-Request-Headers: content-type' | grep -i access-controlYou should see the allow headers in the response.
Troubleshooting
- CORS error but the real problem is a 500 or 404: error responses often lack CORS headers. Check the Network tab's status code.
- The value has multiple origins / header contains multiple values: CORS headers set in two places.
- Works for GET, fails for POST: the preflight is not handled.
CORS FAQ
Is Access-Control-Allow-Origin: * safe?
For truly public, read-only data without credentials, yes. For anything with authentication, list specific origins.
Can I fix CORS in the frontend?
No. Only the server can grant access; the alternative is to proxy API calls through your own domain.
Does CORS protect my API from scripts?
No, it only restricts browsers. Protect APIs with authentication and rate limits.
