Authentication · beginner
CSRF origin check failed: compare browser origin and server policy
A state-changing HTTP request is rejected because the server's CSRF origin check compared the request's browser-supplied Origin (and/or Referer) against its allow-list and found a mismatch, or because a required token was missing or unreadable. The guide explains how the comparison is performed, how to distinguish an origin/referer rejection from a token rejection, and how to verify the server's policy without weakening defenses.
The symptoms
- •The browser DevTools Network panel shows a 4xx response (commonly 403 Forbidden, sometimes 400) on POST, PUT, PATCH, or DELETE; the response body or log carries a phrase such as "CSRF origin check failed", "Origin header does not match", "Invalid Referer", or "csrf_token missing or incorrect".
- •GET requests succeed while the same action issued as POST/PUT/PATCH/DELETE fails, which is the typical shape of a CSRF/same-origin policy enforcement.
- •The failure is reproducible only when the request originates from a specific host, scheme, or port, but works from another host the user trusts (for example, the deployed app vs. a staging alias).
- •The application's anti-forgery cookie is present in the browser, but the server still rejects the submission, indicating the cookie alone is not sufficient under the configured policy.
- •Embedded contexts (iframe, webview, mobile WebView, or server-to-server proxy) fail while top-level navigation in the same browser succeeds, suggesting an Origin/Referer stripping or a cross-frame boundary issue.
Likely causes
- •The server compares the request's Origin header against an allow-list of trusted hosts and the browser is sending an Origin that is not on the list (different subdomain, scheme, or port).
- •The Referer header is used instead of, or in addition to, Origin, and the browser is suppressing Referer due to a Referrer-Policy such as strict-origin or no-referrer, so the server sees an empty or unexpected value.
- •The state-changing endpoint requires both an anti-forgery token (double-submit cookie, synchronizer token, or framework token) AND an Origin/Referer match, and one of the two checks is failing even though the other is fine.
- •The cookie carrying the token was issued with a different Site or Path scope (for example, set on a parent domain or with SameSite=Lax/None) so the browser does not present it on the cross-site or sub-path request that triggers the check.
- •A reverse proxy, CDN, or load balancer is rewriting, stripping, or normalizing the Origin or Referer header before it reaches the application, causing the comparison value to differ from what the browser sent.
- •The deployed site is served on multiple aliases (apex, www, staging, preview) but the policy allow-list only contains one of them, so legitimate requests from the other alias are rejected.
First ten minutes
- 01Open the failing request in the browser DevTools Network panel and record the exact status code, response body excerpt, and request method; confirm the verb is a state-changing method (POST/PUT/PATCH/DELETE) and not a GET.
- 02In the same Network entry, read the Request Headers for Origin and Referer; write down the exact values byte-for-byte including scheme, host, and port, and note whether either header is absent.
- 03Open Application (Storage) → Cookies and confirm whether the anti-forgery cookie exists for the request's domain, and record its Name, Path, SameSite, Secure, and HttpOnly attributes.
- 04Compare the recorded Origin against the application's published allowed origins list (configuration file, environment variable, or framework setting) to determine whether the failure is an origin mismatch or a separate token issue.
- 05Capture the same state-changing request from a second host the user trusts (for example, the canonical app URL vs. a staging alias) to localize whether the rejection is host-specific or universal.
- 06Do not edit cookies, disable protections, or change SameSite attributes during triage; gather evidence first and only adjust configuration once the comparison values are documented.
Evidence to collect
- •The exact HTTP status code and response body line that mentions the CSRF or origin-check failure, with timestamp and request ID from the server access log.
- •The Origin header value as sent by the browser, and the Referer header value (or its documented absence) for the failing request.
- •The application's configured CSRF or allowed-origins policy: the allow-list values, the header(s) being compared (Origin vs. Referer vs. both), and whether token presence is also required.
- •The anti-forgery cookie attributes (Name, Domain, Path, SameSite, Secure, HttpOnly, Expiry) and whether the cookie was presented on the failing request per the Network → Cookies panel.
- •The request path and the cookie Path scope, to determine whether a path mismatch is preventing the token from being attached.
- •Reverse proxy, CDN, or WAF configuration that touches Origin/Referer (header rewriting, forwarding, or normalization rules), and the header values as observed at the origin server versus at the edge.
Where to look
- •Browser boundary: DevTools Network panel for the failing request's Request Headers (Origin, Referer) and Response Headers; Application → Cookies for the anti-forgery cookie attributes.
- •Server boundary: the application framework's CSRF or security middleware configuration (settings file, environment variable, or trusted-hosts list) and the code path that performs the origin comparison.
- •Edge boundary: the reverse proxy or CDN configuration that may rewrite, strip, or forward Origin and Referer headers before the application sees them.
- •Cookie boundary: the response that set the anti-forgery cookie, in particular Set-Cookie attributes (Domain, Path, SameSite, Secure) and whether the cookie is scoped to the request's host.
- •Embedded boundary: iframes, WebViews, or mobile shells where Referrer-Policy may strip the Referer and where the parent document's origin differs from the child's effective origin.
Diagnostic steps
- 01Confirm the failing verb: a GET that fails with this message is unusual and may indicate the framework misclassified the request; a POST/PUT/PATCH/DELETE is the expected shape.
- 02Compare the browser-sent Origin against the server's allow-list; if the Origin is missing, treat the request as cross-origin and check the Referrer-Policy and the framework's "no Origin" behavior separately.
- 03If the Origin matches the allow-list, examine the anti-forgery token path: confirm the token cookie is present in the Network → Cookies panel, and confirm the request body or header carries the matching token value (form field, X-CSRF-Token header, or framework equivalent).
- 04If the Origin does not match, determine whether the mismatch is legitimate (cross-site request) or configuration drift (alias not on the allow-list, scheme/port mismatch, trailing slash, case difference in hostname).
- 05Inspect the edge: with a header inspection tool or by comparing edge vs. origin logs, verify that Origin and Referer are not being rewritten, downgraded, or stripped between the browser and the application.
- 06Reproduce the same request from a host that is on the allow-list; success there and failure elsewhere confirms a host-specific origin comparison rather than a token or cookie issue.
- 07For embedded contexts, document the parent's origin, the child's effective origin, and the Referrer-Policy in effect, and decide whether the embedded context is the intended deployment shape.
Common mistakes
- •Disabling the CSRF check or setting it to "ignore origin" as a quick fix, which removes a defense-in-depth control; the correct response is to add the legitimate origin to the allow-list, not to bypass the comparison.
- •Loosening the cookie's SameSite attribute (for example, changing SameSite=Lax to None) to make the token "stick" across contexts, which weakens a separate defense and can introduce its own regressions.
- •Adding a wildcard or "any subdomain" rule to the origin allow-list to silence failures, which expands trust beyond what is needed and may allow a compromised sibling subdomain to issue cross-site requests.
- •Confusing the CSRF check with CORS: CORS preflight controls cross-origin reads, while the CSRF origin check gates state-changing writes; the fix is usually on the server's allow-list, not on Access-Control-Allow-Origin.
- •Trusting only the Referer header for the comparison; modern browsers suppress Referer under strict referrer policies, so a Referer-only check will reject legitimate same-origin requests.
- •Rewriting or stripping the Origin header at the proxy to "normalize" it, which breaks the very signal the server uses to detect cross-site requests.
Safe fixes
- •If the failing Origin is a legitimate alias of the application (for example, www vs. apex, or a documented staging host), add that exact scheme+host+port value to the CSRF allowed-origins configuration; verify with a follow-up request that the same action now returns a 2xx.
- •If the Origin is missing because of a Referrer-Policy in an embedded context, prefer switching the comparison to the Origin header (which is reliably sent on cross-origin POSTs) over relaxing the Referrer-Policy, and document the change in the security configuration notes.
- •If a reverse proxy is rewriting Origin, set the proxy to forward Origin unchanged (or to set it explicitly to a known value derived from the Host header) and re-test; the comparison value at the application must equal the value the browser sent.
- •If the anti-forgery cookie is not being attached because of a Path or Domain scope mismatch, set the cookie on the correct path and domain so it is presented on the state-changing endpoint, and re-verify the cookie appears in the Network → Cookies panel for the failing request.
- •If a token is required but not being sent, ensure the form or client includes the token in the expected location (hidden form field, request header, or framework token field) and that the token is read from the cookie set by the same response that issued the page.
- •After any configuration change, re-run a same-origin state-changing request and a cross-origin state-changing request from a host not on the allow-list, and confirm that the legitimate request succeeds and the cross-origin request still fails.
Prove the fix
- 01A POST/PUT/PATCH/DELETE from a host that is on the allowed-origins list returns a 2xx, with the response body no longer containing the origin-check failure message and the server access log showing the request as accepted.
- 02A POST/PUT/PATCH/DELETE from a host that is not on the allowed-origins list is still rejected with the same 4xx and message, demonstrating that the fix did not weaken the cross-site rejection.
- 03In the Network panel for the now-successful request, the Origin header value matches exactly one of the entries in the configured allow-list (scheme, host, and port), and the anti-forgery cookie plus token are both present on the request.
- 04A subsequent regression run from the previously failing alias (for example, the staging host) also returns a 2xx, confirming the configuration change covers all legitimate hosts and not just the one used during the initial test.
Prevention and next steps
- •Maintain a single, reviewed list of allowed origins per environment (production, staging, preview), keyed on exact scheme+host+port values, and reference it from the CSRF configuration rather than scattering allow-list entries across code paths.
- •Prefer Origin over Referer as the comparison signal, and treat an absent Origin in a non-browser client as an explicit policy decision (reject, or accept only on documented service-to-service endpoints) rather than a permissive default.
- •Verify that anti-forgery cookies are set with a Domain and Path that matches the endpoints they protect, and with SameSite and Secure attributes aligned to the deployment shape, so the token is reliably presented on state-changing requests.
- •Configure reverse proxies and CDNs to forward Origin and Referer unchanged unless there is a documented, security-reviewed reason to rewrite them, and audit that configuration after any edge changes.
- •Add an automated check that exercises a representative state-changing endpoint from each legitimate host in the allow-list and from at least one off-list host, so a future configuration drift is caught before users see the failure.
Safe commands and checks
browser_devtools_network_panel: select the failing POST/PUT/PATCH/DELETE and record the Request Headers line for Origin, the line for Referer (or its absence), the Request Cookies entry for the anti-forgery cookie, and the Response status code and body excerpt that contains the CSRF or origin-check message. browser_devtools_application_cookies: in Application → Cookies for the request's domain, record the anti-forgery cookie's Name, Domain, Path, SameSite, Secure, HttpOnly, and Expires attributes, and confirm the cookie is present for the failing endpoint. http_observability: from a documented allowed host, re-issue the same state-changing request and confirm a 2xx response with no origin-check message in the body; from an off-list host, confirm the same request still returns the documented 4xx with the origin-check message.