URL Encoding Explained: Percent-Encoding, %20 vs + and Query Strings

URL encoding explained: how percent-encoding works, which characters are reserved, encodeURIComponent vs encodeURI, %20 vs + for spaces, and safe UTM links.

By the MakeItFlow team Updated 8 min read
Share LinkedIn X Facebook WhatsApp
URL Encoding Explained: Percent-Encoding, %20 vs + and Query Strings

URL encoding, also called percent-encoding, replaces characters that aren't allowed in a URL, or that have a special meaning there, with a % sign followed by two hexadecimal digits. A space becomes %20, an ampersand becomes %26 and a pound sign becomes %C2%A3. It keeps values like search terms and campaign names from breaking the link.

If you've ever pasted a link and found it full of %20s, or seen a campaign report split one campaign into three because of stray spaces and symbols, you've met URL encoding. This guide explains which characters need encoding and why, how JavaScript's encoding functions differ, the long-running confusion between + and %20, and how to build tracking links that survive being shared.

To encode or decode anything on this page, use our free URL encoder and decoder. It offers encodeURIComponent, encodeURI and form modes, flags malformed sequences, and splits any URL into its parts.

Key takeaways

  • URL encoding replaces unsafe or reserved characters with a % sign and two hex digits per UTF-8 byte, so a space becomes %20 and £ becomes %C2%A3.
  • Use encodeURIComponent (or URLSearchParams) for individual values and encodeURI only for a complete, already-structured URL.
  • A + means a space only in form-style query strings; in a path it's a literal plus, so %20 is the safe choice everywhere.
  • Build UTM links with an encoder or URL builder, not by hand, to avoid broken parameters and double encoding such as %2520.

How percent-encoding works

A URL can only contain a limited set of ASCII characters. Everything else, including spaces, accented letters, currency symbols and emoji, has to be converted. The rules come from RFC 3986, the IETF standard for URI syntax:

  1. Convert the character to bytes using UTF-8.
  2. Write each byte as % followed by two hex digits.
  3. Use uppercase hex digits, which RFC 3986 recommends for consistency.

A plain ASCII character is one byte, so it becomes one triplet. Other characters take two to four bytes:

Character UTF-8 bytes Encoded
Space 1 %20
& 1 %26
£ 2 %C2%A3
é 2 %C3%A9
€ 3 %E2%82%AC
😀 4 %F0%9F%98%80

Decoding reverses the process: read the bytes, then interpret them as UTF-8. If a system decodes with the wrong character set, you get garbled text such as £ instead of £.

Reserved and unreserved characters

RFC 3986 splits characters into groups:

  • Unreserved characters never need encoding: A–Z, a–z, 0–9, -, ., _ and ~. The RFC says producers shouldn't percent-encode them.
  • Reserved characters have a job in URL syntax. The "general delimiters" are : / ? # [ ] @, and the "sub-delimiters" are ! $ & ' ( ) * + , ; =.
  • Everything else, including spaces, %, ", <, > and all non-ASCII characters, must always be encoded.

Reserved characters only need encoding when you want them as data rather than structure. In https://example.com/search?q=fish&page=2, the ?, = and & are doing their jobs. But if someone searches for "fish & chips", the & inside the value must become %26, or the server reads it as the start of a new parameter:

Broken:  /search?q=fish & chips&page=2     → q = "fish ", plus a stray " chips" parameter
Correct: /search?q=fish%20%26%20chips&page=2 → q = "fish & chips"

The % sign itself must be encoded as %25, otherwise a decoder will try to read the next two characters as a byte.

Common characters and their encodings

Character Name Encoded Why it matters
Space %20 (or + in form data) Not allowed in URLs at all
# Hash %23 Starts the fragment, so anything after it isn't sent to the server
% Percent %25 Starts an encoded byte
& Ampersand %26 Separates query parameters
+ Plus %2B Read as a space in form-style query strings
/ Slash %2F Separates path segments
= Equals %3D Separates parameter names and values
? Question mark %3F Starts the query string
@ At sign %40 Can be read as user information before the host
: Colon %3A Separates the scheme and port

The # row causes some of the most confusing bugs. A product called "Size #2" in an unencoded URL gets truncated at the hash, because browsers never send the fragment to the server.

encodeURIComponent vs encodeURI in JavaScript

JavaScript has two built-in functions, and choosing the wrong one is a classic bug. According to MDN, encodeURIComponent() leaves only A–Z a–z 0–9 - _ . ! ~ * ' ( ) unescaped. encodeURI() uses the same algorithm but also leaves URL syntax characters alone, such as ; , / ? : @ & = + $ #.

