🚀 Client-Side vs Server-Side CORS: Understanding the Real Difference
Source: Dev.to

If you’ve ever screamed at your monitor because of a mysterious CORS error…
Congratulations — you are officially a web developer. 🎉
CORS is like that strict bouncer outside the club who won’t let you in even though you swear you’re on the list.
What Is CORS, Really?
CORS stands for Cross‑Origin Resource Sharing — which sounds fancy, but really means:
“Hey browser, can this website talk to that other website? Or is it a stranger‑danger situation?”
The browser is extremely paranoid.
If domain A calls domain B, the browser goes:
“Hold up. Who sent you? Show me your passport, Aadhar card, PAN card, and two 2×2 photos.”
Client‑Side vs Server‑Side CORS: The Plot Twist
People keep saying “client‑side CORS” and “server‑side CORS,” but the truth is:
Only the server decides CORS. The client is just a confused teenager asking for permission.
Both sides play a role — so let’s decode them.
Client‑Side CORS (AKA: The Illusion of Control)
Developers often write:
fetch("/api", { mode: "cors" })
and expect the browser to say:
“Yes sir! CORS enabled! You’re a genius!”
But the browser actually enforces the rules:
mode: "cors"→ “Please let me in!”mode: "no-cors"→ “Fine, I’ll leave. I didn’t want to see the response anyway.”credentials: "include"→ “Here are my cookies, please don’t judge.”
⚠️ The client cannot enable CORS. It can only request it; the server still holds the key.
Server‑Side CORS (The Real Boss Here)
This is where the actual magic happens. If the server doesn’t approve your origin, the browser blocks the request.
Typical response headers:
Access-Control-Allow-Origin: https://myapp.com
Access-Control-Allow-Methods: GET, POST, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true
Only after receiving the proper headers does the browser allow the request.
Example (Express.js)
const cors = require('cors');
app.use(cors({
origin: "https://myapp.com",
credentials: true
}));
Example (Next.js API Route)
export default function handler(req, res) {
res.setHeader("Access-Control-Allow-Origin", "https://myapp.com");
res.setHeader("Access-Control-Allow-Credentials", "true");
// ...handle request
}
If you forget even one header, you’ll get a CORS error.
Simple vs Preflight Requests (AKA: Browser Drama)
Simple requests
These are “chill.” The browser allows:
GETorPOSTwith simple headers (e.g.,Accept,Content-Typelimited totext/plain,multipart/form-data,application/x-www-form-urlencoded).
Preflight requests
These happen when you use:
- Methods like
PUT,PATCH,DELETE - Custom headers
- A JSON body (i.e.,
Content-Type: application/json)
The browser sends an OPTIONS request first, asking the server for permission. If the server doesn’t respond correctly, the actual request is blocked.
Summary Table for Geniuses Who Scrolled Too Fast
| Thing | Client Side | Server Side |
|---|---|---|
| Who’s in control? | Lol. Not you. | The real boss. |
| Can it allow a request? | ❌ Nope | ✅ Absolutely |
| Can it trigger preflight? | ✅ Yes | ⚠️ Must respond properly |
| Is it real CORS? | ❌ No | ✅ Yes, 100 % legit |
Final Thoughts
CORS errors might feel like a personal attack from the universe, but the rule is simple:
- Client makes a request.
- Server decides whether to allow it (by sending the appropriate headers).
- Browser enforces the decision.
When each part does its job, CORS is painless. When something goes wrong… enjoy the next few hours of debugging. 😅