Access-Control-Allow-Origin and CORS policy errors
Tells the browser which other origins may read this response from JavaScript. Too strict and your front end breaks; too loose and any site can read your users' data.
Updated 3 November 2026
Check your site's CORS header
We send one normal request (following redirects) and grade the Access-Control-Allow-Origin header on the final response.
Recommended value
Access-Control-Allow-Origin: https://app.example.com
| Value / directive | What it does |
|---|---|
| https://app.example.com | Exactly one origin may read the response. To allow several, check the request Origin against an allowlist and echo the match. |
| * | Any origin may read it, but only without credentials. Fine for public, unauthenticated data. |
| null | Matches sandboxed iframes and local files. Never allow it. |
What "blocked by CORS policy" means
Browsers apply the same-origin policy: JavaScript on https://app.example.com cannot read responses from https://api.example.com unless the API says so. When you see
Access to fetch at 'https://api.example.com/user' from origin 'https://app.example.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
the fix belongs on the server being called (the API), not in the front-end code. The API must answer with Access-Control-Allow-Origin: https://app.example.com, and for non-simple requests (JSON bodies, custom headers, PUT/DELETE) it must also answer the preflight OPTIONS request with Access-Control-Allow-Methods and Access-Control-Allow-Headers.
Credentials
If the front end sends cookies (fetch(url, { credentials: 'include' }), or Axios withCredentials), the response must name the exact origin and send Access-Control-Allow-Credentials: true. A * wildcard is rejected by the browser when credentials are involved.
The dangerous mistake: reflecting any origin
A common shortcut is to copy whatever Origin the request sends into Access-Control-Allow-Origin and add Allow-Credentials: true. That lets any website read your logged-in users' API responses. Our check above sends a made-up Origin and tells you if your server reflects it. Echo the origin only after checking it against an allowlist, and add Vary: Origin so caches do not serve one origin's response to another.
Laravel
Laravel handles CORS with the built-in HandleCors middleware and config/cors.php (publish it with php artisan config:publish cors). Set paths to your API routes, list exact origins in allowed_origins (or careful regexes in allowed_origins_patterns), and only set supports_credentials to true if you use cookie auth such as Sanctum's SPA mode.
How to set Access-Control-Allow-Origin
nginx
# Allowlist, not reflection: only listed origins are echoed back.
map $http_origin $cors_origin {
default "";
"https://app.example.com" $http_origin;
}
server {
location /api/ {
add_header Access-Control-Allow-Origin $cors_origin always;
add_header Access-Control-Allow-Credentials "true" always;
add_header Vary "Origin" always;
if ($request_method = OPTIONS) {
add_header Access-Control-Allow-Origin $cors_origin always;
add_header Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE" always;
add_header Access-Control-Allow-Headers "Content-Type, Authorization, X-XSRF-TOKEN" always;
add_header Vary "Origin" always;
return 204;
}
}
}
Apache
SetEnvIf Origin "^https://app\.example\.com$" CORS_ORIGIN=$0
Header always set Access-Control-Allow-Origin "%{CORS_ORIGIN}e" env=CORS_ORIGIN
Header always set Access-Control-Allow-Credentials "true" env=CORS_ORIGIN
Header always merge Vary "Origin"
Laravel middleware
// config/cors.php (php artisan config:publish cors)
return [
'paths' => ['api/*', 'sanctum/csrf-cookie'],
'allowed_methods' => ['*'],
'allowed_origins' => ['https://app.example.com'],
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => true, // only for cookie-based auth (Sanctum SPA)
];
Express (Node.js)
const cors = require('cors');
app.use('/api', cors({
origin: ['https://app.example.com'], // never `true` with credentials
credentials: true,
}));
Cloudflare
Cloudflare transform rules set static values, so they suit a single allowed origin or a public "*" API. For several origins, handle CORS in your application or a Worker that checks the Origin against an allowlist.
In nginx, an add_header inside a location block discards every add_header inherited from the server block. Repeat your security headers there, or use an include file. The always flag also sends the header on error responses.
Common mistakes
-
Reflecting the request Origin with
Access-Control-Allow-Credentials: true. Any site can then read authenticated responses. -
Allowing the
nullorigin. Sandboxed iframes andfile://pages sendOrigin: null, so attackers can too. -
Loose regexes like
example\.com$that also matchevil-example.com. - Trying to fix a CORS error in the front end, or with a browser extension. The server has to send the header.
-
Forgetting
Vary: Originwhen the header changes per request.
FAQ
How do I fix "blocked by CORS policy: No Access-Control-Allow-Origin header"?
Configure the API you are calling to return Access-Control-Allow-Origin with your front-end origin, and to answer preflight OPTIONS requests. In Laravel, set allowed_origins in config/cors.php.
Can I use Access-Control-Allow-Origin: * with cookies?
No. Browsers reject a wildcard when credentials are sent. Name the exact origin and send Access-Control-Allow-Credentials: true.
Is Access-Control-Allow-Origin: * insecure?
Not for public, unauthenticated data. It is wrong for anything that depends on the user being logged in.
What is a CORS preflight?
An OPTIONS request the browser sends before non-simple requests to ask which methods and headers are allowed. The server must answer it with the Access-Control-Allow-* headers.
Related guides
Headers are one of 30+ things attackers check
The free StackShield scan also checks for exposed .env files, debug mode, leaked tooling, weak TLS and more, from the outside, in about a minute.
Run a free scan