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 duringView on GitHub (pinned to 1fd704a7da)
Solutions
- Validate redirect destinations before returning them; only allow `http:`/`https:` (and same-origin relative paths).
- Sanitize or strip user-controlled protocol prefixes before passing to `redirect()`.
- 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
- Never redirect to user input without protocol allowlisting.
- Build redirect URLs with `new URL(target, request.url)`.
- Treat any non-http(s) redirect as a bug.
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
- No handlers were found for the request: ${url.pathname}${url
- Prerender (data): Received a ${response.status} status code
- Cannot write to a destroyed or ended writable stream
- `origin` header is not a valid URL. Aborting the action.
- The `request.url` host does not match `origin` header from a
AI-assisted analysis of remix-run/react-router@1fd704a7da (2026-08-12).
Data as JSON: /api/errors/8257a93da2dbf452.
Report an issue: GitHub.