How do I fix an open redirect?

Direct answer

Don't redirect to a URL from user input. Accept a relative path and reject anything else, or map an identifier to a destination you control. Checking that the value starts with your domain doesn't work, `//evil.com` and `https://yoursite.com.evil.com` both pass naive checks.

Muhammad HasanUpdated

An open redirect is a URL on your domain that sends the visitor somewhere else, with the destination taken from a parameter.

https://yoursite.com/login?next=https://evil.com

It looks minor until you see how it's used. The link genuinely is your domain, which is what makes phishing work. The victim checks the domain, sees yours, and follows it. And in OAuth flows, an open redirect in a registered callback can be chained to leak tokens.

The naive fixes that fail

// All of these are bypassable
if (next.startsWith('/')) { }                    // '//evil.com' passes
if (next.startsWith('https://yoursite.com')) { } // 'https://yoursite.com.evil.com' passes
if (next.includes('yoursite.com')) { }           // 'https://evil.com/?x=yoursite.com' passes

//evil.com is a protocol-relative URL. It starts with a slash and it's a different site.

Relative paths only

If the destination is always somewhere on your site, enforce that:

function safeNext(next) {
  if (typeof next !== 'string') return '/';
  if (!next.startsWith('/')) return '/';
  if (next.startsWith('//')) return '/';    // protocol-relative
  if (next.startsWith('/\\')) return '/';   // backslash variant
  return next;
}

Or parse and compare the resolved origin, which handles encoding variants better than string checks:

function safeNext(next, origin) {
  try {
    const url = new URL(next, origin);
    return url.origin === origin ? url.pathname + url.search : '/';
  } catch {
    return '/';
  }
}

Better: don't accept URLs at all

Where the set of destinations is known, pass a key:

const DESTINATIONS = {
  dashboard: '/dashboard',
  settings: '/settings',
};
res.redirect(DESTINATIONS[req.query.to] ?? '/');

Nothing to validate, nothing to bypass.

If you must allow external destinations

Some products legitimately need this, link shorteners, outbound trackers. Use an allowlist of hosts, and show an interstitial page confirming where the user is going rather than redirecting silently.

Where to look

Login and logout next/return_to/redirect_uri parameters, OAuth callbacks, post-purchase redirects, and anywhere with url, target or dest in a query string.

Grep for res.redirect, redirect(, HttpResponseRedirect and equivalents and check where the argument comes from.

Go deeper

Fast remediation with Kolega

Related answers

See what your own repository returns

Connect a repo and run a scan. No credit card, no pipeline changes.

Get started for free