CORS errors confuse everyone because the message appears in the *browser*, but the fix lives on the *server*. Your frontend on one origin is calling an API on another, and the API never told the browser it is allowed. Let's fix it properly — not with a fragile browser extension.
The Error
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 on the requested resource.
What CORS Actually Is
Same-Origin Policy stops a page on origin A from reading responses from origin B unless B opts in with Access-Control-Allow-Origin. For anything beyond a simple GET, the browser first sends a preflight OPTIONS request — which your server must also answer.
Fix in Express / Node.js
const cors = require("cors");
app.use(cors({
origin: "https://app.example.com", // not "*" if you send cookies
credentials: true,
methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
}));Fix in Laravel
Laravel ships with a CORS layer. Configure config/cors.php:
return [
'paths' => ['api/*'],
'allowed_methods' => ['*'],
'allowed_origins' => ['https://app.example.com'],
'allowed_headers' => ['*'],
'supports_credentials' => true,
];Fix in Next.js Route Handlers
export async function GET() {
return Response.json(data, {
headers: {
"Access-Control-Allow-Origin": "https://app.example.com",
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
},
});
}The Credentials Trap
If you send cookies/auth (credentials: 'include'), you CANNOT use Access-Control-Allow-Origin: *. You must echo the exact origin and set Access-Control-Allow-Credentials: true.
Still Blocked?
Open the Network tab and inspect the preflight OPTIONS request. If it does not return 200 with the allow headers, your server is not handling OPTIONS — that is your real bug.
