Profile    Mohammed Shiroz Status   Loading  
Logo
Share This
Back to blog
Filter by:
Tags
//Article title

Me vs CORS: A Love Story in Five Bad Ideas and One Good One

About Post

Every developer has a relationship with CORS. It starts the same way for all of us.

You build a lovely frontend on localhost:5173. You build a lovely API on localhost:8000. You introduce them. You open the console, and there it is, in red:

Access to fetch at 'http://localhost:8000/api/me' from origin 'http://localhost:5173' has been blocked by CORS policy.

Postman says the API works. curl says the API works. Only the browser has a problem. And so begins a love story in five bad ideas and one good one.

Stage one: denial

❌ "It works in Postman, so the browser is wrong."

Postman isn't a browser. CORS is a rule that browsers enforce to protect their users: a page from one origin can't read responses from another origin unless that other origin says it's allowed. Postman and curl don't run untrusted pages from random websites, so they don't care. The browser is doing its job. Annoyingly well.

Stage two: the wildcard

❌ "Fine. Allow everything."

header('Access-Control-Allow-Origin: *');

For a while, it works. Then you add login with cookies, and the browser refuses again. The wildcard isn't allowed when the request includes credentials. The spec does this on purpose: "any website may read this user's logged-in responses" is exactly the thing CORS exists to prevent.

Stage three: the clever reflection

❌ "I'll just echo back whatever Origin the request sends."

Now credentials work, and you've built the wildcard again, except worse: every website on the internet is now allowed to make logged-in requests to your API and read the answers. It's the CORS equivalent of losing your house key and fixing it by removing the door.

Stage four: the browser flag

❌ "I'll launch Chrome with web security disabled."

Congratulations, it works on your machine. On exactly one machine. Your users won't be launching their browser with a scary flag, and you'll now be browsing the rest of the internet with a key safety feature turned off. Close that window.

Stage five: bargaining with no-cors

❌ "The error message literally suggests mode: 'no-cors'."

It does. And the request goes through! You just can't read the response. It comes back "opaque": no body, no status you can use. You've traded a red error for a silent nothing, which is arguably worse.

Plot twist: it wasn't CORS at all

Here's the moment that saves hours. Open the Network tab and look at the actual response. Very often, the request failed with a 500, a 404 or a redirect to a login page, and that error response came back without CORS headers. The browser then reports the missing headers, not the real problem.

So the console says "CORS". The truth is "your controller threw an exception". Check the status code and your server logs before touching any CORS setting.

The real fix: say exactly who you trust

✅ Allow your own frontend's origin, by name. In Laravel 11 and 12, CORS handling is built in, but config/cors.php isn't published by default. Publish it with php artisan config:publish cors, then:

return [
    'paths' => ['api/*', 'sanctum/csrf-cookie'],
    'allowed_methods' => ['*'],
    'allowed_origins' => [env('FRONTEND_URL', 'http://localhost:5173')],
    'allowed_headers' => ['*'],
    'supports_credentials' => true, // only if you use cookies
    'max_age' => 0,
];

✅ Keep the origin exact. Scheme, host and port all count. https://app.example.com and https://www.app.example.com are different origins, and a trailing slash in the config won't match.

✅ Understand the preflight. For requests with JSON bodies, custom headers like Authorization, or methods like PUT and DELETE, the browser first sends an OPTIONS request to ask permission. If something (a route, a proxy, an auth middleware) rejects that OPTIONS call, the real request never happens.

✅ Or avoid cross-origin entirely. Serve the API and the frontend from the same origin, or proxy /api through your dev server. No cross-origin request, no CORS.

Remember: CORS doesn't protect your server, it protects your users' browsers. Your API still needs authentication. CORS just decides which websites are allowed to read the answers.

Happily ever after

Once it clicks, CORS stops being an enemy. It's a bouncer with a guest list, and the fix is always the same: put the right name on the list, and make sure the door (the preflight) isn't locked.

Which stage did you stay in the longest? I'll admit I spent more time in stage two than I'd like.

Comments (0)
Leave your review

Thanks for your valuable comments. Your comments has been updated and appreciate your getting in touch...

01. About Shiroz

Mohammed Shiroz

Hi, I'm Mohammed Shiroz, a software engineer and AI enthusiast from Sri Lanka who turns ideas into intelligent, real-world solutions. With over 9 years of hands-on experience, I currently lead real estate ERP development at Kate Group, a...

03.My Projects

04. Categories

Ready To order Your Project ?

Get in Touch
Close