Here's the same string through each method:

const s = "Fish & chips £5/each? 50% off";

encodeURIComponent(s);
// "Fish%20%26%20chips%20%C2%A35%2Feach%3F%2050%25%20off"

encodeURI(s);
// "Fish%20&%20chips%20%C2%A35/each?%2050%25%20off"

new URLSearchParams({ q: s }).toString();
// "q=Fish+%26+chips+%C2%A35%2Feach%3F+50%25+off"

encodeURI left the &, / and ? untouched, so if you dropped its output into a query string, the value would break the URL. The rule:

Function Use it for Don't use it for
encodeURIComponent() A single value: a query parameter, a path segment, a redirect target passed as a parameter A whole URL, because it encodes the :// and slashes
encodeURI() A complete URL that is already correctly structured but may contain spaces or non-ASCII characters Individual values, because it leaves &, = and ? alone
URLSearchParams Building or reading whole query strings Paths
new URL() Parsing and modifying URLs safely
escape() Nothing. It's deprecated and doesn't handle UTF-8 properly

In practice, URL and URLSearchParams are the safest options for most work, because they handle encoding for you:

const url = new URL("https://example.com/search");
url.searchParams.set("q", "fish & chips");
url.searchParams.set("utm_campaign", "autumn sale");
url.toString();
// "https://example.com/search?q=fish+%26+chips&utm_campaign=autumn+sale"

Two smaller details from MDN are worth knowing. encodeURIComponent() doesn't encode ! ' ( ) *, which RFC 3986 reserves, so a few strict APIs need an extra replacement step. And it throws a URIError if a string contains a lone surrogate, half of an emoji pair, which can happen when text is cut at a fixed length.

Other languages

Language Form style (space as +) RFC 3986 style (space as %20)
PHP urlencode() rawurlencode()
Python urllib.parse.quote_plus() urllib.parse.quote()
JavaScript URLSearchParams encodeURIComponent()

%20 vs +: which is right for a space?

Both, depending on where the space is.

  • %20 is valid everywhere in a URL: paths, query strings and fragments.
  • + means a space only in application/x-www-form-urlencoded data, the format HTML forms use when they submit with GET or POST. The WHATWG URL Standard defines that format and serialises spaces as +. URLSearchParams follows it.
  • In a path, + is a literal plus sign. /products/C++ refers to C plus plus, not "C" followed by two spaces.

This causes real bugs. decodeURIComponent("autumn+sale") returns autumn+sale, with the plus intact, because it doesn't use form rules. new URLSearchParams("c=autumn+sale").get("c") returns autumn sale. If a genuine plus sign in a value, such as a phone number like +44, isn't encoded as %2B, a form-aware server will turn it into a space.

The safe habit: encode spaces as %20 when you build URLs by hand, encode real plus signs as %2B, and let URLSearchParams or your framework handle form data.

Tracking links are where most businesses meet encoding problems. A UTM-tagged link adds parameters such as utm_source, utm_medium and utm_campaign to the query string, and analytics tools read them to attribute visits. Our UTM parameters guide covers naming conventions in depth. The encoding issues are:

  1. Spaces in values. utm_campaign=autumn sale may arrive as autumn sale, autumn+sale or autumn%20sale depending on where it was pasted. Analytics tools usually decode them to the same value, but some email platforms and link shorteners mangle them. Avoid the problem entirely by using lowercase words joined with hyphens: autumn-sale.
  2. Ampersands and hashes in values. A campaign named "Fish & Chips Friday" or "#1 Offer" breaks the link unless the & and # are encoded. Better still, don't use them in campaign names.
  3. Links that already have a query string. If the landing page URL already contains ?, append UTM parameters with &, not a second ?.
  4. Fragments. Parameters must come before any #. In /page#offer?utm_source=x, the UTM part is inside the fragment and never reaches the server.
  5. Double encoding. If an already encoded link is encoded again, %20 becomes %2520, because the % itself is encoded as %25. You'll see campaign names like autumn%20sale in reports. It usually happens when a link passes through two tools that each encode it.

Our free UTM builder assembles campaign links with the correct encoding, and the URL encoder decodes a suspicious link so you can see exactly what's in it. Agencies juggling several clients will find more link and campaign tools on our page of free tools for digital marketing agencies.

Encoded characters in page URLs and SEO

Non-ASCII characters in paths are allowed. Browsers show /café in the address bar but send /caf%C3%A9. Google can crawl both. Readability is the real concern. When a link with encoded characters is copied into an email or document, people see /caf%C3%A9-menu, which looks broken.

