URL Parsing and Query Editing, Explained

After reading this you will know exactly how a URL splits into its six parts, how percent-encoding turns unsafe characters into %XX bytes, and how to edit query parameters without corrupting the request.

What a URL actually is

A URL is a single string with a strict internal structure. Take this one:

https://user:[email protected]:8443/catalog/shoes?color=blue&size=42#reviews

That string is not one blob. It is six named components stitched together by delimiter characters. The parser reads the delimiters (://, @, :, /, ?, #) and hands you each part separately. Once split, you can see that the port is 8443, the path is /catalog/shoes, and the fragment is reviews, without squinting at punctuation.

The authoritative grammar lives in RFC 3986, section 3. It defines the generic syntax as scheme://authority/path?query#fragment, where the authority itself splits into optional userinfo, host and optional port. Every browser, every HTTP library and this tool follow that same grammar.

The six components and their delimiters

Here is the anatomy of the example above, part by part.

Scheme
Everything before ://. Here it is https. It is case-insensitive and must start with a letter. RFC 3986 section 3.1.
Authority
Between :// and the next /, ? or #. Contains optional userinfo (user:pass@), the host (shop.example.com) and an optional port (:8443).
Path
From the first / up to ? or #. Here /catalog/shoes. This is what the server routes on.
Query
After ?, up to #. A set of key-value pairs joined by &: color=blue&size=42.
Fragment
After #. Here reviews. The browser keeps it; it is never sent in the HTTP request.

The default port depends on the scheme. http defaults to 80, https to 443. A URL like https://example.com:443/ and https://example.com/ point to the same place, so the explicit :443 is redundant.

Percent-encoding: the math of %XX

Only a limited set of characters may appear literally in a URL. RFC 3986 section 2.3 calls these the "unreserved" characters: the ASCII letters A-Z a-z, the digits 0-9, and the four marks - . _ ~. Everything else, if it needs to carry meaning as data rather than as a delimiter, is percent-encoded.

The rule is mechanical. Take the character, look up its byte value in UTF-8, then write a % followed by that byte in two uppercase hexadecimal digits.

\text{encoded} = \text{"\%"} + \text{hex}_2(\text{byte})

Here byte is one of the UTF-8 bytes of the character, and hex_2 writes it as two hex digits with a leading zero if needed. A space is byte 0x20, so it becomes %20. An ampersand is 0x26, so it becomes %26. Characters outside ASCII use more than one byte: the euro sign € is UTF-8 bytes E2 82 AC, so it encodes as %E2%82%AC, three percent-groups for one character.

To decode, you reverse the steps: read each %XX as one byte, collect the byte sequence, then interpret it as UTF-8. This tool decodes every part on display and re-encodes on rebuild.

A worked example you can reproduce

Encoding a query value with a space and an ampersand

Suppose you want the parameter q to carry the literal value tea & coffee (with a space on each side of the ampersand). If you paste that raw into a query, the & looks like a delimiter and the value gets truncated to tea . Encoding fixes it.

  1. Start with the value tea & coffee, which is 13 characters.
  2. Encode each byte that is not unreserved. The letters stay. The two spaces become %20 each. The ampersand (byte 0x26) becomes %26.
  3. The result is tea%20%26%20coffee.
  4. Assembled into a query: ?q=tea%20%26%20coffee.

Count the length: the value grew from 13 characters to 18, because three single characters (two spaces, one ampersand) each became three characters. When the server decodes it, it gets back exactly tea & coffee.

Inside a query string, a space is often written as + instead of %20. That comes from the application/x-www-form-urlencoded format, not from RFC 3986. Both decode to a space in query context, but in the path only %20 means a space; a + in a path is a literal plus sign.

Reading the query as a table

The query string color=blue&size=42&color=red is not a map. It is an ordered list of pairs, and keys may repeat. Splitting on & then on the first = gives you three rows:

Query parsed into ordered key-value rows
#KeyValue (decoded)
1colorblue
2size42
3colorred

How a server treats the two color rows is not defined by any single spec. PHP keeps the last value unless you name the key color[]. Node's URLSearchParams.getAll("color") returns both ["blue", "red"]. Because behavior varies, a table view that preserves order and duplicates tells you the truth about what was sent, rather than silently collapsing it.

Given the URL https://shop.example.com:8443/catalog/shoes?color=blue&size=42#reviews, the six parts are: scheme https, host shop.example.com, port 8443, path /catalog/shoes, query color=blue&size=42, fragment reviews.

When to use this, and when not to

Use a parser whenever you need to see or change one part of a URL without disturbing the rest: adding a tracking parameter, stripping a session token before sharing a link, or checking whether a redirect target smuggled an unexpected host. Editing in a table beats hand-editing the string because you cannot accidentally break the encoding of a neighboring value.

Do not use URL parsing to validate that a resource exists or that a host is safe to trust. Parsing tells you the structure, not the reality. A perfectly well-formed URL can point at a server that returns an HTTP error status, or at a host that does not resolve at all. Structure and reachability are separate questions.

Common mistakes

Three errors show up again and again.

  • Double-encoding. If a value is already %20 and you encode it again, the % (byte 0x25) becomes %25, giving %2520. The server then decodes it back to the literal text %20, not a space. Encode exactly once.
  • Trusting the fragment on the server. The part after # never leaves the browser (RFC 3986 section 3.5). If your backend depends on a value, put it in the query or the path, not the fragment.
  • Assuming keys are unique. As shown above, color=blue&color=red is legal. Code that reads only the first match may drop data the sender intended.

Watch the host boundary in redirect URLs. The string https://[email protected]/ has host evil.test, because everything before the @ is userinfo. A quick glance reads "trusted.example.com" and misses the trap. Always confirm the host the parser reports, not the text your eye lands on first.

Related tools

Once you have a clean URL, other references help with the surrounding work. The MIME type lookup maps a file extension in a path to its content type. The User-Agent parser decodes the client string that travels alongside a request. For raw character handling, the HTML entity reference covers the named escapes you meet in HTML attributes, which are a different escaping scheme from percent-encoding. And the Keycode inspector is handy when you are debugging the form that builds a query in the first place.

Frequently asked questions

Why does my space turn into %20 sometimes and + other times?

Both mean a space inside a query string. %20 is the RFC 3986 form and works in any component. + comes from HTML form submission (application/x-www-form-urlencoded) and only means a space in the query. In a path, + is a literal plus sign, so this tool always emits %20 on rebuild for safety.

Is the port always shown?

Only when it differs from the scheme default or was written explicitly. https://example.com/ has an implied port of 443 but no port component in the string. If you type :443, the parser will show it; whether to keep it is your choice.

Does editing a value in the table change the encoding of other values?

No. Each value is decoded and re-encoded independently on rebuild. Changing color=blue to color=café produces color=caf%C3%A9 and leaves size=42 untouched.

Can a URL contain a non-ASCII host like café.com?

The host uses a separate scheme called Punycode (RFC 3492). café.com is transmitted as xn--caf-dma.com. That is not percent-encoding; it is a distinct algorithm for the domain label only. Query and path use percent-encoding, the host does not.

Is anything I paste sent anywhere?

No. The parsing and rebuilding run entirely in your browser. Nothing you paste leaves your device.