EncryptCodecencryptcodec
Blog/Security
SecurityMarch 12, 2026 · 7 min read

encodeURIComponent vs encodeURI — Stop Guessing Which One to Use

You've got a search API. A user types hello & goodbye = forever. You blindly interpolate that into a query string, ship it, and six months later someone files a bug because & splits the parameter in half and the backend gets two garbage keys instead of one. You've seen this. Everyone has seen this.

The fix is one function call. But which one?

What Each Function Actually Encodes

The difference comes down to which characters get percent-encoded.

encodeURI leaves these characters alone because they're structurally meaningful in a URL:

; , / ? : @ & = + $ # A-Z a-z 0-9 - _ . ! ~ * ' ( )

encodeURIComponent encodes everything except unreserved characters:

A-Z a-z 0-9 - _ . ! ~ * ' ( )

That means encodeURIComponent will encode : / ? # @ & = + , $ — all the characters that give a URL its shape.

Live comparison — type anything or try a sample

Input string
encodeURI()keeps URL structure chars intact
hello%20&%20goodbye%20=%20forever
encodeURIComponent()encodes everything including structure
hello%20%26%20goodbye%20%3D%20forever

✓ For query params and user input: always use encodeURIComponent

Use encodeURI only for complete, already-assembled URLs with no user data

Concrete example:

const value = "hello & goodbye=forever";

encodeURI(value);
// "hello%20&%20goodbye=forever"
// ❌ & and = survive — they'll break your query string

encodeURIComponent(value);
// "hello%20%26%20goodbye%3Dforever"
// ✅ & → %26, = → %3D — safe to embed as a query param value

The Production Bug encodeURI Creates

Say you're building a redirect URL with a returnTo parameter:

/login?returnTo=https://app.example.com/dashboard?tab=settings&user=42

If you use encodeURI on the returnTo value, the ?, =, and & in the value are left unencoded. Your server now sees:

returnTo = https://app.example.com/dashboard
tab = settings
user = 42

Three parameters instead of one. The login redirect breaks, and in a worst case where you're using that returnTo value without validation, you've got an open redirect vulnerability.

encodeURIComponent would have produced:

/login?returnTo=https%3A%2F%2Fapp.example.com%2Fdashboard%3Ftab%3Dsettings%26user%3D42

One parameter. Correct.

When encodeURI Is Actually the Right Call

Almost never — but there is a valid use case: you've received a URL string that might contain non-ASCII characters and you need to normalize it without destroying its structure.

const messyURL = "https://example.com/résumé?lang=fr";
encodeURI(messyURL);
// "https://example.com/r%C3%A9sum%C3%A9?lang=fr"

The path gets fixed, the ? and = stay intact. This is a narrow, specific use case — not the default.

The Gotcha Junior Devs Miss: + vs %20

encodeURIComponent encodes spaces as %20. Some server-side parsers (especially older PHP and Java URLDecoder) expect + for spaces in query strings, not %20. They're both valid in different contexts:

  • %20 — RFC 3986 (URI spec), correct for path segments and modern query parsers
  • + — HTML form encoding (application/x-www-form-urlencoded), used by URLEncoder.encode() in Java and urlencode() in PHP

If you're building a query string to submit as a form, + for spaces is fine. If you're constructing a URL programmatically to pass to a fetch/HTTP client, use %20 via encodeURIComponent.

Mixing them up causes intermittent decoding bugs that only appear with user input containing spaces — the kind that pass your own tests but blow up in production.

Building Full URLs Safely

Don't manually concatenate URLs. Use the URL constructor or your language's URI builder:

// ✅ Correct — let the URL API handle encoding
const base = new URL("https://api.example.com/search");
base.searchParams.set("q", "hello & goodbye=forever");
base.searchParams.set("returnTo", "https://app.example.com/dashboard?tab=1");

console.log(base.toString());
// https://api.example.com/search?q=hello+%26+goodbye%3Dforever&returnTo=https%3A%2F%2Fapp.example.com%2Fdashboard%3Ftab%3D1

// ❌ Wrong — manual concatenation with wrong encoder
const url = `https://api.example.com/search?q=${encodeURI(query)}`;

URLSearchParams, URLComponents, URIBuilder, http_build_query — they all call the equivalent of encodeURIComponent under the hood. Let them do the work.

Use encodeURIComponent (or your language's equivalent) any time you're embedding a value inside a URL — query params, path segments, fragment identifiers. Use encodeURI only when you have a fully-formed URL that needs non-ASCII normalization, which is rare.

The URL API in every modern language handles encoding correctly when you build URLs through it rather than string interpolation. That's the real answer: stop manually constructing URLs, and the encodeURI vs encodeURIComponent question mostly goes away.

Share this post

Encode and decode Base64 strings instantly

Free, browser-based — no signup required.

Frequently Asked Questions

Related posts