What Is an Open Redirect? Risks and How to Prevent It
An open redirect lets attackers use your domain to send users to any URL. How open redirect vulnerabilities work, why they matter, and how to fix them.
An open redirect is a vulnerability where a web application takes a destination URL from user input and redirects the browser to it without checking where it points. An attacker can craft a link that starts on your trusted domain but lands the victim on any site they choose. On its own it looks minor, since nothing on your server is compromised, but open redirects are a reliable building block for phishing and, in authentication flows, for stealing tokens.
How redirects end up open
Redirect parameters exist for good reasons. After a user logs in, you want to send them back to the page they were trying to reach. After a logout, a payment, or an email confirmation, the app needs to know where to go next. The common implementation looks like this:
https://example.com/login?next=/account/settings
On the server, after a successful login:
res.redirect(req.query.next);
That works for /account/settings. It also works for:
https://example.com/login?next=https://examp1e-login.net/
The link is hosted on example.com, it may even pass through a real login page, and then the browser is sent to an attacker-controlled lookalike. The server has turned itself into a trampoline for someone else’s site.
Why it matters
Phishing credibility. People are trained to check the domain before clicking. A link that genuinely begins with your domain passes that check, and email or chat filters that rely on domain reputation may let it through. The victim sees a familiar address, clicks, and ends up on a page designed to harvest their password.
Token theft in OAuth and SSO. This is where open redirects become serious. In OAuth and OpenID Connect, an authorization server sends the user back to a registered redirect_uri with a code or token attached. If the client application has an open redirect anywhere on an allowed path, an attacker can sometimes chain it: the authorization server redirects to the legitimate domain, which then forwards the browser, along with the code or token in the URL, to the attacker. Strict redirect URI matching and PKCE exist largely to close this gap.
Chaining with other bugs. Some server-side features fetch URLs and follow redirects. An open redirect on a trusted host can help an attacker get past an allow-list check and reach internal addresses, a pattern related to server-side request forgery.
Script execution. If the redirect is implemented in client-side JavaScript, for example location.href = params.get("next"), an attacker may be able to supply a javascript: URL. That turns an open redirect into cross-site scripting.
Tricks attackers use to bypass weak checks
Developers often add a quick check, and attackers often get around it. Validating URLs with string operations is the root problem.
- Prefix checks.
next.startsWith("https://example.com")acceptshttps://example.com.attacker.netandhttps://[email protected], where everything before@is treated as user info. - Protocol-relative URLs.
//attacker.nethas no scheme, so it may slip past a check that looks forhttp, but the browser treats it as an absolute URL on the current scheme. - Backslashes. Browsers normalize
/\attacker.netand\\attacker.netin ways that can resolve to another host. - Encoding. Percent-encoded characters, mixed case, and embedded whitespace or control characters can defeat naive pattern matching.
- “Contains” checks.
next.includes("example.com")acceptshttps://attacker.net/?example.com.
How to prevent open redirects
Do not redirect to arbitrary URLs at all. The safest design takes no URL from the user. Store the intended destination server-side in the session before sending the user to log in, or pass an opaque key that maps to a fixed list of destinations:
/login?next=settings → lookup: { settings: "/account/settings" }
Allow only relative paths, and parse them properly. If you need arbitrary in-app paths, require that the value is a path on your own origin. Parse it rather than pattern-matching it. In JavaScript, the URL API does the heavy lifting:
function safeRedirectTarget(input, fallback = "/") {
try {
const base = "https://example.com";
const url = new URL(input, base);
if (url.origin !== base) return fallback;
return url.pathname + url.search + url.hash;
} catch {
return fallback;
}
}
Resolving the input against your own origin and comparing the parsed origin handles protocol-relative URLs, user-info tricks, and backslashes the same way the browser will. Returning only the path portion means the redirect can never leave your site.
Use an exact allow-list for external destinations. If you must redirect to other domains, such as partner sites, compare the parsed hostname against an explicit list of exact values. Avoid suffix matching unless you control every subdomain.
Show an interstitial for external links. Many large sites send external redirects through a “You are leaving this site” page that displays the full destination. It does not stop a determined user, but it removes the illusion that the destination is trusted.
Register exact redirect URIs with identity providers. On the OAuth side, register full, exact callback URLs, never wildcards, and keep the callback handler free of any secondary redirect logic driven by user input.
Validation approaches compared
| Approach | Blocks protocol-relative | Blocks user-info tricks | Flexibility | Recommended |
|---|---|---|---|---|
| No user-supplied URL (session or key lookup) | Yes | Yes | Low | Best |
| Parse and require same origin | Yes | Yes | Medium | Yes |
| Exact hostname allow-list (parsed) | Yes | Yes | Medium | Yes, for external sites |
startsWith or includes string checks | No | No | High | No |
| No validation | No | No | Total | Never |
Testing for open redirects
Look for parameters with names like next, return, returnTo, redirect, url, continue, or dest, in query strings, form fields, and JavaScript that writes to location. Try an absolute external URL, a protocol-relative one, a user@host form, and a backslash variant. Remember to test logout, OAuth callback, and email-link handlers, which are often written quickly and reviewed less carefully than login. This kind of check fits naturally into routine penetration testing.
The takeaway
An open redirect lets anyone use your domain as a launch pad to any other URL. It lends credibility to phishing and can leak OAuth codes and tokens when chained with authentication flows. Avoid user-supplied redirect URLs where you can, and where you cannot, parse the input with a real URL parser, require your own origin or an exact allow-list, and never rely on string prefix or substring checks.
Keep reading
Chisato · · 4 min read What Is a Man-in-the-Browser Attack?
A man-in-the-browser attack uses malware inside the browser itself to alter what a user sees and submits, bypassing HTTPS and session protections entirely.
The Lycoris Team · · 5 min read CSRF vs. XSS: What's the Difference?
CSRF forges a request using a victim's login session; XSS runs the attacker's own code inside the victim's browser. Different mechanisms, different fixes.
Chisato · · 4 min read Magic Links: How Passwordless Email Login Works
A magic link authenticates a user by emailing a single-use, expiring URL instead of checking a password. How the flow works and its real tradeoffs.