---
url: https://findutils.com/blog/url-encoding-explained-percent-encoding-and-query-strings
title: "URL Encoding Explained: %20 vs +, and Why Links Break"
description: "Learn how percent-encoding works, when a space becomes %20 or +, and how to fix double-encoded or broken query strings with free browser tools."
category: developer
content_type: blog
cluster: url-tools
locale: en
read_time: 8
status: published
author: "olgunozoktas"
published_at: 2026-10-17T08:00:00Z
excerpt: "A practical explainer on percent-encoding: which characters must be encoded, why spaces show up as %20 in one place and + in another, and how to debug double-encoded, truncated, or garbled URLs step by step."
tag_ids: ["url-encoding", "percent-encoding", "query-string", "web-development", "developer-tools"]
tags: ["URL Encoding", "Percent-Encoding", "Query String", "Web Development", "Developer Tools"]
primary_keyword: "url encoding explained"
secondary_keywords: ["percent encoding", "%20 vs +", "encodeURIComponent vs encodeURI", "double encoded url", "query string encoding"]
tool_tag: "url-encoder-decoder"
related_tool: "url-encoder-decoder"
related_tools: ["url-encoder-decoder", "url-parser", "json-to-url-params", "url-slug-generator", "base64-encoder"]
updated_at: "2026-10-17T08:00:00Z"
og_image: "/images/content/blog/url-encoding-explained-percent-encoding-and-query-strings-cover-20261017.webp"
image_alt: "A query string with a space and an ampersand passes a row of glowing slots and comes out with those characters replaced by %20 and %26 codes."
---

URL encoding, also called percent-encoding, replaces a character that is unsafe or has a special meaning in a URL with a `%` sign followed by two hexadecimal digits for each of its bytes. A space becomes `%20`, an ampersand becomes `%26`, and `é` becomes `%C3%A9`. Encoding is what lets a value such as `Jack & Jill` travel inside a query string without being read as two separate parameters.

This post explains which characters need encoding, why spaces appear as `%20` in some URLs and `+` in others, and how to debug the most common broken-link cases. FindUtils tools used here: the [URL Encoder Decoder](/network/url-encoder-decoder/), the [URL Parser](/developers/url-parser/), [JSON to URL Params](/developers/json-to-url-params/), and the [URL Slug Generator](/seo/url-slug-generator/).

## Which characters must be percent-encoded?

Percent-encoding is defined in [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986), the generic URI syntax. The RFC sorts characters into two groups, and the group decides whether a character can appear as-is.

| Group | Characters | Rule |
|-------|------------|------|
| Unreserved | `A–Z a–z 0–9 - . _ ~` | Never need encoding; producers should not encode them |
| Reserved: general delimiters | `: / ? # [ ] @` | Separate the parts of a URL; encode them when they are data |
| Reserved: sub-delimiters | `! $ & ' ( ) * + , ; =` | Separate values inside a part; encode them when they are data |
| Everything else | Spaces, quotes, `%`, `<`, `>`, non-ASCII letters, emoji | Always encode |

Non-ASCII text is first converted to UTF-8 bytes, and each byte is encoded on its own. That is why one emoji such as 😀 becomes four triplets, `%F0%9F%98%80`.

## Why is a space sometimes %20 and sometimes +?

