Skip to content

Troubleshooting

wiki edited this page Sep 4, 2026 · 1 revision

Troubleshooting

The browser says "CORS error" and I cannot tell why

Open the network tab and look at the response headers, not the console message. The console text is deliberately vague; the headers tell you which step failed.

  • No Access-Control-Allow-Origin at all → the origin is not in the allowlist, or the request carried no Origin header.
  • Access-Control-Allow-Origin present but the request still fails → the failure is a credentials or headers mismatch, not an origin one.

My origin is in the list and it still does not match

Origins are matched exactly, scheme and port included.

Configured Request Origin Matches
app.example.com https://app.example.com no — no scheme configured
https://app.example.com/ https://app.example.com no — trailing slash
https://app.example.com https://app.example.com:8443 no — different port
http://localhost:3000 http://localhost:3000 yes

Note that http://localhost:3000 and http://127.0.0.1:3000 are different origins. Development setups usually need both.

The application will not start

cors: origin "app.example.com" is not a valid origin: missing scheme
cors: AllowCredentials is incompatible with a "*" allowlist

The policy is validated in OnInitialize and a contradictory one aborts startup, on purpose — both mistakes otherwise produce a policy that looks configured and allows nothing.

For the second: browsers require a concrete origin on a credentialed response and discard "*". List the origins.

Everything is refused and there is no error

Check the startup log:

CORS is enabled with no allowed origins; every cross-origin request will be refused

The default allowlist is empty. Configure WithAllowedOrigins.

Credentials are not being sent

Both sides have to agree:

cors.WithAllowCredentials(true)
fetch(url, { credentials: "include" })

And the allowlist cannot be "*".

My client cannot read a response header

Only CORS-safelisted response headers are readable cross-origin unless you expose them:

cors.WithExposedHeaders(
	"X-RateLimit-Limit", "X-RateLimit-Remaining", "X-RateLimit-Reset", "Retry-After",
	"X-Request-Id", // yours
)

Remember that WithExposedHeaders replaces the defaults — if rate-limit headers stopped being readable after you added one of your own, this is why.

The preflight advertises the wrong methods

By default the advertised methods come from the router's own Allow set for that path, so they match the routes that exist. If they look wrong:

  • Check the path actually has the routes you expect (app.Routers()["default"].PrintRootsTree()).
  • Check you have not set WithAllowedMethods, which overrides the derived list with a static one that can drift.

If the router answers a 404 for the preflight path, the middleware falls back to advertising the common methods rather than an empty list.

A disallowed origin gets a 200 instead of a 403

That is intended. The request proceeds and the browser withholds the response from script — see How It WorksRefusal is silent. A 403 would refuse non-browser clients, which are not subject to CORS at all.

If you want to block by origin, that is an authorization rule, not CORS.

A 401 or 500 shows up as an opaque network error

That is what CORS headers on error responses prevent, and this extension adds them at PriorityCORS — outside auth and rate limiting — precisely so they are present whatever the inner layers decide.

If you are still seeing it, check that the CORS extension is registered and that the response actually carries an Origin-matched Access-Control-Allow-Origin. A middleware registered outside PriorityCORS that short-circuits the request would bypass it.

Cached responses go to the wrong origin

The middleware sets Vary: Origin on every response that carried an Origin, including refusals. If a CDN or proxy in front of the application strips or ignores Vary, it will cross-serve responses no matter what the application sends — check the proxy configuration.