{"record":{"id":"6b3554626473d9ab","repo":"remix-run/react-router","slug":"origin-header-is-not-a-valid-url-aborting-the-a","errorCode":null,"errorMessage":"`origin` header is not a valid URL. Aborting the action.","messagePattern":"`origin` header is not a valid URL\\. Aborting the action\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/react-router/lib/actions.ts","lineNumber":17,"sourceCode":"export function throwIfPotentialCSRFAttack(\n  request: Request,\n  allowedActionOrigins: string[] | undefined,\n) {\n  let originHeader = request.headers.get(\"origin\");\n  let originDomain: string | null = null;\n  let originUrl: URL | null = null;\n\n  try {\n    if (typeof originHeader === \"string\" && originHeader !== \"null\") {\n      originUrl = new URL(originHeader);\n      originDomain = originUrl.host;\n    } else {\n      originDomain = originHeader;\n    }\n  } catch {\n    throw new Error(\n      `\\`origin\\` header is not a valid URL. Aborting the action.`,\n    );\n  }\n  let requestUrl = new URL(request.url);\n  let originMatchesRequest = originUrl\n    ? originUrl.origin === requestUrl.origin\n    : originDomain === requestUrl.host;\n\n  if (originDomain && !originMatchesRequest) {\n    if (!isAllowedOrigin(originDomain, allowedActionOrigins)) {\n      // This seems to be an CSRF attack. We should not proceed with the action.\n      throw new Error(\n        \"The `request.url` origin does not match `origin` header from a forwarded \" +\n          \"action request. Aborting the action.\",\n      );\n    }\n  }\n}","sourceCodeStart":1,"sourceCodeEnd":35,"githubUrl":"https://github.com/remix-run/react-router/blob/c091832969928593bf9f7d56d1b371bd0f6d5412/packages/react-router/lib/actions.ts#L1-L35","documentation":"validateActionOrigin() guards forwarded data-mode actions against CSRF by parsing the request's Origin header. The literal string 'null' is tolerated, but any other value must parse via new URL(); when parsing throws, the action is aborted with this error rather than proceeding with an unverifiable origin. The header is attacker-controllable input, so the library fails closed.","triggerScenarios":"A request reaches the router with an Origin header that is not a valid absolute URL and not the literal 'null' — e.g. 'example.com' (no scheme), '[::1]:3000' (bare host:port), or garbage injected by a proxy directive like proxy_set_header Origin $host.","commonSituations":"Reverse proxies rewriting Origin to a scheme-less host; custom fetch wrappers or curl invocations setting Origin manually to a bare hostname; API gateways appending values to the header.","solutions":["Fix the proxy to pass the original Origin through unchanged (or clear it) instead of rewriting it to a scheme-less value","If you set Origin manually in client code, send a fully-qualified URL like https://example.com or omit the header","Verify with curl -H 'Origin: https://example.com' that requests now pass the check"],"exampleFix":"# before (nginx — sets Origin to a bare host, not a valid URL)\nproxy_set_header Origin $host;\n\n# after\nproxy_set_header Origin $scheme://$host;","handlingStrategy":"type-guard","validationCode":"// validate before forwarding action requests (proxy/gateway side)\nfunction isValidOriginHeader(origin: string | null): boolean {\n  if (origin === null || origin === \"null\") return true;\n  try {\n    new URL(origin);\n    return true;\n  } catch {\n    return false;\n  }\n}\nif (!isValidOriginHeader(req.headers.get(\"origin\"))) {\n  return new Response(\"Bad Request\", { status: 400 });\n}","typeGuard":"function isValidOriginHeader(origin: string | null): boolean {\n  if (origin === null || origin === \"null\") return true;\n  try {\n    new URL(origin);\n    return true;\n  } catch {\n    return false;\n  }\n}","tryCatchPattern":null,"preventionTips":["Never rewrite Origin to a scheme-less value in proxy config — pass it through or set $scheme://$host","Always send fully-qualified origins (https://example.com) from custom clients","Add an integration test that posts an action with a valid Origin header"],"tags":["csrf","origin-header","actions","security","http","proxy"],"backgroundTag":"invalid-origin-header","analyzedSha":"c091832969928593bf9f7d56d1b371bd0f6d5412","analyzedAt":"2026-08-21T18:46:20.427Z","contentChangedAt":"2026-08-21T18:46:20.427Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}