remix-run/react-router · error · Error

Invalid redirect location

Error message

Invalid redirect location

What it means

`normalizeRedirectLocation` rejected an absolute redirect URL because, after parsing, its protocol is in the `invalidProtocols` blocklist (`data:`, `javascript:`, `file:`, `blob:`, `about:`, `chrome:`, `content:`, `devtools:`, `filesystem:`, `chrome-untrusted:`). This blocks open-redirect/UXSS vectors through redirect responses.

Source

Thrown at packages/react-router/lib/router/router.ts:6920

  }
}

function normalizeRedirectLocation(
  location: string,
  currentUrl: URL,
  basename: string,
  historyInstance: History,
): string {
  if (isAbsoluteUrl(location)) {
    // Strip off the protocol+origin for same-origin + same-basename absolute redirects
    let normalizedLocation = location;
    let url = PROTOCOL_RELATIVE_URL_REGEX.test(normalizedLocation)
      ? new URL(
          normalizeProtocolRelativeUrl(normalizedLocation, currentUrl.protocol),
        )
      : new URL(normalizedLocation);
    if (hasInvalidProtocol(url.toString())) {
      throw new Error("Invalid redirect location");
    }
    let isSameBasename = stripBasename(url.pathname, basename) != null;
    if (url.origin === currentUrl.origin && isSameBasename) {
      return removeDoubleSlashes(url.pathname) + url.search + url.hash;
    }
  }

  try {
    let url = historyInstance.createURL(location);
    if (hasInvalidProtocol(url.toString())) {
      throw new Error("Invalid redirect location");
    }
  } catch {}

  return location;
}

// Utility method for creating the Request instances for loaders/actions during

View on GitHub (pinned to 1fd704a7da)

Solutions

  1. Validate redirect destinations before returning them; only allow `http:`/`https:` (and same-origin relative paths).
  2. Sanitize or strip user-controlled protocol prefixes before passing to `redirect()`.
  3. If you genuinely need a non-http redirect, return a regular page that links to it instead of an HTTP redirect.

Example fix

// before
return redirect(userInputUrl); // userInputUrl may be 'javascript:...'

// after
const u = new URL(userInputUrl, request.url);
if (u.protocol !== 'http:' && u.protocol !== 'https:') {
  throw new Response('Bad redirect target', { status: 400 });
}
return redirect(u.toString());
Defensive patterns

Strategy: validation

Validate before calling

const ALLOWED = new Set(['http:', 'https:']);
function safeRedirectTarget(target: string, base: string): string {
  const u = new URL(target, base);
  if (!ALLOWED.has(u.protocol)) throw new Error(`Blocked redirect protocol: ${u.protocol}`);
  return u.toString();
}

Type guard

function isSafeAbsoluteRedirect(target: string): boolean {
  try {
    const u = new URL(target);
    return u.protocol === 'http:' || u.protocol === 'https:';
  } catch { return false; }
}

Prevention

When it happens

Trigger: A loader/action returns a `redirect(...)` (or a Response with `Location`) whose value is an absolute URL with one of the blocked protocols, e.g. `redirect('javascript:alert(1)')` or `redirect('data:text/html,...')`.

Common situations: User-supplied input echoed into a redirect target without validation; a CMS/database storing a `javascript:` URL that ends up as a redirect destination; deliberate or accidental open-redirect-style payload.

Related errors


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