Two encodings exist side by side. RFC 3986 percent-encoding writes a space as `%20`. HTML forms use the `application/x-www-form-urlencoded` format, defined in the [WHATWG URL Standard](https://url.spec.whatwg.org/#application/x-www-form-urlencoded), which writes a space as `+`.

The difference matters when you decode. In JavaScript, `decodeURIComponent()` does not turn `+` into a space, so `a+b%20c` decodes to `a+b c`. [MDN's decodeURIComponent() reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent) notes that query parameters need `+` replaced with spaces first. `URLSearchParams` does the opposite: per [MDN's URLSearchParams reference](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams), it reads `+` as a space and writes spaces as `+`.

The practical rule: a literal plus sign inside a value must be sent as `%2B`. Base64 strings, phone numbers in international format, and math expressions are the usual victims. MDN shows a Base64 value `E+AXQB+A` turning into `E AXQB A` after a round trip through `URLSearchParams`.

## encodeURI vs encodeURIComponent

JavaScript has two encoding functions, and choosing the wrong one is a classic bug. `encodeURIComponent()` encodes everything except `A–Z a–z 0–9 - _ . ! ~ * ' ( )`, including `/ ? & = #`, so it is the right choice for a single value. `encodeURI()` leaves the URL's structural characters alone, so it suits a whole URL that only needs spaces and non-ASCII letters fixed.

| Input | encodeURIComponent | encodeURI |
|-------|-------------------|-----------|
| `hello world & café?` | `hello%20world%20%26%20caf%C3%A9%3F` | — |
| `https://example.com/a b?q=x&y=é` | — | `https://example.com/a%20b?q=x&y=%C3%A9` |

The FindUtils [URL Encoder Decoder](/network/url-encoder-decoder/) encodes with `encodeURIComponent()`, so it treats your input as a value: `?`, `&`, `=`, and `/` are all encoded. Paste only the value you want to embed, not the full URL. MDN's [encodeURIComponent() reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent) also notes that RFC 3986 reserves `!`, `'`, `(`, `)`, and `*`, which this function leaves unencoded.

## How to debug a broken URL step by step

Most broken links fail in one of five ways. Work through them in order; each check takes seconds.

### Step 1: Split the URL into its parts

Paste the URL into the [URL Parser](/developers/url-parser/). It shows the protocol, host, port, path, query string, and hash, and lists every query parameter in a key and value table with the values decoded. A parameter that is missing or cut short in the table points straight at the problem. Repeated keys, such as `size=42&size=43`, appear as one row per value and as an array in the JSON output.

### Step 2: Look for an unencoded & or # inside a value

If a value contains a raw `&`, everything after it becomes a new parameter. If it contains a raw `#`, everything after it becomes the fragment, which browsers do not send to the server. Encode the value on its own with the [URL Encoder Decoder](/network/url-encoder-decoder/): `&` becomes `%26` and `#` becomes `%23`.

### Step 3: Check for double encoding

A value that contains `%25` followed by two hex digits, such as `%2520`, was encoded twice: `%25` is the encoded form of `%` itself. Decode it once in the [URL Encoder Decoder](/network/url-encoder-decoder/); if the result still contains `%20`-style triplets, decode again and then find the step in your code that encodes an already-encoded string.

### Step 4: Watch for malformed percent signs

A `%` that is not followed by two hex digits, such as `100%`, cannot be decoded. The decoder reports invalid input in that case, which is the signal to encode the literal percent sign as `%25`.

### Step 5: Rebuild the query string from data

When a query string has many parameters, build it from a JSON object instead of by hand. [JSON to URL Params](/developers/json-to-url-params/) encodes each value with `encodeURIComponent()` by default and writes arrays either as repeated keys (`tags=a&tags=b`) or with brackets (`tags[]=a&tags[]=b`). It also converts in the other direction, from a query string or full URL back to JSON.

## What about non-ASCII domain names?

Domain names are not percent-encoded. Browsers convert an internationalized host name to an ASCII form starting with `xn--`, while the path and query are percent-encoded as UTF-8. Following the WHATWG URL Standard, `https://münchen.de/straße` becomes `https://xn--mnchen-3ya.de/stra%C3%9Fe`. The [URL Parser](/developers/url-parser/) shows the converted host, which helps when a log entry and a link do not seem to match.

## Encoding is not the same as a clean URL

Percent-encoding makes any text safe inside a URL, but it does not make it readable. For page addresses that people see and share, a slug made of lowercase ASCII words and hyphens is easier to read and type. The [URL Slug Generator](/seo/url-slug-generator/) turns a title such as `Café Menu: Spring 2026` into `cafe-menu-spring-2026`, transliterating accented letters instead of encoding them.

## Common mistakes

### Mistake 1: Encoding the whole URL with a component encoder

Running a full URL through `encodeURIComponent()` encodes `://` and every `/`, producing a string that no longer works as a link. Encode each value, then assemble the URL.

### Mistake 2: Sending a literal + unencoded

Servers that parse form-style query strings read `+` as a space. Encode a plus sign in data as `%2B`.

### Mistake 3: Decoding twice on the server

If a framework already decoded the query, decoding it again turns a legitimate `%25` back into `%` and can corrupt values. Decode exactly once.

### Mistake 4: Putting secrets in a URL

Encoding is not encryption; anyone can decode a URL. URLs also end up in browser history and server logs, so keep tokens and passwords out of query strings where possible.

## FAQ

### What does %20 mean in a URL?

`%20` is the percent-encoded form of a space character. The `20` is the hexadecimal value of the space in ASCII and UTF-8.

### Should I use %20 or + for spaces?

Use `%20` in paths and in general URLs. `+` means a space only in form-encoded query strings. `%20` is understood in both places, which makes it the safer choice when you encode by hand.

### Why does my URL contain %2520?

The URL was encoded twice. `%25` is an encoded percent sign, so `%2520` decodes to `%20`, which decodes to a space. Remove the extra encoding step.

### Does the URL Encoder Decoder upload my data?

No. On the tool page, encoding and decoding run in your browser and the input is not sent to a server.

### Is URL encoding the same as Base64?

No. Percent-encoding escapes individual characters so that text fits in a URL, while Base64 re-encodes binary data as text. A Base64 value can itself contain `+`, `/`, and `=`, so it still needs percent-encoding inside a query string. The [Base64 Encoder](/developers/base64-encoder/) handles the Base64 step.

### Is the fragment after # sent to the server?

No. The part after `#` stays in the browser. The URL Parser shows it so you can see what an anchor link or a client-side router will use.

## Tools used in this post

- **[URL Encoder Decoder](/network/url-encoder-decoder/)** — percent-encode a value or decode an encoded string
- **[URL Parser](/developers/url-parser/)** — split a URL into parts and list decoded query parameters
- **[JSON to URL Params](/developers/json-to-url-params/)** — build a query string from JSON, or turn one back into JSON
- **[URL Slug Generator](/seo/url-slug-generator/)** — create readable, transliterated slugs from titles
- **[Base64 Encoder](/developers/base64-encoder/)** — encode and decode Base64 before placing it in a URL
- **[Complete guide to Base64 encoding](/guides/complete-guide-to-base64-encoding/)** — how Base64 differs from percent-encoding

## Sources

- [RFC 3986: Uniform Resource Identifier (URI): Generic Syntax](https://datatracker.ietf.org/doc/html/rfc3986)
- [WHATWG URL Standard: application/x-www-form-urlencoded](https://url.spec.whatwg.org/#application/x-www-form-urlencoded)
- [MDN: encodeURIComponent()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent)
- [MDN: decodeURIComponent()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent)
- [MDN: URLSearchParams](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams)
