remix-run/react-router · error · Error

`origin` header is not a valid URL. Aborting the action.

Error message

`origin` header is not a valid URL. Aborting the action.

What it means

Thrown by `throwIfPotentialCSRFAttack` when the `Origin` request header is a non-`null` string that cannot be parsed by `new URL(originHeader)`. The library aborts the action because it cannot safely classify the request as same-origin or cross-origin.

Source

Thrown at packages/react-router/lib/actions.ts:14

export function throwIfPotentialCSRFAttack(
  request: Request,
  allowedActionOrigins: string[] | undefined,
) {
  let originHeader = request.headers.get("origin");
  let originDomain: string | null = null;

  try {
    originDomain =
      typeof originHeader === "string" && originHeader !== "null"
        ? new URL(originHeader).host
        : originHeader;
  } catch {
    throw new Error(
      `\`origin\` header is not a valid URL. Aborting the action.`,
    );
  }
  let host = new URL(request.url).host;

  if (originDomain && originDomain !== host) {
    if (!isAllowedOrigin(originDomain, allowedActionOrigins)) {
      // This seems to be an CSRF attack. We should not proceed with the action.
      throw new Error(
        "The `request.url` host does not match `origin` header from a forwarded " +
          "action request. Aborting the action.",
      );
    }
  }
}

// Implementation of micromatch by Next.js https://github.com/vercel/next.js/blob/ea927b583d24f42e538001bf13370e38c91d17bf/packages/next/src/server/app-render/csrf-protection.ts#L6
function matchWildcardDomain(domain: string, pattern: string) {

View on GitHub (pinned to 1fd704a7da)

Solutions

  1. Inspect the raw `Origin` header the client is actually sending (browser devtools, proxy access logs).
  2. Fix the upstream proxy/client so Origin is either a well-formed absolute URL or omitted/`null`.
  3. If you control the client, send `Origin: https://your-app.example` or omit the header rather than send garbage.
  4. Reproduce with curl `-H 'Origin: https://valid.example'` to confirm the action succeeds once the header is well-formed.

Example fix

# before
curl -X POST https://app/api -H 'Origin: ///'

# after
curl -X POST https://app/api -H 'Origin: https://app.example'
Defensive patterns

Strategy: validation

Validate before calling

function isValidOriginHeader(h: string | null): boolean {
  if (h === null || h === 'null') return true;
  try { new URL(h); return true; } catch { return false; }
}
// in your proxy/edge, sanitize before forwarding:
if (!isValidOriginHeader(request.headers.get('origin'))) {
  return new Response('Bad Origin', { status: 400 });
}

Type guard

function isParsableOrigin(h: string | null | undefined): h is string {
  if (!h || h === 'null') return false;
  try { new URL(h); return true; } catch { return false; }
}

Try / catch

try {
  await action({ request });
} catch (e) {
  if (e instanceof Error && e.message.includes('`origin` header is not a valid URL')) {
    return new Response('Invalid Origin header', { status: 400 });
  }
  throw e;
}

Prevention

When it happens

Trigger: A mutation request (POST/PUT/PATCH/DELETE) reaches a React Router action with an `Origin` header value that is not a valid URL and not the literal string `null`. Examples: a malformed proxy header, `Origin: //`, or a custom client setting garbage in Origin.

Common situations: Misconfigured reverse proxy rewriting/stripping Origin; a CDN injecting a malformed Origin; a security scanner or curl invocation sending `Origin: foo`; non-browser clients that set Origin to a non-URL token.

Related errors


AI-assisted analysis of remix-run/react-router@1fd704a7da (2026-08-12). Data as JSON: /api/errors/6b3554626473d9ab. Report an issue: GitHub.