Six open redirect variants mapped by severity: from CVSS 6.1 URL parameter redirects to CVSS 9.8 OAuth code theft chains. Quick-reference comparison and detection workflow.
TL;DR
endsWith are always bypassableOpen redirect vulnerabilities share a root cause — user-controlled input flows into a redirect mechanism without host validation — but differ enough in location and trigger that each variant requires a distinct testing and remediation approach. The six canonical variants under CWE-601 are mapped below, with severity anchors based on observed real-world chains.
The base CVSS score for an isolated open redirect is 6.1 (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N). This reflects user interaction required (the victim must follow the link), limited initial impact. The score climbs steeply when the redirect is positioned in a sensitive flow.
The classic form. A request parameter named next, return, redirect, goto, url, dest, or similar is passed to the server's redirect function without validation. The server emits a 30x response with a Location header containing the attacker's URL.
Severity: CVSS 6.1 in isolation; 8.6+ if the parameter is on an endpoint that handles tokens or sensitive state.
Full detail: URL Parameter Redirect
The application constructs redirect URLs using the Host or X-Forwarded-Host request header rather than a hardcoded base URL. An attacker injects a custom host, causing the redirect URL to point to their domain. Prominent on password reset flows — the reset link in the email contains the injected host.
Severity: CVSS 6.1-8.1 depending on whether the redirect chain enables account takeover via token theft.
Full detail: Header-Based Redirect
Client-side code reads a URL from window.location.search, document.referrer, window.location.hash, or localStorage, then assigns it to window.location or document.location.href without validation. No server-side 30x response — the redirect happens entirely in the browser. Automated scanners miss this unless they execute JavaScript.
Severity: CVSS 4.3-7.4. Lower because attacker needs social engineering delivery, but harder to detect automatically.
Full detail: JavaScript DOM-Based Redirect
The OAuth authorization server does not enforce exact redirect_uri matching per RFC 6749 §3.1.2. An attacker injects an open redirect on a registered domain into the redirect_uri parameter — the authorization server treats the registered domain as valid and appends the authorization code before redirecting. The code arrives at the attacker's endpoint via the open redirect chain.
Severity: CVSS 8.6-9.8 — authorization code theft enables full account takeover.
Full detail: OAuth redirect_uri Hijacking
The application server fetches a URL supplied by the user (document loader, webhook, image proxy, PDF renderer). An SSRF allowlist permits a trusted domain that has an open redirect. The server follows the redirect to an internal or cloud metadata endpoint. This upgrades an open redirect from a client-side phishing primitive to a server-side SSRF primitive.
Severity: CVSS 8.6-9.1 when cloud metadata credentials are reachable (CVE-2024-2376, LangChain).
Full detail: Open Redirect to SSRF Chain
The application exposes a redirect endpoint that accepts the destination URL as a path segment: /go/https://evil.com, /redirect/https://evil.com, /r/<url>. Often found in legacy URL shortener functionality, link tracking, or affiliate systems.
Severity: CVSS 6.1. Identical impact to URL parameter redirect; different exploitation URL structure. Covered in URL Parameter Redirect.
Most open redirect fixes attempt URL string validation. They fail because different parsers interpret the same string differently. The WHATWG URL standard (Chrome, Firefox, Edge, Safari), RFC 3986, and Node.js legacy url.parse reach different conclusions for the same input string — which is the mechanism behind most bypass payloads:
| Input | WHATWG URL | RFC 3986 | Node.js legacy url.parse |
|---|---|---|---|
//evil.com | host=evil.com (protocol-relative) | invalid (no scheme) | host=evil.com |
\evil.com | host=evil.com (normalizes \ to /) | invalid | host=evil.com (legacy treats \ as /) |
https:evil.com | host=evil.com | host=evil.com | scheme=https path=evil.com |
https://evil.com\@trusted.com | host=evil.com (\@ is path delim) | host=evil.com | host=trusted.com (legacy parses \@ as userinfo) |
//evil%2Ecom | host=evil.com (percent-decodes) | host=evil%2Ecom (raw) | host=evil%2Ecom |
The attack surface: a backend using Python urlparse rejects \evil.com (sees empty netloc) while the browser's WHATWG engine resolves it to https://evil.com. A filter built on RFC 3986-compliant parsing may reject //evil.com as missing scheme, while Chrome follows it as a protocol-relative URL. Node.js legacy url.parse treats \ as a path separator making \evil.com produce host=evil.com — while newer WHATWG new URL() also resolves it to evil.com. No string-level parser consistency exists across runtimes.
The only reliable fix: parse the URL with the same parser the browser uses (WHATWG new URL() in Node.js, or a dedicated library), extract the hostname, and compare against an exact Set of allowed hostnames. Never operate on the raw URL string.
Is the redirect parameter on an OAuth/SAML/SSO endpoint?
Yes → redirect-oauth → CVSS 8.6-9.8 (authorization code theft possible)
No → Is the application server fetching the URL itself?
Yes → redirect-ssrf-chain → CVSS 8.6+ (SSRF primitive)
No → Is the redirect URL embedded in email (e.g., password reset)?
Yes → redirect-header or redirect-url-param → CVSS 6.1-8.1
No → Client-side only (window.location)?
Yes → redirect-javascript → CVSS 4.3-7.4
No → Standard redirect-url-param → CVSS 6.1Understanding the CVSS scoring transitions is essential for triaging which redirects to report and how to communicate their impact.
CVSS 6.1 — Isolated URL Parameter Redirect
The base vector is CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N. User interaction is required (UI:R) because the victim must click a crafted link. Confidentiality and Integrity are both Low because the redirect in isolation only moves the victim to an attacker domain — no automatic data disclosure. Scope is Changed (S:C) because the redirect crosses the trust boundary of the originating domain.
CVSS 8.1 → 8.6 — Host Header / Password Reset Poisoning
When the redirect is constructed from the Host or X-Forwarded-Host header and embedded in a password reset email, the Integrity impact rises to High (I:H): an attacker can intercept the reset token and take over the victim account. The Confidentiality impact also rises because the token (a credential) is disclosed. This elevates the vector to approximately CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:N = 8.1 or higher depending on whether PR:N holds (it does if no authentication is needed to trigger the password reset flow).
CVSS 8.6 — OAuth redirect_uri + Authorization Code Theft
In the OAuth chain, the Authorization Code arrives at the attacker's endpoint directly. The Confidentiality impact is High (C:H) — the authorization code is equivalent to a session credential. Integrity is High (I:H) — the attacker can use the code to act as the victim. Scope is Changed. The CVSS base vector: AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:N = 8.6. The score reaches 9.8 when no user interaction is required (e.g., background OAuth flows where the victim is already authenticated) or when the attacker can skip client_secret (public clients, broken server validation).
CVSS 8.6 → 9.1 — Open Redirect → SSRF → Cloud Metadata When the server follows the redirect server-side, UI:R drops to UI:N (no victim click required — the server autonomously fetches). This alone drives the score up. Confidentiality is High when IAM credentials are reachable. The score reaches 9.1 when cloud credential access allows lateral movement to a high-value data store (S3, secrets manager, database). CVE-2024-2376 (LangChain) was rated 8.6 because the direct impact was credential disclosure; real-world exploitation chains from those credentials would score higher.
Each open redirect variant produces a distinct set of observable signals during testing. Recognizing these signals prevents both missed vulnerabilities and false positives:
| Variant | Server Signal | Browser Signal | Automated Tool |
|---|---|---|---|
| URL parameter | 30x Location: <canary> in HTTP response | Browser navigates to canary domain | OpenRedireX, nuclei redirect templates |
| Header-based | Reset email contains injected host, OR response body reflects X-FH | None (email delivery required or OOB) | Param Miner (Burp), httpx header reflection |
| DOM JavaScript | No server-side 30x at all | Browser window.location changes to canary (no network request to server) | Dalfox --open-redirect, headless Chrome monitoring |
| OAuth redirect_uri | Authorization server returns 302 with code appended to crafted URI | Code arrives at canary server | Burp OAuth Scanner, manual Repeater |
| SSRF chain | OOB DNS/HTTP callback from server IP (not victim browser) | None | Interactsh + nuclei SSRF, Burp Collaborator |
| Path-based | 30x Location: <canary> for /go/<canary> pattern | Browser navigates | nuclei path-based redirect templates |
The most common source of false positives: same-origin redirects where the Location header points to a path or the same domain. Always extract the eTLD+1 from the Location value and verify it differs from the target's eTLD+1 before reporting. CDN domains (*.cloudfront.net, *.akamaiedge.net, *.fastly.net) owned by the target are not open redirects — map declared CDN hostnames before running automated scans.
| Method | Tool | Signal |
|---|---|---|
| Parameter fuzzing | OpenRedireX, nuclei | 30x Location contains canary domain |
| DOM-based | Burp Scanner, dalfox --open-redirect | window.location assignment with param |
| Host header | Param Miner (Burp) | Email/response contains injected Host |
| OAuth flow | Manual + Burp Repeater | authorization code in redirect to attacker |
| SSRF via redirect | nuclei SSRF templates + Interactsh | OOB DNS/HTTP callback from server |
| Variant | Primary Fix |
|---|---|
| URL parameter | Allowlist hostname Set — exact match, not endsWith |
| Header-based | Hardcode base URL in env var, never read from Host header |
| JavaScript DOM | Validate on client using WHATWG URL constructor before window.location assignment |
| OAuth redirect_uri | Exact string comparison per RFC 6749 §3.1.2; no wildcards |
| SSRF chain | Validate final destination after following redirects, not just initial URL |
| Path-based | Replace with opaque redirect key mapped server-side |
Open redirect manifests in six forms: (1) URL parameter redirect — ?next=, ?return=, ?goto= passed to res.redirect() without validation; (2) header-based redirect — Host or X-Forwarded-Host injected into Location construction; (3) DOM-based JavaScript redirect — window.location = userParam in browser; (4) OAuth redirect_uri hijacking — authorization code theft via unvalidated callback URL; (5) SSRF chain — server-side redirect following reaches internal endpoints; (6) path-based — /go/https://evil.com pattern.
Prioritize by severity potential: (1) OAuth redirect_uri first — authorization code theft is critical; (2) any parameter named next/return/goto on login or logout endpoints — phishing attack surface; (3) Host header on password reset — account takeover; (4) any server-side redirect following (SSRF chain potential); (5) client-side JavaScript redirects — DOM-based, harder to detect automatically.
Base CVSS is 6.1 (Medium). Escalate to High if: the redirect is on an OAuth/SAML endpoint (token theft possible), the server follows redirects server-side (SSRF chain), or the endpoint handles sensitive operations like password reset. Escalate to Critical if: authorization codes are exposed, cloud credentials accessible via SSRF chain, or full account takeover is demonstrable.
OpenRedireX for parameter fuzzing; nuclei templates for CVE-specific checks; Burp Suite Scanner for passive detection + active scan; Param Miner for Host header variants; Dalfox v3+ for DOM-based detection. No single tool covers all variants — combine parameter fuzzing with manual OAuth testing and header injection.
CWE-601 is the specific class for URL redirection to untrusted sites. CWE-20 (Improper Input Validation) is the parent class. Host header injection that causes a redirect is often classified as CWE-20 or CWE-444 depending on the mechanism. When the outcome is specifically a redirect to an attacker-controlled URL, CWE-601 is the correct primary classification.
Direct SSRF controls the request URL directly. SSRF via open redirect is indirect: the attacker supplies a URL to a trusted domain that has an open redirect, and the server follows the redirect to an internal endpoint. SSRF filters allow the trusted domain but do not block the redirect. The key difference is that open redirect-based SSRF bypasses host-based SSRF allowlists.
OWASP WSTG-CLNT-004 covers DOM-based open redirect. For server-side: enumerate redirect parameters, submit external URLs, verify 30x Location response contains the external URL, test bypass payloads (//evil.com, \evil.com, %09https://evil.com). For DOM-based: review JavaScript source for window.location, document.location, location.href assignments; use a headless browser to observe actual navigation.
Yes. CSP does not prevent open redirect. CSP restricts where scripts, frames, and resources load from — it does not control the browser handling of HTTP redirect responses. An open redirect sends the browser to a new origin entirely, outside the CSP context. The attacker domain is under no CSP constraints.
HackerOne's CVSS calculator rates isolated open redirect at Low to Medium (CVSS 6.1). When chained with OAuth token theft or SSRF, reports are rated High to Critical and earn $1,000-$10,000+. HackerOne #112614 (Twitter OAuth open redirect) paid $1,470. Forgejo CVE-2025-30215 (OAuth redirect_uri) was rated CVSS 8.1.
Common bypasses: URL double-encoding (%2540 for @, %252F for /); Unicode normalization (fullwidth period %EF%BC%8E for ., zero-width space %E2%80%8B); control character prefix (%09 tab, %0A newline before https://); null byte (%00) between trusted and attacker domain; HTML entity encoding if input passes through HTML before redirect.