Space in a URL (%20)
A space becomes %20 when you percent-encode it. The space character occupies byte value 0x20 in ASCII.1 RFC 3986 places it outside both the reserved and unreserved sets.2 Consequently, every space you type into a link must be replaced before the URL travels, or the request breaks at the first whitespace. Two encodings compete for the job. Standard percent-encoding, defined by RFC 3986, writes a space as %20.3 While the older application/x-www-form-urlencoded format writes it as a plus sign.4 Both decode back to the same character, yet they are not interchangeable in every context. Paste any string into the CapyToolkit encoder and ENCODE mode shows all three outputs together, so you can watch exactly how a single space differs across encodeURIComponent, encodeURI, and form encoding.
What is %20?
%20 is the percent-encoded representation of a single space character. Percent-encoding, specified in RFC 3986 Section 2.1.2 replaces an unsafe byte with a percent sign followed by two hexadecimal digits that spell the byte value. A space is ASCII 32, which is 0x20 in hexadecimal.1 so its escape sequence is %20. Uppercase and lowercase hex digits are equivalent.3 meaning %20 and %20 decode identically. Any RFC 3986 compliant decoder, including your browser's native decodeURIComponent.5 converts %20 back to a literal space.Why a raw space breaks a URL
Whitespace terminates a URL in many parsers. When a browser, proxy, or logging system reads a request line, it treats the first space as the boundary between the URL and the HTTP version that follows.6 Consequently, a literal space inside a link silently truncates the address, and whatever came after it is lost or misread.
How RFC 3986 forbids literal spaces
RFC 3986 excludes whitespace from both unreserved and reserved sets.7 so no compliant URL leaves a space raw. Any byte outside those classes must be percent-encoded before it enters the address. From that rule follows the requirement you meet constantly: replace each space with %20 or a + in form context before you assemble the final string. The CapyToolkit encoder applies this substitution the moment you type, so a pasted phrase with spaces becomes a transmittable component instantly.
%20 and the plus sign are not the same rule
Two conventions encode a space, and confusing them causes a large share of decoding bugs. Standard percent-encoding always uses %20, which spells ASCII 32 in two uppercase hex digits because the space sits outside every unreserved character class RFC 3986 permits.7 The application/x-www-form-urlencoded serialization, born from early HTML forms, instead writes a space as +.8 and reserves %20 for other bytes. Picking the wrong convention silently rewrites a value when the decoder runs.
When a plus sign means a space
A + only means a space inside form-encoded data.8 In that context, a decoder first converts + back to a space and then processes %XX escapes. Yet decodeURIComponent follows RFC 3986, not the form format, so it leaves + untouched.5 Consequently, decoding a form-encoded query with the wrong function returns every space as a literal plus sign. The CapyToolkit decoder handles the query-parameter case for you, and ENCODE mode shows the form-encoded row separately so you can tell a real + from a space artifact at a glance.
If you need the opposite direction, encoding a space as + happens automatically when you use URLSearchParams or a form submission, because both serialize in the application/x-www-form-urlencoded format.4 That format swaps spaces for plus signs before percent-encoding the remaining reserved bytes, which is why decoding it requires a two-step process rather than a single decodeURIComponent call.
Where %20 belongs versus where + is safe
Context decides which space encoding is correct. Inside a URL path segment, only %20 is valid; a + in a path is a literal plus sign and is never read as a space under RFC 3986. Within a query string, both forms appear in the wild, because many server frameworks still accept + for historical form compatibility.
Why + behaves differently across parsers
A raw plus in a path survives as a literal plus, not a space, because only form-aware decoders know to convert + back to a space. That conversion behavior is defined specifically for application/x-www-form-urlencoded data, not for URL paths or general query strings, which is why identically encoded bytes can interpret so differently depending on context. Encoding the space as %20 removes the risk entirely and is portable across every parser, making it the safer choice when you cannot predict which decoder will read your URL.
When you are unsure which parser will read the string, %20 is the safe choice everywhere, because every RFC 3986 decoder converts it back to a space in both paths and queries. Risking + outside form data means a strict decoder, or a path parser, returns a literal plus instead of the space you expected. The difference is invisible in some contexts and catastrophic in others.
Furthermore, the WHATWG URL Standard that browsers follow encodes a query space as %20 when you build a URL through the URL object, while URLSearchParams emits + because it serializes as form data. This split explains a frequent surprise: the same space renders as %20 or + depending on which API produced the string. When you are unsure, %20 is the safer universal choice, since every compliant decoder converts it to a space in both paths and queries.
Reading a space through the tool
Watching a space round-trip cleanly is the fastest way to confirm an encoding is correct. Paste an encoded value such as search%20term into DECODE mode, and the tool returns search term immediately, with no bytes leaving your browser. Then build on that by switching to ENCODE mode and typing a phrase with spaces to compare encodeURIComponent, encodeURI, and the form-encoded output side by side. If the source came from a form submission and contains +, you will see the plus sign survive a raw component decode, which is your signal that the string used form encoding rather than standard percent-encoding. Both encodeURIComponent and encodeURI render a space as %20; only the form row uses +. That three-way comparison turns an abstract rule into something you verify with your own input in seconds.
Try in the tool
Open the URL Encoder / Decoder tool pre-filled to %20 to verify it or try a different one.
Check %20 in the tool →- 1.
"Space (character)," Wikipedia, en.wikipedia.org, accessed July 2026. https://en.wikipedia.org/wiki/Space_(character)
- 2.
IETF, "Percent-Encoding," RFC 3986 Section 2.1, datatracker.ietf.org, January 2005. https://datatracker.ietf.org/doc/html/rfc3986#section-2.1
- 3.
RFC Editor, "RFC 3986: Uniform Resource Identifier (URI): Generic Syntax," Section 2.1, rfc-editor.org, January 2005. https://www.rfc-editor.org/rfc/rfc3986.html#section-2.1
- 4.
WHATWG, "application/x-www-form-urlencoded," URL Standard, url.spec.whatwg.org, accessed July 2026. https://url.spec.whatwg.org/#application/x-www-form-urlencoded
- 5.
Mozilla Developer Network, "decodeURIComponent," developer.mozilla.org, accessed July 2026. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent
- 6.
IETF, "Request Line," RFC 7230 Section 3.1.1, datatracker.ietf.org, June 2014. https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.1
- 7.
Mozilla Developer Network, "encodeURIComponent," developer.mozilla.org, accessed July 2026. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
- 8.
WHATWG, "Form URL-encoded data," HTML Standard, html.spec.whatwg.org, accessed July 2026. https://html.spec.whatwg.org/multipage/forms.html#url-encoded-form-data
In standard percent-encoding under RFC 3986, yes. Both encodeURIComponent and encodeURI turn a space into %20. The exception is the application/x-www-form-urlencoded format used by HTML forms, which encodes a space as a plus sign instead. When you need to confirm which form a string is using, CapyToolkit shows both the %20 and the + outputs so you can verify the exact encoding your context needs. That side-by-side display makes the difference easy to confirm at a glance.
A plus sign means the string was produced by form encoding, the application/x-www-form-urlencoded format that HTML forms and many query builders use. It is a valid space representation inside a query string, but only after a form-aware decoder converts it back. In a path segment, a + stays a literal plus and is not a space.
Yes. Although encodeURI preserves structural characters like /, ?, and #, it still percent-encodes a space as %20 because a raw space is never legal in a URL. The difference between encodeURI and encodeURIComponent is how they treat delimiters, not how they treat spaces. Both convert a space to %20.
Not safely. A literal space is outside every character class RFC 3986 permits, so parsers may truncate the URL at the space or reject it. Browsers sometimes paper over the mistake by encoding it for you, but relying on that is fragile. Always encode a space to %20 before building or storing a URL.
Paste the encoded string into the CapyToolkit decoder in DECODE mode and the tool converts every %20 back to a space instantly, entirely in your browser. In JavaScript, decodeURIComponent('a%20b') returns a b. Note that this does not turn + into a space; form-encoded plus signs need a form-aware decoder.
Ampersand in a URL (%26)
An unencoded ampersand quietly splits one value into two. In a query string, the ampersand is the delimiter that separates key-value pairs, so a raw & inside a value ends the current parameter and begins a new one. The byte value of an ampersand is 0x261, giving the escape %26. RFC 3986 classifies & as a reserved sub-delimiter2, which means it carries structural meaning and must be percent-encoded whenever it appears as data. This is exactly where encodeURIComponent and encodeURI diverge: encodeURIComponent encodes & to %26, while encodeURI leaves it untouched because it assumes the ampersand is a real delimiter. Choose the wrong one and a value like Fish & Chips breaks the parameter it belongs to. The CapyToolkit encoder shows both results at once, so the difference is visible before you ship the URL.
What is %26?
%26 is the percent-encoded form of the ampersand character. An ampersand is ASCII 38, or 0x26 in hexadecimal1, so RFC 3986 percent-encoding writes it as %263. In a URL query string, an unencoded & acts as the delimiter between parameters, following the application/x-www-form-urlencoded convention that the WHATWG URL Standard also adopts4. Encoding it as %26 tells a parser to treat the ampersand as literal data inside a value rather than as a separator between two parameters.How a stray ampersand corrupts a query string
Parameter parsing depends on the ampersand as a boundary4. When a server splits a query string, it cuts the text at every & and then divides each piece at the first = into a key and a value. Consequently, a value that contains a literal ampersand produces an extra fragment the parser reads as its own parameter.
Consider ?title=Fish & Chips&id=7: a naive parser sees three parameters, title=Fish , an empty-keyed Chips, and id=7, none of which match your intent. From the parser's point of view nothing is wrong, because the ampersand did precisely its job as a delimiter. The fix is to encode the data ampersand as %26, turning the value into Fish%20%26%20Chips so it stays whole. Paste the broken query into the CapyToolkit decoder and the parameter table shows exactly how the string splits, which makes the corruption obvious.
encodeURIComponent encodes &, encodeURI does not
The ampersand is the clearest case where picking the wrong encoder matters. encodeURIComponent treats & as data and escapes it to %265. encodeURI treats & as structure and leaves it in place6, because its job is to encode a whole URL without disturbing the delimiters that hold it together, which is why the wrong choice shows up as two mirror-image failures.
Why encodeURI is wrong for values
Reach for encodeURI on a single parameter value and any ampersand inside it survives unescaped, ready to split the query the moment a parser reads it. That is the defining reason the two functions exist separately. Use encodeURIComponent for each key and value you insert into a query, and reserve encodeURI for a finished URL you only need to make safe for an HTML attribute. The CapyToolkit ENCODE panel places both outputs on adjacent rows, so you watch the ampersand vanish in one and persist in the other.
The mismatch between these two functions is easier to spot when you look at the failure modes. A value encoded with encodeURI keeps the ampersand as a delimiter, so any parser reconstructing the query from parameters will treat it as a separator even though no boundary belongs there. That is why debugging a malformed query often starts with checking which encoder produced each component.
The ampersand versus the HTML entity &
A URL ampersand and an HTML ampersand solve different problems, and mixing them creates double-escaping. Inside HTML, an ampersand starts a character reference, so an & in an href attribute is often written as the entity &. That entity belongs to HTML, not to the URL, because the browser decodes & back to a single & before the URL is ever parsed7.
Keeping the encoding layers separate
Yet developers sometimes percent-encode and HTML-encode the same ampersand, producing %26amp; or &, which decodes to visible garbage. The rule is to apply one layer at a time. Percent-encode the ampersand as %26 when it is data inside the URL, then, only if you are embedding the finished URL in HTML, let the templating layer handle entity escaping. Decoding a suspect value in the tool reveals whether an extra HTML layer has crept in.
Fixing a split parameter with the tool
Recovering the intended value is straightforward once you see the split. Paste a query string such as q=cats%26dogs&page=2 into DECODE mode, and the parameter table lists q with the value cats&dogs and page with 2, confirming the %26 kept the ampersand inside the first value. That decoded table result is the clearest evidence that the encoder preserved the exact byte value instead of letting the parser invent a second parameter.
Rebuilding an encoded query in the tool
Editing the parameter table is where the fix becomes permanent. Change any value in the table and the rebuilt query string below it re-encodes everything with encodeURIComponent, so the ampersand returns as %26 without any extra step.
Because the rebuild runs only on the edited query, you can fix multiple parameters in a single pass and watch the output update in real time without leaving the page. That behavior is difficult to duplicate by hand, since reconstructing a query from raw text requires keeping track of every %XX sequence while you type.
If instead you paste q=cats&dogs&page=2, the table shows an extra parameter, which is your signal that the ampersand was never encoded. Building on this, edit the value directly in the table and the rebuilt URL below re-encodes it with encodeURIComponent, so the ampersand returns as %26 automatically. That round trip, from broken input to corrected output, happens entirely in your browser without any request leaving the page.
Try in the tool
Open the URL Encoder / Decoder tool pre-filled to %26 to verify it or try a different one.
Check %26 in the tool →- 1.
"Ampersand," Wikipedia, en.wikipedia.org, accessed July 2026. https://en.wikipedia.org/wiki/Ampersand
- 2.
IETF, "Reserved Characters," RFC 3986 Section 2.2, datatracker.ietf.org, January 2005. https://datatracker.ietf.org/doc/html/rfc3986#section-2.2
- 3.
RFC Editor, "RFC 3986: Uniform Resource Identifier (URI): Generic Syntax," Section 2.2, rfc-editor.org, January 2005. https://www.rfc-editor.org/rfc/rfc3986.html#section-2.2
- 4.
WHATWG, "application/x-www-form-urlencoded," URL Standard, url.spec.whatwg.org, accessed July 2026. https://url.spec.whatwg.org/#application/x-www-form-urlencoded
- 5.
Mozilla Developer Network, "encodeURIComponent," developer.mozilla.org, accessed July 2026. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
- 6.
Mozilla Developer Network, "encodeURI," developer.mozilla.org, accessed July 2026. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI
- 7.
WHATWG, "Form URL-encoded data," HTML Standard, html.spec.whatwg.org, accessed July 2026. https://html.spec.whatwg.org/multipage/forms.html#url-encoded-form-data
A raw ampersand inside a parameter value is read as the delimiter that ends that parameter and starts a new one, so the value splits apart. Paste the query into CapyToolkit and the parameter table shows precisely where the string is splitting. Encode the data ampersand as %26 to keep it whole.
Use %26 when the ampersand is data inside the URL itself. Use & only as an HTML entity when you embed a URL in HTML markup, and never stack the two. The browser decodes & to & before parsing the URL, so combining both layers produces a corrupted address.
Yes. encodeURIComponent escapes & to %26 because it assumes the ampersand is part of a value rather than a delimiter. encodeURI does the opposite and leaves & in place. That is why you encode individual query values with encodeURIComponent and reserve encodeURI for a complete URL.
Percent-encode it as %26 before appending the value to the query string. In JavaScript, encodeURIComponent('Fish & Chips') yields Fish%20%26%20Chips. CapyToolkit does not require any setup: type the value in ENCODE mode and copy the encoded output, or edit it inside the decode parameter table to rebuild the query safely.
The ampersand is character 38 in ASCII, which is 0x26 in hexadecimal, so its percent-encoding is %26. Percent-encoding always spells out the underlying byte in two hex digits after the percent sign. The same scheme produces %20 for a space and %3D for an equals sign.
Forward Slash in a URL (%2F)
Encoding a forward slash lets a value survive inside a single path segment. The slash is the character that separates path segments in a URL1, so /a/b/c is three levels deep. When a value that belongs in one segment contains a slash, such as a date like 2026/07/09 or a key like folder/file, that slash would otherwise read as a new segment boundary. Percent-encoding it as %2F keeps it as literal data2. The forward slash is ASCII 47, or 0x2F3, which gives the escape %2F. Here encodeURIComponent and encodeURI part ways: encodeURIComponent escapes / to %2F4, while encodeURI preserves it as a structural delimiter5. The CapyToolkit encoder shows both, so you can confirm the slash is encoded when the value must stay inside one segment.
What is %2F?
%2F is the percent-encoded representation of the forward slash. A slash is ASCII 47, or 0x2F in hexadecimal3, so RFC 3986 percent-encoding writes it as %2F2. In a URL path, an unencoded / is a gen-delimiter that separates one path segment from the next1. Encoding it as %2F signals that the slash is data within a single segment rather than a boundary between segments. Query strings treat / more loosely and often leave it unencoded6, but inside a path the distinction matters.The slash as a path boundary
Every slash in a path is a structural signal by default. A URL path is a sequence of segments separated by /7, and the parser measures depth by counting slashes7. Consequently, a value carrying its own slash changes the shape of the path unless that slash is encoded. This is why /a/b/c reads as three separate segments rather than a single route, which is the same rule that makes a value like reports/2026 unsafe unless percent-encoded.
Take a resource keyed by reports/2026: dropped raw into a path as /files/reports/2026, it reads as two segments, reports and 2026, rather than one key. Under RFC 3986 the slash is a gen-delimiter, one of the highest-priority structural characters8, so a parser always honors it as a boundary9. From that follows the encoding step: to keep reports/2026 as a single segment, write it as reports%2F20269. The tool makes the effect concrete, since decoding %2F restores the literal slash while leaving the path depth unchanged.
Why encodeURIComponent escapes the slash
The forward slash is a textbook example of why two encoders exist. encodeURIComponent assumes it is handed a single component, so it escapes / to %2F to protect the value from being split4. encodeURI assumes it is handed a complete URL, so it leaves every / in place to preserve the path structure5.
Reading the two encoder rows side by side
Neither is wrong; each matches a different job. When you build a path from parts, encode each part with encodeURIComponent so any embedded slash becomes %2F. When you already hold a full URL and only need it safe for an attribute, encodeURI keeps the slashes that make it a valid address. The CapyToolkit ENCODE panel shows the slash escaped in the component row and intact in the encodeURI row, side by side, so the right choice is a matter of reading which output preserves your intent.
%2F in paths and the double-slash server trap
Encoding a slash correctly does not guarantee a server will accept it. Many web servers treat an encoded slash inside a path with suspicion, because %2F has historically been used to slip past path-based access controls. That legacy is why the infrastructure surrounding a standard-compliant URL can still display a mismatch between what the specification allows and what the server permits.
Servers that reject %2F by default
Apache refuses a request containing %2F in the path unless AllowEncodedSlashes is enabled, returning a 404 by default10. Tomcat blocks encoded slashes for the same reason11, and reverse proxies often normalize or reject them. Consequently, a value that is technically correct may still fail in production because the infrastructure, not the standard, forbids it. When a path-embedded slash is unavoidable, the common workaround is to carry the value in a query parameter, where %2F is accepted freely. The decoder helps you confirm what the encoded path actually contains before you start blaming the application code.
What matters at the application layer is whether the server forwards the path as the standard intended or rewrites it before the request reaches routing. A path that is correct under RFC 3986 can arrive truncated or normalized depending on server configuration, so testing the actual endpoint before encoding client-side logic is the only reliable confirmation.
Checking a segment value in the tool
Confirming a slash-bearing value round-trips is quick. Paste folder%2Ffile into DECODE mode and the tool returns folder/file, showing the %2F preserved the slash as data. If you paste a full path like /a/b/c, no decoding changes it, because those slashes are structural and were never encoded. That contrast is readable on the same screen as the ENCODE mode output, where entering a slash-valued string shows encodeURIComponent escaping it while encodeURI preserves it, which is the fastest way to confirm which behavior you need.
Watching both encoders update together
Building on this, switch to ENCODE mode and enter a value with a slash to watch encodeURIComponent produce %2F while encodeURI leaves the slash alone. That contrast, visible in one screen, tells you immediately which function suits the string you hold. Every conversion runs locally in your browser, so even a path that embeds an internal object key or a customer identifier stays on your machine.
Try in the tool
Open the URL Encoder / Decoder tool pre-filled to %2F to verify it or try a different one.
Check %2F in the tool →- 1.
IETF, "Reserved Characters," RFC 3986 Section 2.2, datatracker.ietf.org, January 2005. https://datatracker.ietf.org/doc/html/rfc3986#section-2.2
- 2.
RFC Editor, "RFC 3986: Uniform Resource Identifier (URI): Generic Syntax," Section 3.3, rfc-editor.org, January 2005. https://www.rfc-editor.org/rfc/rfc3986.html#section-3.3
- 3.
ASCII-Code.com, "Forward Slash Character (/)," ascii-code.com, accessed July 2026. https://www.ascii-code.com/character/slash
- 4.
Mozilla Developer Network, "encodeURIComponent()," developer.mozilla.org, accessed July 2026. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
- 5.
Mozilla Developer Network, "encodeURI()," developer.mozilla.org, accessed July 2026. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI
- 6.
IETF, "Query Component," RFC 3986 Section 3.4, datatracker.ietf.org, January 2005. https://datatracker.ietf.org/doc/html/rfc3986#section-3.4
- 7.
WHATWG, "URL Path Segment String," URL Standard, url.spec.whatwg.org, accessed July 2026. https://url.spec.whatwg.org/#url-path-segment-string
- 8.
Wikipedia, "Uniform Resource Identifier — Syntax," en.wikipedia.org, accessed July 2026. https://en.wikipedia.org/wiki/Uniform_Resource_Identifier#Syntax
- 9.
Wikipedia, "Uniform Resource Locator — Path," en.wikipedia.org, accessed July 2026. https://en.wikipedia.org/wiki/URL#Path
- 10.
Apache HTTP Server Project, "AllowEncodedSlashes Directive," httpd.apache.org, accessed July 2026. https://httpd.apache.org/docs/current/mod/core.html#allowencodedslashes
- 11.
Apache Tomcat Project, "The HTTP Connector — encodedSolidusHandling," tomcat.apache.org, accessed July 2026. https://tomcat.apache.org/tomcat-10.1-doc/config/http.html#encodedSolidusHandling
Encode a slash as %2F when it is data that must stay inside a single path segment, such as a date or a hierarchical key. Leave slashes unencoded when they are genuine segment separators. In a query string, a slash is usually safe unencoded, but %2F is always accepted there too.
Several servers block encoded slashes in the path by default for security reasons. Apache needs AllowEncodedSlashes On, and Tomcat has a similar guard. The standard permits %2F, but the server refuses it. Moving the value into a query parameter usually avoids the restriction while keeping the slash intact.
No. encodeURI preserves / because it treats the input as a complete URL whose slashes are structural. Only encodeURIComponent escapes / to %2F, since it assumes the input is a single value. That difference is why you encode path parts individually with encodeURIComponent.
After decoding, yes: %2F becomes a literal /. Before decoding they differ in meaning. A raw / is a segment separator that changes the path structure, while %2F is data that a parser keeps inside one segment. CapyToolkit shows the decoded value so you can confirm which one you have.
Percent-encode it as %2F before assembling the path. In JavaScript, encodeURIComponent('a/b') returns a%2Fb. Paste the value into CapyToolkit ENCODE mode and grab the encodeURIComponent row, which returns the escaped form ready to insert. Be aware that some servers still reject an encoded slash in the path, so test the endpoint before relying on it.
Plus Sign in a URL (%2B)
The plus sign carries two meanings in a URL, and that ambiguity is the whole reason to encode it. In the application/x-www-form-urlencoded format, a + stands for a space1. Wherever that format is decoded, a literal plus you meant as data becomes a space, silently rewriting your value. RFC 3986 resolves the conflict by treating + as a reserved character that must be percent-encoded when it is data2, producing %2B from byte value 0x2B3. The two JavaScript encoders disagree here: encodeURIComponent escapes + to %2B4, while encodeURI leaves it in place5. Consequently, a phone number like +15551234567 or a base64 token ending in + can arrive corrupted if it was never encoded to %2B. The CapyToolkit encoder shows the component, encodeURI, and form outputs together, so you can see when a plus needs protecting.
What is %2B?
%2B is the percent-encoded form of the plus sign. A plus is ASCII 43, or 0x2B in hexadecimal3, so RFC 3986 percent-encoding writes it as %2B6. The plus sign is special because the application/x-www-form-urlencoded format uses a bare + to represent a space7. Encoding a literal plus as %2B removes that ambiguity6, guaranteeing a form-aware decoder returns a plus sign rather than a space. Inside a path a raw + is already literal5, but encoding it as %2B remains the most portable choice.The plus sign means space in form data
The plus sign inherited a second job from the earliest web forms. Before modern percent-encoding settled, the application/x-www-form-urlencoded format chose + as a compact stand-in for a space1, and that choice never went away. Consequently, any decoder built for form data converts every bare + back into a space before it does anything else.
Why form decoding treats + as a space
When a browser submits a form or a library serializes parameters as form data, a space in your input becomes a + on the wire7. The reverse also holds, so a + on the wire becomes a space on the way back. From this two-way rule comes the hazard, because a plus you meant literally is indistinguishable from a plus that encodes a space unless you wrote it as %2B. The tool shows the form-encoded row explicitly, so this substitution is never a surprise when you compare outputs.
Where a literal plus gets corrupted
A handful of everyday values contain a plus and break in exactly this way. International phone numbers begin with a +, standard base64 alphabets include +8, and arithmetic expressions carry it as an operator. These two everyday examples break in production because they travel through URLs that are otherwise safe, which is why the corruption usually goes unnoticed until an outage points back to the query string.
Base64 in a query string
Base64 is the classic casualty. Its standard alphabet uses + and /8, so a token dropped raw into a query can lose its plus signs to space conversion and its slashes to path confusion, corrupting the value beyond recovery. Encoding the token with encodeURIComponent turns + into %2B and / into %2F4, preserving every byte. Alternatively, many systems adopt the base64url alphabet, which swaps + and / for - and _ precisely to survive URLs unencoded9. When you must carry standard base64, encode it; the decoder lets you verify the token returns byte-for-byte identical.
But the base64url switch is not always an option when you must interoperate with a legacy system that expects standard base64. In that case encodeURIComponent is the safer path on the encoding side, and the decoder confirms the original token shape once the value returns. That round-trip is what makes the tool useful for debugging an access token or a client secret dropped into a REST header.
encodeURIComponent protects the plus
Protecting a literal plus comes down to choosing the encoder that treats it as data. encodeURIComponent escapes + to %2B, so the value survives a form-aware decode as a plus. encodeURI leaves + untouched, because within a complete URL it assumes the plus already sits where it belongs and does not need protection.
For a single value destined for a query string, that assumption is dangerous. Use encodeURIComponent whenever the value might contain a plus you intend literally, and confirm the output shows %2B9. The CapyToolkit ENCODE panel makes the check trivial: the component row escapes the plus, the encodeURI row keeps it, and the form row shows how a space would have produced a + in the first place. Reading the three rows together tells you at a glance whether your value is safe to append.
Spotting a plus-for-space bug in the tool
A plus-for-space bug is easy to reproduce and easy to spot. Paste q=1+2 into DECODE mode: a raw component decode leaves the + in place, yet you know a form parser would read it as 1 2, which is exactly the ambiguity you are hunting when a query string moves between raw and form-aware contexts.
Confirming the fix with %2B
Now paste q=1%2B2 and the value decodes to 1+2 unambiguously, because the %2B fixed the meaning. Building on this, the parameter table lets you edit a value and rebuild the query with encodeURIComponent, so any plus you type returns as %2B in the output. Because every step runs in your browser, you can test a real access token or a customer phone number without sending it to a server anywhere.
Try in the tool
Open the URL Encoder / Decoder tool pre-filled to %2B to verify it or try a different one.
Check %2B in the tool →- 1.
WHATWG, "application/x-www-form-urlencoded," URL Standard, url.spec.whatwg.org, accessed July 2026. https://url.spec.whatwg.org/#application/x-www-form-urlencoded
- 2.
IETF, "Reserved Characters," RFC 3986 Section 2.2, datatracker.ietf.org, January 2005. https://datatracker.ietf.org/doc/html/rfc3986#section-2.2
- 3.
ASCII-Code.com, "Plus Sign (+)," ascii-code.com, accessed July 2026. https://www.ascii-code.com/character/plus
- 4.
Mozilla Developer Network, "encodeURIComponent()," developer.mozilla.org, accessed July 2026. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
- 5.
Mozilla Developer Network, "encodeURI()," developer.mozilla.org, accessed July 2026. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI
- 6.
RFC Editor, "RFC 3986: Uniform Resource Identifier (URI): Generic Syntax," Section 2.1, rfc-editor.org, January 2005. https://www.rfc-editor.org/rfc/rfc3986.html#section-2.1
- 7.
HTML Standard, "Form URL-encoded data," html.spec.whatwg.org, accessed July 2026. https://html.spec.whatwg.org/multipage/forms.html#url-encoded-form-data
- 8.
Wikipedia, "Base64," en.wikipedia.org, accessed July 2026. https://en.wikipedia.org/wiki/Base64
- 9.
RFC Editor, "RFC 4648: The Base16, Base32, and Base64 Data Encodings," rfc-editor.org, October 2006. https://www.rfc-editor.org/rfc/rfc4648.html
The value passed through a form-encoding decoder, which reads a bare + as a space under the application/x-www-form-urlencoded rules. CapyToolkit shows the form-encoded row separately so the difference is visible before you copy anything. To keep a literal plus, encode it as %2B before building the URL, which removes any ambiguity between a sign and a space.
Percent-encode it as %2B. In JavaScript, encodeURIComponent('+1') returns %2B1. Type the value into CapyToolkit ENCODE mode and copy the encodeURIComponent row, which escapes the plus. Avoid encodeURI for this, since it leaves the plus in place and lets a form decoder turn it into a space.
Yes. encodeURIComponent escapes + to %2B because it treats the input as data. encodeURI leaves + unchanged. That is why you encode individual query values with encodeURIComponent, especially values like phone numbers or base64 tokens that legitimately contain a plus.
Standard base64 uses + and /, both of which have special meaning in URLs. Unencoded, the + can decode to a space and the / can confuse path parsing. Encode the token with encodeURIComponent, or use the base64url alphabet that replaces + and / with - and _ to travel safely.
A raw + in a path is already a literal plus, so encoding is not strictly required there. The danger lives in query strings and form data, where a bare + means a space. Encoding to %2B everywhere is the safest habit, since it removes ambiguity regardless of which parser reads the URL.
Percent Sign in a URL (%25)
Type 100% into a URL without encoding it and a decoder throws an error. The percent sign is the one character percent-encoding cannot leave alone, because % is the escape marker itself: every % must be followed by two hexadecimal digits that name a byte1. A literal percent that is not encoded looks like the start of an escape, so 100% reads as 100 plus a broken sequence, and decodeURIComponent raises a URIError2. The fix is to encode the percent sign as %25, from its byte value 0x253. Because the percent sign is also what a second encoding pass targets, it sits at the center of most double-encoding bugs4. The CapyToolkit decoder flags an invalid sequence instead of failing silently, so you can find the bare percent fast.
What is %25?
%25 is the percent-encoded representation of a literal percent sign. A percent sign is ASCII 37, or 0x25 in hexadecimal3, so RFC 3986 percent-encoding writes it as %251. The percent sign is unique because it introduces every escape sequence: a decoder reads % and expects two hex digits to follow5. Encoding a literal percent as %25 stops a decoder from mistaking your data for an escape. It is also the character a second encoding pass acts on, which makes %25 the fingerprint of double-encoded data.The percent sign is the escape character
Percent-encoding uses one character to announce all the others6. When a decoder scans a string, a % tells it to stop reading literal text and to interpret the next two characters as a hexadecimal byte6. Consequently, the percent sign can never stand for itself unless it is escaped, or the decoder would confuse real data with an escape sequence.
The introducer rule and its consequences
RFC 3986 makes this explicit: % is reserved as the escape introducer, and a literal percent must be written as %255. From that single rule flows a large family of bugs, because any unescaped percent in user input, a filename like 50%off.pdf or a value like 100%, sabotages the decode. Encoding the percent first, before appending anything else, keeps the sequence well-formed. The tool escapes it to %25 for you the instant you type a percent in ENCODE mode.
Why decoding throws an invalid percent-encoding error
A bare percent is the most common trigger for a decode failure. decodeURIComponent follows RFC 3986 strictly, so it refuses any % that is not followed by two valid hex digits2. That refusal happens before the decoder looks at the characters around it, which is why positioning or extra context does not help: the input is rejected at the parsing level the moment an escape sequence is malformed.
What the invalid-encoding error means
When the CapyToolkit decoder shows Invalid percent-encoding, it caught a malformed sequence: a lone %, a % trailed by non-hex characters like %zz, or a truncated escape such as %2 at the end of the string. Each of these makes native decodeURIComponent throw a URIError, which the tool turns into a readable message instead of a blank result. Consequently, the error is a diagnosis rather than a dead end. Look for a percent that should have been %25, or an escape that lost a digit during copying, and the fix usually clears the whole string.
The error is local to one escape sequence, so the rest of the string may remain readable once you delete or fix the offending character. That bounded failure mode is why repairing a malformed URL is usually a matter of finding the stray percent rather than rewriting the entire string.
%25 and the double-encoding fingerprint
A string encoded twice announces itself through the percent sign4. Encoding replaces each % with %25, so a value that already contained an escape like %20 becomes %2520 after a second pass. Furthermore, that %25 prefix is the tell you look for, because no legitimate single encoding produces a literal %25 in the middle of an otherwise normal string.
Recovering the original value
Seeing %2520 where you expected %20, or %253A where you expected %3A, means the data was encoded one time too many. Decoding it once yields %20, still encoded, rather than the space you wanted. The remedy is to decode repeatedly until the output stops changing, then encode exactly once. Paste a suspicious value into the CapyToolkit decoder and decode in stages; if the first pass returns another percent-escaped string, you have found a double-encoding, and a second pass recovers the original.
Catching a bare percent in the tool
The tool turns an abstract error into a located problem. Paste a value containing a bare percent, such as discount=100%, into DECODE mode, and the error bar reports invalid percent-encoding rather than returning a misleading partial result. That surfaced message is faster than copying the string into a terminal and parsing a stack trace, which is what makes the tool useful when you are debugging a malformed API call buried in logs.
Correct the input to discount=100%25 and the value decodes to 100%, confirming the fix. Building on this, if a value decodes to something that still looks escaped, run it through DECODE mode again to test for double-encoding. Because the decoder never sends your input anywhere, you can safely probe production URLs, redirect chains, or logged request lines that carry sensitive parameters while you track down the stray percent.
Try in the tool
Open the URL Encoder / Decoder tool pre-filled to %25 to verify it or try a different one.
Check %25 in the tool →- 1.
RFC Editor, "RFC 3986: Uniform Resource Identifier (URI): Generic Syntax," Section 2.1, rfc-editor.org, January 2005. https://www.rfc-editor.org/rfc/rfc3986.html#section-2.1
- 2.
Mozilla Developer Network, "decodeURIComponent()," developer.mozilla.org, accessed July 2026. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent
- 3.
ASCII-Code.com, "ASCII Code 37 - Per cent sign," ascii-code.com, accessed July 2026. https://www.ascii-code.com/37
- 4.
WHATWG, "application/x-www-form-urlencoded," URL Standard, url.spec.whatwg.org, accessed July 2026. https://url.spec.whatwg.org/#application/x-www-form-urlencoded
- 5.
IETF, "Reserved Characters," RFC 3986 Section 2.2, datatracker.ietf.org, January 2005. https://datatracker.ietf.org/doc/html/rfc3986#section-2.2
- 6.
Wikipedia, "Percent-encoding," en.wikipedia.org, accessed July 2026. https://en.wikipedia.org/wiki/Percent-encoding
A percent sign in your input is not followed by two valid hex digits, so the decoder cannot interpret it. CapyToolkit reports the error instead of returning a broken partial result, which makes the malformed spot easy to find. Common causes are a bare % that should be %25, a truncated escape like %2, or non-hex characters after the percent.
Encode it as %25. In JavaScript, encodeURIComponent('100%') returns 100%25. Encoding the percent first is essential, because leaving it raw makes any following characters look like part of a broken escape sequence.
It is a space that was encoded twice. The first pass turned the space into %20, and the second pass encoded that percent sign into %25, producing %2520. It signals double-encoding. Decode the value once to get %20, then decode again to recover the space, and fix the code that encoded it twice.
Yes. Both encodeURIComponent and encodeURI escape a literal % to %25, because an unescaped percent would corrupt the string. This is also why running an already-encoded value through an encoder again produces %25 sequences, the classic marker of an accidental double-encoding.
Not with a strict decoder. A lone % makes decodeURIComponent throw, and CapyToolkit surfaces that as an invalid-encoding message. Replace the bare percent with %25, or fix the truncated escape, then decode again. The tool pinpoints the failure so you know the input is malformed rather than the decoder.
Hash Sign in a URL (%23)
The hash sign marks where the URL fragment begins1. Everything after a # is the fragment identifier, a client-side pointer the browser uses to scroll to an element or route a single-page app, and it is never sent to the server. RFC 3986 defines # as a gen-delimiter that separates the fragment from the rest of the URL2, so a literal hash inside a value truncates the address: the parser treats everything after the first # as the fragment.
The byte value of a hash is 0x233, giving the escape %23. As with other delimiters, encodeURIComponent escapes # to %234 while encodeURI leaves it in place5. Consequently, a value like a color code #ff8800 or a hashtag can silently cut a URL short. The CapyToolkit encoder shows both outputs so you can protect a hash that belongs to your data.
What is %23?
%23 is the percent-encoded representation of the hash sign, also called the pound sign or number sign. A hash is ASCII 35, or 0x23 in hexadecimal3, so RFC 3986 percent-encoding writes it as %231. In a URL, an unencoded # is a gen-delimiter that begins the fragment identifier, the portion after the hash that browsers handle locally and never transmit to the server2. Encoding a literal hash as %23 keeps it as data inside a path or query rather than starting a fragment.The hash begins the fragment
A hash divides a URL into two very different halves6. To the left of the first # lies the part the browser sends to the server: scheme, host, path, and query. To the right lies the fragment, which the browser keeps to itself for scrolling to an anchor or driving client-side routing.
Why everything after # never reaches the server
Consequently, the fragment never appears in server logs or request handlers, a property that matters when you reason about what data actually reaches your backend. RFC 3986 fixes the hash as the boundary between these halves6, so the first unencoded # always ends the server-visible portion. From that structural role comes the encoding rule: a hash that is part of your data, rather than a fragment marker, must be written as %23. Decoding a URL in the tool shows the hash restored as literal text, letting you see where data ends and a fragment would begin.
How a stray hash truncates a URL
Several common values contain a hash and lose everything after it. A CSS color like #ff8800, a social hashtag, or an issue reference like #4213 all carry a literal hash. The failure is silent: the URL is still sent, but the server receives only what came before the first #, which is why the data loss is easy to miss until a query parameter shows up empty or an analytics link records no destination at all. Masking the problem further, the truncated URL is still syntactically valid, so browsers and servers alike send a 200 response instead of flagging the missing value as an error.
A color code that cuts the URL short
Picture a query built as ?color=#ff8800&size=lg. The parser reads color= and then hits the #, treating ff8800&size=lg as the fragment, so the server sees only color= with an empty value and never receives the size. Nothing errors; the URL is perfectly valid, just not what you meant. Encoding the hash as %23 produces ?color=%23ff8800&size=lg, which keeps the color and the size in the query where the server can read them. The decoder makes the failure visible, because the parameter table shows the truncated value the moment you paste the raw URL.
The same truncation appears in any URL that carries a literal hash, from a routing identifier in an API link to a hashtag in a tracking parameter. Because the fragment boundary is a rule the browser enforces before a request ever leaves the client, no amount of server-side validation can recover data that never arrived. Encoding guarantees the server receives the full value regardless of where the hash appears, so testing the finished URL is only useful after the value has already been protected with %23.
encodeURIComponent versus encodeURI for the hash
The hash follows the same split as the other delimiters between the two encoders. encodeURIComponent escapes # to %234, protecting a value that contains one. encodeURI leaves # in place5, because within a complete URL it assumes the hash is a genuine fragment marker that belongs where it is and does not need encoding.
For a single value destined for a query or path segment, that assumption truncates your data without warning. Use encodeURIComponent on any key or value that might carry a hash, and confirm the output shows %23. The CapyToolkit ENCODE panel prints both, so the escaped hash in the component row and the untouched hash in the encodeURI row sit next to each other. Reading them together removes the guesswork about which function preserves your value and which one hands part of it to the fragment.
Confirming a hash value in the tool
Verifying a hash-bearing value takes one paste. Enter label=%23urgent&id=9 in DECODE mode, and the parameter table shows label holding #urgent and id holding 9, proving the %23 kept the hash inside the value. That side-by-side proof is faster than guessing or reconstructing the URL by hand, because the table shows immediately what the parser kept and what it dropped.
Rebuilding a truncated URL
Paste the unencoded label=#urgent&id=9 instead and the table reveals only label with an empty value, because everything after the hash became a fragment. Building on this, edit the value in the table and the rebuilt query re-encodes the hash as %23, restoring a well-formed URL. Since the tool runs entirely in your browser, you can inspect internal links, analytics URLs, or deep links that carry a literal hash without any of them leaving the page.
Try in the tool
Open the URL Encoder / Decoder tool pre-filled to %23 to verify it or try a different one.
Check %23 in the tool →- 1.
IETF, "Fragment," RFC 3986 Section 3.5, datatracker.ietf.org, January 2005. https://datatracker.ietf.org/doc/html/rfc3986#section-3.5
- 2.
IETF, "Reserved Characters," RFC 3986 Section 2.2, datatracker.ietf.org, January 2005. https://datatracker.ietf.org/doc/html/rfc3986#section-2.2
- 3.
ASCII-Code.com, "ASCII Code 35 - Number sign," ascii-code.com, accessed July 2026. https://www.ascii-code.com/35
- 4.
Mozilla Developer Network, "encodeURIComponent()," developer.mozilla.org, accessed July 2026. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
- 5.
Mozilla Developer Network, "encodeURI()," developer.mozilla.org, accessed July 2026. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI
- 6.
RFC Editor, "RFC 3986: Uniform Resource Identifier (URI): Generic Syntax," Section 3.5, rfc-editor.org, January 2005. https://www.rfc-editor.org/rfc/rfc3986.html#section-3.5
The first unencoded # starts the fragment, and the fragment is never sent to the server. CapyToolkit shows the truncation directly in its parameter table the moment you paste the raw URL, so the missing data is obvious right away. Any data after a literal hash is dropped from the request, so encode the hash as %23 to keep the value in the path or query.
Percent-encode it as %23. In JavaScript, encodeURIComponent('#ff8800') returns %23ff8800. Avoid encodeURI here, since it leaves the hash in place and lets it start a fragment.
No. Everything after the # stays in the browser and is used for scrolling to anchors or client-side routing. It does not appear in server logs or request handlers. This is why a stray hash that starts a fragment silently removes the rest of your data from what the backend receives.
No. encodeURI leaves # in place because it treats the input as a complete URL whose hash marks the fragment. Only encodeURIComponent escapes # to %23. That is why you encode individual values that may contain a hash with encodeURIComponent rather than encodeURI.
It represents a literal hash, pound, or number sign inside URL data, so the character is not read as the start of a fragment. Common cases include CSS color codes, hashtags, and issue numbers. CapyToolkit encodes a hash to %23 and decodes it back, letting you confirm the value round-trips correctly.
Question Mark in a URL (%3F)
A question mark in the wrong place turns part of your path into a query. The ? is the delimiter that separates the path from the query string, so the first unencoded question mark in a URL marks where parameters begin1. RFC 3986 defines ? as a gen-delimiter for exactly this purpose2. Consequently, a literal question mark inside a path segment, such as a filename like faq?.html or a search phrase, splits the URL at the wrong point and hands the remainder to the query parser. The byte value of a question mark is 0x3F3, which gives the escape %3F. encodeURIComponent escapes ? to %3F4, while encodeURI preserves it as structure5. The CapyToolkit encoder shows both, so you can keep a question mark inside your data when it is not meant to open a query string.
What is %3F?
%3F is the percent-encoded representation of the question mark. A question mark is ASCII 63, or 0x3F in hexadecimal3, so RFC 3986 percent-encoding writes it as %3F1. In a URL, the first unencoded ? is a gen-delimiter that separates the path from the query string. Encoding a literal question mark as %3F keeps it as data within a path segment rather than starting the query. Once the query has begun, additional question marks are permitted literally, but encoding remains the most portable choice.The question mark opens the query string
A URL splits into path and query at a single character6. The first ? a parser meets ends the path and begins the query string, where key-value pairs live6. Consequently, a question mark that belongs to your data, rather than to the URL structure, changes where that split happens in a way that is invisible unless you inspect the resulting query.
Why a misplaced ? breaks resources without erroring
Imagine a path segment /help/what?now: the parser reads /help/what as the path and treats now as the start of a query, even though you meant the whole thing as one label. Under RFC 3986 the question mark is a gen-delimiter2, so a parser always honors the first one as the path-query boundary. From that follows the fix: encode a data question mark as %3F7, giving /help/what%3Fnow, which keeps the label whole. Decoding the value in the tool restores the literal ? while leaving the URL structure intact.
Only the first question mark is the delimiter
Not every question mark in a URL is a delimiter, and the distinction trips people up when a value contains one that looks like structure. A ? inside a search term or resource label is just punctuation until the first delimiter establishes context, but assuming that context without checking leads to a misread boundary. The WHATWG URL Standard defines the query as running from the first ? to the end of the URL or the next #7, and within that span the question mark is a legal literal character. That exception is useful in practice: it means a question mark inside a value does not reopen the boundary debate, provided the first ? has already settled it.
Why a second ? is usually fine
Once the query has opened, a further ? inside a value does not restart anything, because there is only one query section. So ?q=a?b is a valid query where the value of q is a?b in most parsers. Yet relying on that tolerance is risky, since some frameworks and proxies still choke on a raw question mark in a value. Encoding it as %3F is unambiguous everywhere. The CapyToolkit decoder shows how a given parser splits the string, so you can confirm whether a bare ? is read as data or as structure.
That distinction matters for debugging search URLs and tracking links, where a literal question mark inside a query value can be misread as a secondary delimiter unless you inspect the parsed result carefully. In analytics links, for example, a ? inside a campaign parameter can split the query unexpectedly if the tracking template does not encode it first, which makes encoded values the safer default whenever a literal question mark appears in user-supplied data.
encodeURIComponent escapes the question mark
The question mark sorts into the same two-encoder split as the other delimiters. encodeURIComponent escapes ? to %3F4, treating it as data no matter where it appears in the string. encodeURI leaves ? untouched5, because in a full URL it assumes the question mark opens the query and any others are secondary.
For a single path segment or a value, that assumption misplaces the boundary without warning, because the URL is still valid and only the meaning changes. Reach for encodeURIComponent whenever a value might contain a question mark you intend literally, and verify the output shows %3F. The CapyToolkit ENCODE panel prints the escaped form in the component row and the untouched form in the encodeURI row. Comparing the two makes it obvious which output keeps your data inside the segment where it belongs and which one would start a query prematurely.
Keeping a question mark as data in the tool
Testing a question mark value is a quick round trip. Paste /search/why%3Fnot into DECODE mode and the tool returns /search/why?not, showing the %3F preserved the question mark as part of the segment. Paste /search/why?not instead and any query parsing treats not as the start of parameters, which is why the difference is visible in the decoded output before you edit anything.
Comparing both encoder outputs
Building on this, switch to ENCODE mode and type a value containing a question mark to watch encodeURIComponent produce %3F while encodeURI leaves it alone. That side-by-side view tells you which function suits the string in hand. Because the tool works entirely in your browser, you can check internal search URLs or help-center links that embed punctuation without transmitting any of them to a server.
Try in the tool
Open the URL Encoder / Decoder tool pre-filled to %3F to verify it or try a different one.
Check %3F in the tool →- 1.
IETF, "Query Component," RFC 3986 Section 3.4, datatracker.ietf.org, January 2005. https://datatracker.ietf.org/doc/html/rfc3986#section-3.4
- 2.
IETF, "Reserved Characters," RFC 3986 Section 2.2, datatracker.ietf.org, January 2005. https://datatracker.ietf.org/doc/html/rfc3986#section-2.2
- 3.
ASCII-Code.com, "ASCII Code 63 - Question mark," ascii-code.com, accessed July 2026. https://www.ascii-code.com/63
- 4.
Mozilla Developer Network, "encodeURIComponent()," developer.mozilla.org, accessed July 2026. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
- 5.
Mozilla Developer Network, "encodeURI()," developer.mozilla.org, accessed July 2026. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI
- 6.
RFC Editor, "RFC 3986: Uniform Resource Identifier (URI): Generic Syntax," Section 3.3, rfc-editor.org, January 2005. https://www.rfc-editor.org/rfc/rfc3986.html#section-3.3
- 7.
WHATWG, "URL Parsing," URL Standard, url.spec.whatwg.org, accessed July 2026. https://url.spec.whatwg.org/#url-parsing
The first unencoded ? ends the path and starts the query string, so anything after it is parsed as parameters. CapyToolkit decodes the URL and shows the parameter table, so you can see exactly where the split happened. A question mark inside a path segment must be encoded as %3F to stay part of the path.
Strictly, no. RFC 3986 allows a literal ? after the query has begun, so ?q=a?b is valid in most parsers. In practice, some frameworks and proxies mishandle it, so encoding it as %3F is safer. Testing how a value decodes before you commit to leaving a bare question mark is the only reliable confirmation.
Encode it as %3F. In JavaScript, encodeURIComponent('why?') returns why%3F. Type the value into CapyToolkit ENCODE mode and copy the encodeURIComponent row. Use this for any question mark that is data rather than the delimiter that opens the query string.
No. encodeURI leaves ? in place because it treats the input as a complete URL whose question mark opens the query. Only encodeURIComponent escapes ? to %3F. That is why you encode individual path parts and values with encodeURIComponent rather than encodeURI.
The first unencoded question mark. Everything before it is the path, and everything after it, up to a # fragment, is the query string. Because that single character defines the boundary, a literal question mark meant as data has to be encoded as %3F so it does not move the split.
At Sign in a URL (%40)
Encoding an at sign matters most when you put an email address in a URL. The @ has a structural role in the authority component: in user:pass@host, it separates the optional userinfo from the hostname. RFC 3986 defines @ as a gen-delimiter that ends the userinfo section, so a literal at sign in a username or password must be encoded, or the parser reads the wrong host1. The byte value of an at sign is 0x40, giving the escape %40. In query strings the at sign is more relaxed and often survives unencoded, yet %40 is always accepted and removes any doubt2. encodeURIComponent escapes @ to %40, while encodeURI leaves it in place. The CapyToolkit encoder shows both, so you can encode an email address safely wherever it appears.
What is %40?
%40 is the percent-encoded representation of the at sign. An at sign is ASCII 64, or 0x40 in hexadecimal, so RFC 3986 percent-encoding writes it as %40. In a URL authority, an unencoded @ separates the userinfo, such as user:pass, from the host that follows1. Encoding a literal at sign as %40 keeps it as data rather than as that separator, which is essential when an email address or a credential contains one. Query strings tolerate a bare @ in most parsers, but %40 is the portable choice.The at sign separates userinfo from host
The at sign controls where the hostname of a URL actually starts. In the authority component, anything before an @ is userinfo, and the real host begins after it. Consequently, an at sign placed carelessly can point a URL at a host you did not intend. Encoding that literal at sign as %40 removes the delimiter meaning from the character, which is exactly what percent-encoding does when a structural byte turns into data.
Phishing and the userinfo trick
This is the mechanism behind a classic phishing trick: https://[email protected] looks like it leads to trusted.com, but the browser reads trusted.com as a username and connects to evil.com. RFC 3986 makes the @ the delimiter that ends userinfo, so a literal at sign in a credential must be encoded as %40 to avoid confusing the parser3. From this follows a practical habit: encode any at sign that is data, not structure. Decoding a suspicious URL in the tool reveals the true host by showing exactly where the userinfo ends4.
Email addresses in query strings and mailto links
The most frequent place an at sign appears as data is an email address. Whether you pass an address as a query parameter or build a mailto: link, the @ between the local part and the domain is content, not structure. Both embedding formats treat the at sign as ordinary data, not as a structural delimiter, which is why percent-encoding it as %40 is the safest choice.
Passing an email as a parameter
Consider [email protected]. Most query parsers accept the bare @ and return [email protected] intact, because the query grammar permits it. Yet encoding the address as ?email=user%40example.com is safer, since it survives stricter parsers, proxies, and any later re-parsing without ambiguity. Furthermore, when the email travels through a redirect or is nested inside another URL, the extra encoding stops the at sign from being misread as an authority delimiter.
The CapyToolkit encoder turns [email protected] into user%40example.com in the component row, and the decoder confirms it round-trips back to the original address. This round-trip check is useful when you are building a URL from user input and need to verify the encoded value will not be mangled by an intermediate parser or transport layer.
encodeURIComponent versus encodeURI for the at sign
The at sign lands on the familiar two-encoder divide. encodeURIComponent escapes @ to %40, treating it as data. encodeURI leaves @ in place, because in a complete URL it assumes the at sign is a legitimate authority delimiter2. Neither function is wrong in isolation, they just apply different rules depending on whether the input is a complete URL or a single component whose value happens to contain an at sign.
Choosing the safer default for email values
For a single value such as an email address, encoding is the safer default. Use encodeURIComponent when the at sign is content, and check that the output shows %40. The CapyToolkit ENCODE panel displays the escaped form in the component row beside the untouched form in the encodeURI row. Seeing both together makes clear which output treats the at sign as part of your value rather than as a piece of URL structure that decides the host.
Encoding an email value in the tool
Confirming an email survives encoding takes a single round trip. Paste email=user%40example.com into DECODE mode and the parameter table shows email holding [email protected], verifying the %40 preserved the address. Switch to ENCODE mode, type an address, and the component row returns it with %40 in place of the at sign, ready to append to a query.
Building on this, you can edit an email value directly in the decode parameter table and rebuild the query with the at sign safely encoded. Because the tool processes everything in your browser, you can encode real customer email addresses or credentials without sending them to any server, which is exactly what you want when the value is personal data.
Try in the tool
Open the URL Encoder / Decoder tool pre-filled to %40 to verify it or try a different one.
Check %40 in the tool →- 1.
IETF, "Uniform Resource Identifier (URI): Generic Syntax," RFC 3986, datatracker.ietf.org, January 2005. https://datatracker.ietf.org/doc/html/rfc3986
- 2.
Mozilla Developer Network, "encodeURIComponent()," developer.mozilla.org, accessed July 2026. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
- 3.
OWASP Foundation, "Userinfo Abuse," owasp.org, accessed July 2026. https://owasp.org/www-community/attacks/open_redirect
- 4.
WHATWG, "URL Standard," url.spec.whatwg.org, accessed July 2026. https://url.spec.whatwg.org/
In a query string, most parsers accept a bare @, so it often works unencoded. Encoding it as %40 is safer, because it survives strict parsers, proxies, and nested URLs without ambiguity. In the authority part of a URL, an at sign is a delimiter, so a literal one there must always be encoded.
Percent-encode it as %40. In JavaScript, encodeURIComponent('[email protected]') returns user%40example.com. Type the address into CapyToolkit ENCODE mode and copy the encodeURIComponent row. The tool runs locally, so you can encode real email addresses without transmitting them anywhere.
Everything before an @ in the authority is treated as userinfo, so https://[email protected] connects to evil.com and reads trusted.com as a username. Attackers use this to disguise links. Decoding the URL in CapyToolkit shows where the userinfo ends and the real host begins.
No. encodeURI leaves @ in place because it treats the at sign as a valid authority delimiter within a complete URL. Only encodeURIComponent escapes @ to %40. That is why you encode a single value like an email address with encodeURIComponent rather than encodeURI.
It is the percent-encoded at sign, from byte value 0x40. It appears whenever an at sign is data rather than the userinfo delimiter, most often in an encoded email address. encodeURIComponent turns @ into %40 in a single pass, which is why the encoded form of an email address usually starts with the local part followed by %40.