Logo
Back to Blog
Web Development August 8, 2026 7 min read

Why CORS Wildcard (*) Breaks Cookies and Credentials

O

OmniWebKit Team

Backend Engineering

Share:
Article Cover Image

Your API works. You add cookies for authentication and everything breaks. The console now says the wildcard cannot be used when credentials are included, which sounds like a bug in the browser. It is not — the spec forbids that combination on purpose.

The CORS Credentials Wildcard Error, in One Sentence

You cannot say "any site may read this" and "send the user cookies" at the same time.

These two headers are mutually exclusive:

Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

Browsers reject the pair outright. The response is discarded even though the server sent it and the status was 200.

The fix is to name the origin instead of using a star. Everything below is about doing that safely.

Check what your endpoint currently returns with our CORS tester — it flags this specific conflict.

Why the Access-Control-Allow-Credentials Wildcard Rule Exists

Because the combination would break every authenticated site on the internet.

Credentials mean the browser attaches the user cookies automatically. A wildcard means any page may read the reply.

Put them together and any site your user visits could call your API as that logged-in user and read the response. Their bank balance, their messages, their account settings.

So the browser refuses. It is one of the few CORS rules that protects users rather than merely inconveniencing developers.

Echoing the Origin Without Opening a Hole

Validate first, then echo. Never echo blindly.

const ALLOWED = new Set([
  'https://app.example.com',
  'https://admin.example.com',
]);

const origin = req.headers.origin;
if (ALLOWED.has(origin)) {
  res.setHeader('Access-Control-Allow-Origin', origin);
  res.setHeader('Access-Control-Allow-Credentials', 'true');
  res.setHeader('Vary', 'Origin');
}

Note the exact-match lookup. We have reviewed real code using startsWith, which happily accepts https://example.com.attacker.io because your domain appears as a substring.

Reflecting the origin with no check at all is functionally a wildcard that also allows credentials — the exact thing the rule exists to prevent.

The Vary Header That Stops Caches Leaking

A dynamic origin value needs Vary: Origin, or a cache will mix responses up.

Your CDN stores responses by URL. If the response contains a header that changes per requester, and you do not say so, one origin gets served the copy cached for another.

The failure looks random. It works for you, fails for a colleague, then swaps around after a deploy.

Vary: Origin

One line, and it removes an entire class of bug that is miserable to reproduce.

Getting withCredentials to Actually Work

Both sides have to opt in, and then the cookie itself has to be configured for cross-site use.

fetch('https://api.example.com/me', {
  credentials: 'include',
});

That is the client half. The server half is the allow-credentials header shown above.

Then there is the cookie, which is where people get stuck after fixing the headers:

Set-Cookie: session=abc; SameSite=None; Secure; HttpOnly

SameSite=None permits cross-site sending, and browsers require Secure alongside it. Secure means https only, with localhost as the one exception.

Credentials also cover HTTP auth headers and TLS client certificates, so this rule can bite even when you are not using cookies at all.

The Other Wildcards That Fail Quietly

The same restriction applies to three more headers, and their failure is silent.

  • Access-Control-Allow-Headers: *
  • Access-Control-Allow-Methods: *
  • Access-Control-Expose-Headers: *

All three accept a star in modern browsers — but only when credentials are off. With credentials enabled, the star is treated as a literal header named star, which matches nothing.

No error appears. Your request just fails as though the header were absent, which is why this one costs so much time.

The full behaviour of each is in our CORS headers reference.

Wrapping Up

The CORS credentials wildcard error is the browser enforcing a rule that protects your users. A star and cookies cannot coexist, and no configuration flag changes that.

Keep an explicit allowlist, compare origins with exact string equality, echo the matched value, and always send Vary: Origin.

Still stuck earlier in the chain? See the missing Access-Control-Allow-Origin fix, or read what CORS is protecting against for the wider picture.

Frequently Asked Questions

Why is the wildcard banned with credentials specifically?

+
Because a wildcard means every site, and credentials mean the browser attaches the user cookies automatically. Together they would let any page on the internet make authenticated requests as your logged-in user and read the answer.

Can I echo whatever origin arrives?

+
You can, and it is a wildcard with extra steps unless you check it first. Validate the incoming origin against an allowlist, then echo it. Reflecting blindly with credentials enabled is a serious vulnerability.

Why does my allowlist check pass for evil-example.com?

+
Almost certainly a startsWith or includes comparison. An origin of https://example.com.attacker.io contains your domain as a substring, so a loose check waves it through. Compare the full string exactly.

Do I need Vary: Origin when echoing?

+
Yes, always, and forgetting it is how caches leak data. Without it a CDN can store the response for one origin and serve it to another, complete with the header naming the wrong site. Add it whenever the value is dynamic.

Does Access-Control-Allow-Headers accept a wildcard?

+
It does in modern browsers, but only when credentials are off. With credentials enabled the star is read as a literal header named star, which matches nothing. The same trap applies to the methods and expose headers.

What counts as credentials besides cookies?

+
HTTP authentication headers and TLS client certificates too. Setting withCredentials or the fetch credentials option to include sends all of them, which is why the rule catches people who are not using cookies at all.

Why does my cookie still not arrive after fixing this?

+
The cookie attributes are probably wrong. A cross-site cookie needs SameSite set to None and the Secure flag, and Secure means it will not work over plain http except on localhost. That combination catches almost everyone once.

Can I use a wildcard subdomain like https://*.example.com?

+
No, the spec allows exactly one origin or a single star, nothing in between. Pattern matching has to happen in your own code: check the incoming origin against your rule, then echo the exact value.

Is echoing the origin slower than a static value?

+
The comparison costs nothing, but the caching impact is real. A dynamic value plus Vary: Origin means your CDN stores a separate copy per origin, which lowers the hit rate on a widely embedded API.

Should a public API use the wildcard?

+
For genuinely public data with no authentication, yes — it is simpler and caches better. The moment any response varies by user, switch to an allowlist. We tell clients to decide this per endpoint rather than per service.

Tags

#CORS#Security#Cookies#HTTP