Skip to content
ansezz.

▸ Free tool

URL Parser & Encoder.

Paste a URL and read every part of it. Edit query parameters and watch the URL rebuild itself. Encode or decode any value with the four functions people keep mixing up.

▸ It normalizes

This uses the browser's own URL parser, so it lowercases the host, punycodes IDNs, drops default ports and resolves .. — the parsed href can differ from what you pasted. That is the wire format, not a bug. Nothing is uploaded.

▸ Parts

href normalized, what the browser sends
origin scheme + host + port — the CORS unit
protocol includes the colon
username userinfo — deprecated
password userinfo — deprecated
host hostname + port
hostname no port, punycoded if IDN
port empty when it is the scheme default
pathname dot segments already resolved
search includes the leading ?
hash never sent to the server

▸ Query parameters

Edit a value or delete a row and the URL above rebuilds instantly. Values are re-serialized with URLSearchParams, so spaces become + and & becomes %26. Duplicate names are kept in order.

Name Value Remove

▸ Rebuilt URL

▸ Encode / decode

  • encodeURIComponent — one value going into a URL: a query value, a path segment, a redirect target. Escapes / ? & = # : @ + $ , ;.
  • encodeURI — a whole URL you assembled yourself. Leaves the delimiters alone so it still parses.
  • decodeURIComponent — reading one value back out. Does not turn + into a space.
  • decodeURI — reading a whole URL back. Leaves %2F, %26, %3F encoded on purpose, because decoding them would change the URL's structure.

What the parser actually does

Everything above runs through the same WHATWG URL parser your address bar uses — new URL(input) — so what you see is what a browser will really send, not a regex approximation. That matters, because the parser normalizes as it goes: the scheme and host get lowercased, bücher.de becomes xn--bcher-kva.de, :443 disappears from an https URL because it is the default port, /a/b/../c collapses to /a/c, and tabs and newlines are stripped outright. If the parsed href does not match the string you pasted, that is the gap between the text you have and the request that goes on the wire.

Two things it does not do. It does not check that the host resolves or that the path exists. And it is not a validator — javascript:alert(1) and data:text/html,… parse perfectly. Before you render a user-supplied URL into an href, allowlist url.protocol against https: and whatever else you actually intend to support.

encodeURI vs encodeURIComponent

encodeURI encodes a whole URL, so it deliberately leaves the delimiters intact. encodeURIComponent encodes one piece that sits inside a URL, so it escapes those same delimiters. Here is the entire difference, character by character:

  encodeURI encodeURIComponent
space %20 %20
/ slash / %2F
? question ? %3F
& ampersand & %26
= equals = %3D
# hash # %23
+ plus + %2B
: colon : %3A
@ at @ %40
~ tilde ~ ~

The classic bug is reaching for encodeURI on a value. "/checkout?next=" + encodeURI("/a?b=1&c=2") produces /checkout?next=/a?b=1&c=2, so the server reads next=/a?b=1 and a bonus parameter c=2. A # in the value is worse: the fragment is never sent, so everything after it silently vanishes before the request leaves the browser. Rule of thumb — value in, encodeURIComponent; whole URL, encodeURI; and if you can, skip both and let URL plus URLSearchParams do the escaping.

Why + is a space in a query but not in a path

+ meaning space comes from application/x-www-form-urlencoded, the HTML form serialization, and it applies to the query string only. In a path a + is a literal plus: /a+b is a file called a+b, not a b. Same byte, two meanings, decided by which side of the ? it lands on.

The trap is decodeURIComponent, which knows nothing about form encoding: decodeURIComponent("a+b") returns "a+b". Hand-roll query parsing with split("&") and decodeURIComponent and every space arrives as a plus. Use URLSearchParams, which applies form rules in both directions. And a literal plus in a value has to travel as %2B — which is exactly what the parameter table above emits.

Repeated keys are legal, and every stack disagrees

?tag=rag&tag=agents is a perfectly valid URL, and no spec says what it means. URLSearchParams.get("tag") returns the first value; getAll("tag") returns both. PHP keeps the last one, so $_GET['tag'] is agents unless you write tag[]=. Go's r.URL.Query().Get("tag") returns the first. Express hands you an array. ASP.NET keeps both and joins them with a comma if you stringify it.

One URL, three different meanings, depending on who receives it. If repeated keys are load-bearing in your API, document the semantics and validate the count server-side. If they are not, put a delimiter inside a single value and stop thinking about it.

How long can a URL be, in practice

HTTP sets no limit. The limits live in the software along the path, and they are lower than people expect. Apache's LimitRequestLine defaults to 8190 bytes. nginx returns 414 Request-URI Too Large when the request line does not fit one of its large_client_header_buffers (8 KB each by default). Node caps total headers at 16 KB. The famous 2,000-character rule is really Internet Explorer's old 2,083-character cap, and it survives because that is roughly where links start breaking in chat clients, email templates and CDN configs.

Practical version: stay under ~2,000 characters for anything a human will share, under 8,000 for anything a server will parse, and move the payload into a POST body past that. Remember too that every byte of a query string lands in access logs, browser history and the Referer header of outbound links — which is why session tokens and signed URLs do not belong there.

Questions, answered

What is the difference between encodeURI and encodeURIComponent?

encodeURI escapes a whole URL and deliberately leaves the delimiters / ? & = # : intact so the result still parses as a URL. encodeURIComponent escapes those delimiters too, because it is meant for one piece that sits inside a URL — a path segment or a query value. Use encodeURIComponent on values, encodeURI on a complete URL, and ideally neither: build the URL with URL and URLSearchParams instead.

Why does my URL show a + instead of a space?

The + comes from application/x-www-form-urlencoded, the form serialization that applies to query strings. URLSearchParams uses it, so a value of “a b” is written as “a+b”. It only applies to the query — inside a path a + is a literal plus character, and decodeURIComponent will never turn a + back into a space.

Does this URL parser send my URL anywhere?

No. Parsing, query editing and encoding all run in your browser using the built-in URL, URLSearchParams and encodeURIComponent APIs. There is no network call, no logging of your input, and nothing is stored. Signed URLs and session tokens still deserve care everywhere — they belong out of logs and chat threads, not just out of online tools.

How do repeated query parameters like ?tag=a&tag=b work?

They are valid, and no standard defines what they mean. In JavaScript, URLSearchParams.get returns the first value and getAll returns every value. PHP keeps the last one, Go's Query().Get returns the first, and Express hands you an array — so the same URL can mean three different things depending on which backend receives it.

What is the maximum length of a URL?

HTTP itself defines no limit; the software along the path does. Apache's LimitRequestLine defaults to 8190 bytes, nginx answers 414 when the request line overflows one of its 8 KB header buffers, and Node caps total headers at 16 KB. The familiar 2,000-character rule comes from Internet Explorer's 2,083-character cap and is still a sane ceiling for links humans will share.

Why does the URL constructor say “Invalid URL”?

new URL() only accepts an absolute URL, so “example.com/path” and “/path?a=1” both throw unless you pass a base — add a scheme like https:// and they parse. The other common causes are a raw space or another forbidden character in the host, and an empty host after the //. This tool catches the error and names the case instead of throwing.

Related

Keep reading