URL Encoding and Decoding — A Complete Developer Guide
Why URLs need percent-encoding, what each component requires, plus signs in query strings, and safe encode/decode workflows for APIs and forms.
A support ticket arrived with a screenshot: a search link showing q=hello%2520world and zero results. Someone ran encodeURIComponent twice. The server searched for the literal string hello%20world instead of hello world.
URL encoding looks trivial until plus signs, slashes, Unicode, and double-encoding collide. The rules are consistent once you know which part of the URL you're encoding.
URL anatomy: encode the right piece
https://example.com:443/path/to/page?name=John+Doe&tag=c%2Fa#section
|_____| |_________|_|____________| |______________| |__|
scheme host port path query fragment
Each component has different reserved characters:
| Component | Encode? | Notes |
|-----------|---------|-------|
| Scheme | No | https is fixed |
| Host | Rarely | Internationalized domains use punycode |
| Path | Sometimes | / is delimiter, not encoded within path segments |
| Query values | Yes | &, =, +, spaces need care |
| Fragment | Sometimes | Not sent to server; still matters for client routing |
Encoding the entire URL as one string breaks structural characters. Encode individual parameter values, then assemble.
What percent-encoding does
Unsafe character → % + two hex digits of the UTF-8 byte:
| Character | Encoded |
|-----------|---------|
| space | %20 |
| & | %26 |
| = | %3D |
| + | %2B |
| / | %2F |
| ? | %3F |
| # | %23 |
| é | %C3%A9 (UTF-8 bytes) |
The name "percent-encoding" comes from the % prefix.
Use URL Encoder to inspect how specific strings transform.
encodeURI vs encodeURIComponent (JavaScript)
encodeURI — for full URLs; preserves :/@?#[] etc.
encodeURI("https://example.com/a b");
// https://example.com/a%20b
encodeURIComponent — for query parameter values; encodes everything including / and ?
encodeURIComponent("a/b?c");
// a%2Fb%3Fc
Rule of thumb: URIComponent for values, URI for whole URLs (rarely needed if you build URLs with URLSearchParams).
The plus sign confusion
In application/x-www-form-urlencoded (HTML forms):
- Space →
+
In URL paths and RFC 3986 strict encoding:
- Space →
%20
Decoders in most frameworks accept + as space in query values. Don't rely on + in paths.
When debugging "space works locally but not in production," check whether one layer decodes + and another doesn't.
URL Decoder shows both representations side by side.
Building query strings correctly
Wrong:
const url = `/search?q=${query}`; // breaks if query contains & or =
Right:
const params = new URLSearchParams({ q: query, page: "1" });
const url = `/search?${params.toString()}`;
URLSearchParams handles encoding per key and value.
Manual construction — encode each value with encodeURIComponent, join with &:
/search?q=hello%20world&tag=node%2Fjs
Decoding: once, at the right layer
Servers and frameworks decode query parameters automatically. Problems arise when:
- Client encodes
- Server decodes
- Application encodes again before storage
- Display decodes again
Each layer should encode outbound and decode inbound exactly once.
Use URL Decoder to verify what a string looks like after one decode pass.
Double-encoding: the silent killer
Original: 100% done
First encode: 100%25%20done
Second encode: 100%2525%2520done
Links shared through multiple systems (email → CRM → redirector) sometimes accumulate encoding layers. Symptom: search terms look right in the address bar but match wrong in the database.
Fix: Decode in a loop until stable, or compare against known double-encoding patterns (%25 for %).
Paths with special characters
File paths in URLs need segment-level encoding:
/files/2024/Q1 Report.pdf ❌ (spaces)
/files/2024/Q1%20Report.pdf ✓
Slashes inside a single path segment (filename with /) must encode the slash:
encodeURIComponent("a/b") → a%2Fb
But don't encode the slashes between path segments.
Unicode and international URLs
https://example.com/café — browsers display Unicode; wire format may use punycode for domains and percent-encoding for paths:
/caf%C3%A9
Always UTF-8 encode before percent-encoding bytes. Latin-1 assumptions break emoji and CJK.
Base64 in URLs: different tool
Putting binary in URLs sometimes uses Base64url (- and _ instead of + and /). That's alphabet substitution, not percent-encoding.
JWTs in URLs need Base64url, not encodeURIComponent on the whole token.
APIs and redirect URLs
OAuth redirect_uri parameters must match registered values exactly — encoding differences cause redirect_uri_mismatch errors.
Common mistakes:
- Trailing slash encoded vs not
httpvshttps- Query parameter order (some providers care)
Log the exact bytes sent, not the pretty-printed version.
HTML links vs JavaScript URLs
<a href="/search?q=hello world"> ❌ ambiguous
<a href="/search?q=hello%20world"> ✓
Template engines may auto-escape & in attributes — verify output in View Source, not rendered DOM alone.
Server-side decoding (conceptual)
Most frameworks provide:
decodeURIComponent(JavaScript)urllib.parse.unquote(Python)url.QueryUnescape(Go)
Plus form parsers that convert + to space in query values.
Never decode before authentication middleware if the raw URL is part of the signature (webhook verification, AWS SigV4).
Security considerations
- Open redirects — validate decoded redirect targets against an allowlist
- Log injection — decoded
%0Anewlines in parameters can break log formats - SSRF — encoded
http://127.0.0.1may bypass naive string filters; decode then validate - XSS in reflected parameters — encoding for URL context ≠ HTML context; escape at output layer too
Extracted values headed for JSON APIs should pass through JSON Formatter during debugging to spot embedded control characters.
Debugging checklist
| Symptom | Check |
|---------|-------|
| + appears as literal plus | Decoder didn't treat + as space in query |
| %20 visible in UI | Over-encoded or not decoded on display |
| Broken slashes in path | encodeURIComponent on full path instead of segments |
| Unicode mojibake | Wrong charset before percent-encoding |
| OAuth redirect fails | Byte-exact compare of encoded redirect_uri |
Framework-specific gotchas
Axios and fetch — params serializers differ. Axios may encode differently than URLSearchParams. When debugging partner integrations, log config.paramsSerializer output, not assumed defaults.
Ruby on Rails — CGI.escape vs ERB::Util.url_encode handle spaces differently (+ vs %20). Mixing helpers across a codebase causes subtle bugs in redirect URLs.
PHP — urlencode() vs rawurlencode() mirror the space-as-plus vs space-as-%20 split. RFC 3986 prefers rawurlencode for path segments.
Java — URLEncoder.encode uses + for spaces (application/x-www-form-urlencoded style). Use URI builder classes for RFC-compliant path encoding.
When stack traces show encoding at multiple layers, draw a diagram of encode/decode boundaries — framework middleware often decodes before your handler runs, and your handler encodes again before storage.
Internationalized domain names (IDN)
Hostnames with Unicode (münchen.de) use punycode in the wire format (xn--mnchen-3ya.de). That's separate from path/query percent-encoding but shows up in the same URLs developers copy from browsers. Don't manually encode punycode hostnames — use URL parser libraries that accept Unicode display form.
Related articles
- Understanding Base64 Encoding — Base64url vs percent-encoding
- Understanding Regular Expressions — parse log lines with encoded params
- JSON Formatting Guide — API payloads with encoded fields
Conclusion
URL encoding translates characters that would break URL structure into %XX escape sequences. Encode each component at construction time, decode once at consumption, and know whether + means space in your context. Double-encoding and wrong encoder choice (encodeURI vs encodeURIComponent) cause most production link bugs — not missing libraries.
Frequently Asked Questions
Common questions answered to help you get the most from this tool.
Vertex Solutions Editorial Team
Guides and articles are produced under this collective byline — not attributed to invented individual experts. We research tool workflows, check steps against live tools where practical, and avoid fabricated personal stories, client anecdotes, or invented test results.
- Content research — Topics come from real tool workflows, common questions, and gaps in existing guides.
- Technical review — Steps, tool behavior, and examples are checked against the live tools on this site before publication when practical.
- Fact checking — Claims about formats, browser behavior, and calculator outputs are verified against documentation and tested sample inputs where practical.
- Updates — Pages may be revised when tools, official guidance, or browser behavior changes. There is no fixed review calendar for every URL.
- Corrections — Report factual errors via Contact.
Full policy: Editorial Standards. Tool checks: How we verify tools.