Parse the incoming URL, compare its host against an allow list, and refuse anything else. Better still, accept a short key that maps to a known destination instead of a URL. Never pass req.query values straight into res.redirect or res.location, because that turns your domain into a phishing launchpad.
Definition
An open redirect is when an application accepts a URL as input, often in a query string like ?url=, and uses it to set the Location header with a 3xx status. The Express security guide describes this as potentially dangerous user input, and says an application must validate that it supports redirecting to the incoming URL, to avoid sending users to malicious links such as phishing sites, among other risks.
It looks minor, but a link on a trusted domain that bounces to an attacker's page is convincing. Login flows that return users to a page after sign in are the usual place for it.
The Express guide's check
The security guide shows this approach: parse the URL with the URL constructor, compare the host, and reject on mismatch or parse error.
app.use((req, res) => {
try {
if (new URL(req.query.url).host !== 'example.com') {
return res.status(400).end(`Unsupported redirect to host: ${req.query.url}`);
}
} catch (e) {
return res.status(400).end(`Invalid url: ${req.query.url}`);
}
res.redirect(req.query.url);
});
Treat this as a starting point. Echoing the rejected URL back into the response, as the example does, can create its own reflected content problem if the response type allows markup, so return a fixed message in your own code.
Better patterns, ranked by safety
| Pattern | Safety | Notes |
|---|---|---|
| Map a key to a fixed destination | Highest | Client sends a name like dashboard, server picks the URL |
| Relative paths only, validated | High | Reject anything starting with two slashes or containing a scheme |
| Allow list of exact hosts | Medium to high | Compare parsed host, not a substring |
| Block list of bad hosts | Low | Always incomplete |
Substring checks fail in predictable ways, for example a check that the URL contains your domain also passes for a hostile domain that merely includes it.
Steps
- Find every use of res.redirect and res.location. Search for them in your codebase.
- For each, ask where the destination comes from. If any part is user controlled, fix it.
- Prefer the key to destination map. Otherwise use parsed host comparison against exact entries.
- Restrict the scheme to https, and to http only for local development.
- Add tests with hostile inputs: another host, a scheme relative URL, an encoded value and a lookalike domain.
- Check recent advisories for your Express version, since redirect handling itself has had fixes. See CVE-2024-29041 open redirect.
Behind a proxy
If you build absolute URLs from req.hostname while trust proxy is on, the value comes from X-Forwarded-Host, which a client or proxy can set. Do not use it to build redirect targets unless the proxy overwrites it. See Express trust proxy.
Safeguard's DAST web scanning can probe parameters like these in a running app, and its dependency scanning flags advisories in the Express version you use. Try the free scan at gold.safeguard.sh/scan.
Quick check
Is checking that the URL starts with my domain enough?
No. Parse the URL and compare the host exactly. Prefix and substring checks are easy to bypass with lookalike hosts.
Can I just redirect to a relative path?
It is safer, but validate it. Paths beginning with two slashes can be read as another host by browsers.
Sources
Last reviewed: 19 October 2026.