{"record":{"id":"8257a93da2dbf452","repo":"remix-run/react-router","slug":"invalid-redirect-location","errorCode":null,"errorMessage":"Invalid redirect location","messagePattern":"Invalid redirect location","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/react-router/lib/router/router.ts","lineNumber":6920,"sourceCode":"  }\n}\n\nfunction normalizeRedirectLocation(\n  location: string,\n  currentUrl: URL,\n  basename: string,\n  historyInstance: History,\n): string {\n  if (isAbsoluteUrl(location)) {\n    // Strip off the protocol+origin for same-origin + same-basename absolute redirects\n    let normalizedLocation = location;\n    let url = PROTOCOL_RELATIVE_URL_REGEX.test(normalizedLocation)\n      ? new URL(\n          normalizeProtocolRelativeUrl(normalizedLocation, currentUrl.protocol),\n        )\n      : new URL(normalizedLocation);\n    if (hasInvalidProtocol(url.toString())) {\n      throw new Error(\"Invalid redirect location\");\n    }\n    let isSameBasename = stripBasename(url.pathname, basename) != null;\n    if (url.origin === currentUrl.origin && isSameBasename) {\n      return removeDoubleSlashes(url.pathname) + url.search + url.hash;\n    }\n  }\n\n  try {\n    let url = historyInstance.createURL(location);\n    if (hasInvalidProtocol(url.toString())) {\n      throw new Error(\"Invalid redirect location\");\n    }\n  } catch {}\n\n  return location;\n}\n\n// Utility method for creating the Request instances for loaders/actions during","sourceCodeStart":6902,"sourceCodeEnd":6938,"githubUrl":"https://github.com/remix-run/react-router/blob/1fd704a7dabcbe3ae09d7387b460e6acaba30ec1/packages/react-router/lib/router/router.ts#L6902-L6938","documentation":"`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.","triggerScenarios":"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,...')`.","commonSituations":"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.","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."],"exampleFix":"// before\nreturn redirect(userInputUrl); // userInputUrl may be 'javascript:...'\n\n// after\nconst u = new URL(userInputUrl, request.url);\nif (u.protocol !== 'http:' && u.protocol !== 'https:') {\n  throw new Response('Bad redirect target', { status: 400 });\n}\nreturn redirect(u.toString());","handlingStrategy":"validation","validationCode":"const ALLOWED = new Set(['http:', 'https:']);\nfunction safeRedirectTarget(target: string, base: string): string {\n  const u = new URL(target, base);\n  if (!ALLOWED.has(u.protocol)) throw new Error(`Blocked redirect protocol: ${u.protocol}`);\n  return u.toString();\n}","typeGuard":"function isSafeAbsoluteRedirect(target: string): boolean {\n  try {\n    const u = new URL(target);\n    return u.protocol === 'http:' || u.protocol === 'https:';\n  } catch { return false; }\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["redirect","security","open-redirect","ssr"],"backgroundTag":null,"analyzedSha":"1fd704a7dabcbe3ae09d7387b460e6acaba30ec1","analyzedAt":"2026-08-12T13:54:57.804Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}