Redirects

How to pass a URL as a query parameter.

Redirect links, OAuth redirect_uri, and “return to” parameters all embed one URL inside another. If you don’t encode the inner URL, its own ?, &, and # get read as part of the outer URL and everything breaks. The fix is one function.

The problem

Say you want to send a user to a login page and pass where they should return afterwards:

// WRONG — the inner URL's ?tab=2 is read as the OUTER URL's parameter
https://site.com/login?return=https://app.com/dashboard?tab=2

// The server sees TWO parameters on the outer URL:
//   return = https://app.com/dashboard
//   tab    = 2         ← leaked out of the inner URL

The fix: encodeURIComponent

Percent-encode the entire inner URL before putting it in the parameter. Every structural character (:, /, ?, &, #) becomes a % sequence, so it travels as data, not structure:

const inner = 'https://app.com/dashboard?tab=2';
const url = 'https://site.com/login?return=' + encodeURIComponent(inner);
// https://site.com/login?return=https%3A%2F%2Fapp.com%2Fdashboard%3Ftab%3D2

Or, more robustly, let URLSearchParams do the encoding — it encodes each value exactly once:

const url = new URL('https://site.com/login');
url.searchParams.set('return', 'https://app.com/dashboard?tab=2');
url.toString();
// https://site.com/login?return=https%3A%2F%2Fapp.com%2Fdashboard%3Ftab%3D2

Reading it back

On the receiving side, URLSearchParams decodes it automatically:

const params = new URLSearchParams(location.search);
const returnUrl = params.get('return');
// "https://app.com/dashboard?tab=2"  ← fully decoded, ready to use

Don’t double-encode

A common bug: encoding a value that’s already encoded, or wrapping encodeURIComponent around a value you then also pass through URLSearchParams. The symptom is %2520 where you expected %20 (the % itself got encoded). Encode once, at the point you build the URL. See our double-encoding guide if you hit this.

Security note: validate before redirecting

If you redirect users to a URL that came from a query parameter, an attacker can craft a link that bounces your users to a phishing site (an “open redirect”). Always check the decoded URL against an allowlist of trusted destinations before redirecting. Our decode-then-validate guide covers the pattern.

Building these by hand? The URL builder assembles a URL from parts with correct encoding, and the decoder unwraps an encoded inner URL so you can read it.

About this page

Written and maintained by the urlencodedecode.com team. Every technical claim on this page is verified against primary sources — the RFCs (3986, 3629, 4648, 7578), the WHATWG URL Standard, and official vendor or language documentation — rather than second-hand summaries. When a source contradicts a common assumption, we follow the source and note the discrepancy. Corrections: contactus@urlencodedecode.com.

Common questions

Frequently asked.

Percent-encode the inner URL with encodeURIComponent before adding it, e.g. '?return=' + encodeURIComponent(innerUrl). This turns the inner URL's :, /, ?, &, and # into %-sequences so they don't break the outer URL. URLSearchParams.set() does the same encoding for you.

Because the inner URL's own ? and & are being read as part of the outer URL. Encoding the inner URL with encodeURIComponent makes those characters into data (%3F, %26) so the outer URL stays intact.

Use encodeURIComponent for a URL you're embedding as a value — it encodes the structural characters. encodeURI is for a whole URL you want to keep usable, so it leaves :/?&= alone, which is exactly wrong for an embedded value.

new URLSearchParams(location.search).get('return') returns the fully decoded inner URL, ready to use. You don't need to call decodeURIComponent yourself — URLSearchParams decodes values automatically.

Not without checking it first. Redirecting to an unvalidated user-supplied URL is an open-redirect vulnerability. Validate the decoded URL against an allowlist of trusted hosts before redirecting.

Related

Keep exploring.