Skip to content
Putting technology to work.
Insights to guide decisions and action.

Search articles

Demystifying CORS errors and fixing them properly — Ending the outsourcing habit of "just allowing everything"

Table of contents · 6 items

When calling an API on a different domain via fetch from the frontend, the browser console turns red: "Access to fetch … has been blocked by CORS policy." When hosting frontend and backend separately in custom development, developers hit this wall almost without exception. Searching for help yields top results suggesting that "adding Access-Control-Allow-Origin: * fixes it." Pasting it does make the error disappear. But that may simply be the equivalent of setting a locked door to "let everyone in" just to stop it from blocking you.

Articles titled "Developers don't understand CORS" routinely trend on Hacker News, showing how widespread this state of "the error is gone, but the mechanism is misunderstood" truly is. When delivering APIs in custom development, leaving them set to "allow all for now" is out of the question. In this article, we explain the fundamentals of what CORS protects and map out how to turn risky shortcuts into secure configurations.

CORS is a mechanism to protect users, not servers

The primary source of confusion is the misconception that CORS is a security mechanism restricting access to servers. The reality is quite the opposite. CORS is a restriction enforced by the browser to protect users.

It is predicated on the browser's foundational rule: the same-origin policy. This prevents JavaScript running on one site (origin) from reading arbitrary resources from another origin—without it, simply visiting a malicious site could run code that reads private data from your online banking session open in another tab. CORS is the mechanism by which a server explicitly grants an exception to this strict rule, declaring that this specific origin is allowed to read responses.

In other words, the Access-Control-Allow-Origin header is a permission slip from the server informing the browser that requests from a specific origin are permitted to read the response. The error appears because the server has not granted permission to that origin. Eliminating this with * (allowing all origins) is equivalent to marking the permission slip as "anyone permitted." While the error disappears, it exposes everything you intended to protect.

Misunderstanding this derails API integration architecture from the ground up. This core concept is also intimately tied to how authentication credentials are handled. Looking at cookie and token management alongside the perspectives covered in our article on auditing JWT, cookie, and session designs provides a clear, multidimensional view of the relationship between CORS and authentication.

Why "allow all for now" is dangerous

Pasting Access-Control-Allow-Origin: * becomes exceptionally hazardous when dealing with APIs that handle credentials.

Under browser specifications, * (wildcard allow) and credentials support (credentials) cannot be used together. Consequently, when trying to permit all origins on an authenticated API, developers often resort to an even more dangerous workaround: echoing back the request origin directly into the allow header.

// やってはいけない例:来たオリジンを無検証でそのまま許可する
app.use((req, res, next) => {
  res.header("Access-Control-Allow-Origin", req.headers.origin); // 危険
  res.header("Access-Control-Allow-Credentials", "true");
  next();
});

This translates to "whatever origin requests access, allow that origin"—effectively permitting all origins while simultaneously allowing credentials to be transmitted. Malicious sites can now make authenticated requests and receive responses, completely neutralizing the protections CORS was designed to provide. Under the guise of "fixing the error," the safeguard protecting users has been dismantled.

Correct configurations to adopt in custom development

The proper approach is neither * nor origin echoing, but explicitly enumerating allowed origins via an allowlist.

Common approachConsequenceEvaluation in custom development
Allow-Origin: *Grants read access to all origins; credentials cannot be sentUnacceptable except for public, unauthenticated APIs
Reflecting the incoming origin without validationEffectively allows all origins while enabling credentialsDangerous; must never be included in production deliverables
Validating against an allowlist before returning headersAllows only registered originsThe baseline standard

Implementation consists of verifying whether the request origin is present in a predefined allowlist, returning that origin in the allow header only when a match is confirmed.

const allowedOrigins = ["https://app.example.com", "https://admin.example.com"];

app.use((req, res, next) => {
  const origin = req.headers.origin;
  if (allowedOrigins.includes(origin)) {
    res.header("Access-Control-Allow-Origin", origin);
    res.header("Access-Control-Allow-Credentials", "true");
  }
  next();
});

An operational best practice is managing the allowlist via environment variables across production, staging, and development rather than hardcoding values into application source. Furthermore, when using PUT, DELETE, or custom headers, the browser will issue a preflight OPTIONS request, requiring Access-Control-Allow-Methods and Access-Control-Allow-Headers to be returned accordingly. How misconfigurations at the API gateway layer create security loopholes also connects directly to cases covered in our article on how API Gateway misconfigurations cause authorization bypasses.

Real-world remediation in client projects

On a SaaS API where our company was engaged for a security review (keeping the company name confidential), the frontend and backend operated on different domains, and developers had resolved development CORS errors by simply echoing back the incoming origin. Because authentication relied on cookies and included Allow-Credentials: true, any website could query this API with active credentials. The team assumed CORS was properly configured, unaware that it was wide open.

We transitioned the setup to an allowlist model, configuring production, staging, and local origins through environment variables. We simply shifted from unconditionally permitting whatever arrived to permitting only verified origins. We also cleaned up preflight responses, paring down permissions for unneeded methods and headers. Visual functionality remained unchanged, but unauthorized, credentialed access from malicious sites was successfully blocked.

What proved most impactful in this project was sharing the understanding across the team that resolving a CORS error does not mean it was configured correctly. There are countless wrong ways to silence an error. Only after understanding what the mechanism protects can you judge whether a fix is sound. Understanding comes first; configuration follows.

Where to begin

The first step is checking how your APIs currently configure CORS. If Access-Control-Allow-Origin contains * or echoes incoming origins verbatim, it warrants immediate review. For credentialed APIs, switching to an explicit origin allowlist and managing those lists per environment restores security without disrupting functionality.

If you lack confidence in CORS configurations across separate domain setups, have live production systems running on wildcard permissions, or want to audit your API security posture, reach out via GleamHub's contact form. We will review your current CORS and authentication architecture to establish a secure setup without disrupting operations.

Sources

Share this articleXFacebook
Kakeru Suzuki

Fascinated by the possibilities of technology, has had a deep interest in programming and digital art since student days

Turn this article's theme into your company's next step

Concrete steps forward for your organization.

We organize your desired architecture, legacy systems, and operational requirements to formulate your next steps toward execution.

  • Desired architecture
  • Integration with existing environments
  • Operational requirements
Consult on development & operations initiatives

You can consult with us from the initial conceptual stage. Details from this article will be carried over to the inquiry form.

Receive the latest articles by email