For English-language sites, the simplest approach is to keep slugs to lowercase ASCII letters, numbers and hyphens. Our slug generator converts titles into clean slugs, and the guide to SEO-friendly URLs covers structure and redirects. If you change URLs, check the old ones redirect properly.

Domain names are a separate system. International domain names such as café.example are converted to Punycode (xn--caf-dma.example), not percent-encoded.

Encoding is not security

Percent-encoding makes data safe to transport inside a URL. It doesn't make data safe to display or execute. Anyone can decode it, and attackers routinely encode payloads to slip past simple filters. Three rules:

  • Encode for the context. URL-encode values going into URLs, HTML-escape values going into a page, and use parameterised queries for databases. One doesn't replace another.
  • Decode once. Decoding repeatedly until nothing changes can turn %253C into <, reopening the exact hole a filter was meant to close.
  • Validate redirect targets. A ?next= or ?return= parameter that accepts any encoded URL can become an open redirect for phishing. Allow only your own paths or a fixed list of domains.

Like Base64 encoding, URL encoding is about representing data safely in a text format, not hiding it.

A quick checklist for building URLs

  1. Build URLs with URL and URLSearchParams, or your framework's URL helper, rather than string concatenation.
  2. Encode each value separately with encodeURIComponent if you must build strings by hand.
  3. Never run encodeURI or encodeURIComponent on a URL that's already encoded.
  4. Use %20 for spaces in paths and %2B for genuine plus signs.
  5. Keep campaign names lowercase with hyphens, and put query strings before any #.
  6. Test the final link by clicking it, and decode it to check every parameter arrived intact.

If your team builds dozens of campaign links a week by hand, that's a good candidate for automation. Make It Flow can set up a simple workflow that generates consistent, correctly encoded links from a spreadsheet. Get in touch if that would help.

FAQ

What is URL encoding used for?

URL encoding converts characters that aren't allowed in URLs, or that have a special meaning there, into a safe format. It's used for search terms, form submissions, campaign parameters, file names and any text containing spaces, symbols or non-English characters, so the browser and server read the URL exactly as intended.

Why does a space become %20?

A space isn't allowed in a URL, so it's percent-encoded. The space character has the byte value 32, which is 20 in hexadecimal, so it becomes %20. In form-style query strings, a space may also be written as +, but %20 is valid everywhere in a URL.

What is the difference between encodeURI and encodeURIComponent?

encodeURIComponent() encodes everything except letters, digits and - _ . ! ~ * ' ( ), so it's right for individual values like query parameters. encodeURI() leaves URL structure characters such as / ? & = # alone, so it's only for complete URLs. Using encodeURI on a value lets & and = break the query string.

What does %2520 mean in a URL?

It means the URL was encoded twice. The first pass turned a space into %20. The second pass encoded the % sign as %25, producing %2520. Find the step that encodes an already encoded value, usually a link passing through two tools, and remove one of them.

Do I need to encode UTM parameters?

Yes, if the values contain spaces, ampersands, hashes, plus signs or non-ASCII characters. The easiest approach is to avoid them altogether by using lowercase words joined with hyphens, such as utm_campaign=autumn-sale. A UTM builder handles encoding automatically and stops parameters breaking when links are shared.

Is URL encoding the same as encryption?

No. URL encoding is a public, reversible format that anyone can decode instantly. It exists to transport text safely inside a URL, not to hide it. Never put passwords, tokens or personal data in a URL thinking encoding protects them, because URLs end up in browser history, server logs and analytics.

#url encoding #percent-encoding #utm links #web development

More guides for your industry

Free checklist (PDF)

Free marketing agencies website checklist

31 practical checks for marketing agencies: website speed and mobile, Google Business Profile and reviews, email security, booking and enquiry handling, and AI search visibility. Each one says why it matters, and many link to a free tool that checks it.

We email you the PDF and nothing else unless you tick the box. See the privacy policy.

Written by the MakeItFlow team

We build the website audit these guides are based on. Every fact is checked against the primary sources listed above and this guide was last reviewed on 25 October 2026 . How we research and check

Share LinkedIn X Facebook WhatsApp

Check your own site in 30 seconds

Run a free audit covering speed, SEO, DNS, email authentication and security headers — and get a prioritised fix list.

Weekly newsletter

One practical website fix, every week

New guides on speed, SEO, email deliverability and security — straight to your inbox. No spam, unsubscribe in one click.

Keep reading