Safeguard
Application Security•

How do you prevent open redirect vulnerabilities in Node.js apps?

An open redirect sends users to any URL an attacker supplies. In Node.js, redirect only to allowlisted targets and never pass raw input to res.redirect.

Safeguard Security Team
4 min read

An open redirect happens when your app sends a browser to a URL taken from user input without checking it. In Node.js, avoid passing request parameters to res.redirect. Redirect to a fixed list of destinations or to relative paths you validate, so a link on your domain cannot send people elsewhere.

Why is it a vulnerability and not just a quirk?

MITRE's weakness entry CWE-601 covers URL redirection to an untrusted site. The harm is trust. A phishing link such as https://yourbank.example/login?next=https://evil.example starts on your real domain, passes email filters and fools careful users, then lands on the attacker's page.

The OWASP cheat sheet on unvalidated redirects and forwards shows the root pattern in several languages: read a URL from the request, then send it back in a Location header with no checks. It also notes the use in phishing.

Redirects also feed other flaws. OAuth flows that accept loose redirect URIs can leak authorization codes, and a redirect can bypass an allowlist that trusts your domain.

What does vulnerable Express code look like?

A constructed example of the pattern:

app.get('/login/done', (req, res) => {
  res.redirect(req.query.next);
});

Whatever is in next becomes the destination. It can be an absolute URL, a protocol-relative one starting with two slashes, or a trick like a backslash that some browsers treat as a slash.

Why do simple filters fail?

A check such as "starts with /" is not enough, since //evil.example begins with a slash and means a different host. A check that the string contains your domain fails with https://evil.example/?x=yourbank.example or with https://yourbank.example.evil.example. Blocklisting http misses other schemes and encodings.

Parsing is the safer route. Node's URL API exposes new URL(value, base) and the resulting origin. Compare parsed origins, not strings.

How should the fix look?

Best option: do not accept a destination at all. Store the return location server side, in the session, or accept a short key that maps to a known path.

const targets = { home: '/', billing: '/account/billing' };
app.get('/login/done', (req, res) => {
  const path = targets[req.query.next] || '/';
  res.redirect(path);
});

If you must accept a path, parse it against your own origin and verify the result stays on it:

function safePath(input) {
  try {
    const base = 'https://app.example.com';
    const u = new URL(input, base);
    return u.origin === base ? u.pathname + u.search : '/';
  } catch {
    return '/';
  }
}

This rejects absolute URLs to other hosts, protocol-relative forms and malformed input, falling back to a safe default. Keep the base value in configuration, not derived from the request's Host header, which the client controls.

What about redirects after login?

Post-login redirects are the most common source. Users hit a protected page, get sent to sign in, and are returned to where they were headed. Store that destination in the server-side session before the login redirect, not in a query string the browser can edit. If a query parameter is unavoidable, sign it or map it to a short key. Also check logout flows and password reset links, which often carry a return address and are skipped in reviews.

How to check or apply this

  1. Search the codebase for res.redirect, res.location, Location headers and framework helpers that redirect.
  2. Trace each argument back. If any part comes from a query, body, header or cookie, treat it as untrusted.
  3. Replace with a key lookup, or with the parsed same-origin check above.
  4. In OAuth and single sign-on code, register exact redirect URIs. Do not use wildcard or prefix matching.
  5. Add tests for these inputs: an absolute external URL, //host, a backslash form, mixed-case schemes, URL-encoded slashes, and an empty value.
  6. Use a framework middleware or shared helper so no route builds redirects by hand.
  7. Run static analysis rules for tainted redirect targets and review the findings.
  8. Where an external link is needed, show an interstitial page that states the destination and asks the user to confirm.

The OWASP Node.js security cheat sheet is a good wider checklist for the same codebase.

Quick check

Is checking that the URL starts with a slash enough?

No. A value starting with two slashes points to another host. Parse the value and compare the origin, or use a fixed lookup instead.

Does an open redirect on its own count as serious?

It is usually rated lower than code execution, but it enables phishing and can expose OAuth codes. Fixing it is cheap, so fix it.

Sources

Last reviewed: 18 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.