21.11.2025 • 4 min read

Understanding CORS: A Beginner’s Guide to Cross-Origin Requests

Cover Image

Introduction

When I first started working with APIs, I kept running into this mysterious error in my browser console:

Access to fetch at https://api.example.com/data from origin http://localhost:3000 has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.

At first, I thought my code was broken. I spent hours debugging my fetch requests, changing headers, even questioning my sanity.

Turns out, it wasn’t a bug in my code—it was CORS.

CORS, or Cross-Origin Resource Sharing, is a browser security mechanism that controls how web pages can request resources from a different domain. It prevents malicious websites from sneaking around and accessing your private APIs or data without permission.

Let’s demystify it.

What I Thought About CORS

At first, I thought CORS was just a “browser problem”—something I could ignore or patch with random headers I found online. Tutorials often suggested adding:

res.setHeader("Access-Control-Allow-Origin", "*"); 

And that would magically fix everything. But this approach is risky in production. It basically says: “Hey browser, allow anyone, anywhere, to access this API.” Not ideal for sensitive data.

I realized that to really understand CORS, I needed to know why browsers enforce it, not just how to bypass it.

My New Understanding of CORS

CORS exists because browsers follow the Same-Origin Policy (SOP). This policy restricts web pages from making requests to a different domain than the one that served the page.

  • Origin is defined by protocol + domain + port.
  • Example: http://localhost:3000 and http://example.com are different origins.

CORS allows servers to tell browsers: “It’s okay for this page from another origin to access my resources.”

Here are the main pieces I now understand:

  • Simple Requests: GET, POST, or HEAD requests with standard headers. Browser sends the request directly and checks the response headers for permission.

  • Preflight Requests: For non-simple requests (like PUT, DELETE, or custom headers), browsers send an OPTIONS request first to ask the server if the actual request is allowed.

  • CORS Headers:

    • Access-Control-Allow-Origin: Specifies which origin(s) can access the resource.
    • Access-Control-Allow-Methods: Which HTTP methods are allowed (GET, POST, etc.).
    • Access-Control-Allow-Headers: Which custom headers can be used.
    • Access-Control-Allow-Credentials: Whether cookies or authentication info can be sent.
  • Credentials & Cookies: Browsers block credentials by default. If your API requires cookies or auth headers, the server must explicitly allow them with Access-Control-Allow-Credentials: true and cannot use * for the origin.

Example Scenario

Suppose your frontend runs at http://localhost:3000 and your backend API is at http://api.example.com.

A simple CORS setup in Node.js/Express would look like:

const express = require('express');
const cors = require('cors');
const app = express();

app.use(cors({
  origin: 'http://localhost:3000',
  methods: ['GET', 'POST'],
  credentials: true
}));

app.get('/data', (req, res) => {
  res.json({ message: 'Hello from API!' });
});

app.listen(4000, () => console.log('API running on port 4000'));

Now, your frontend can safely make requests without running into CORS errors.

Why CORS Matters

Understanding CORS is more than just fixing errors. It’s about web security:

  • Prevents Cross-Site Request Forgery (CSRF) attacks.
  • Ensures data privacy across different origins.
  • Forces developers to think about who can access APIs. Ignoring it might work in development with *, but in production, proper CORS policies are crucial.

Resources That Helped Me

  • MDN Web Docs – CORS Clear explanation of headers, preflight requests, and real-world examples.

  • Web.dev – CORS Visual guides and diagrams that explain request flow between browser and server.

  • Express.js Documentation – CORS Middleware Quick way to implement secure and flexible CORS policies in Node.js.

Conclusion

CORS may seem annoying at first—blocking requests left and right—but it’s a security feature, not a bug. Once you understand origins, headers, and preflight requests, you can configure APIs safely and avoid random browser errors.

Think of CORS like a bouncer at a club: only letting trusted guests in, keeping your data secure, and preventing chaos.

Next steps: in upcoming posts, I’ll explore common CORS mistakes, debugging tips, and real-world scenarios in modern web applications.

CORS isn’t just a hurdle—it’s a fundamental part of how the web stays safe.