URL parser

Paste an address and see it the way a browser does: scheme, host, port, path, query, fragment, and every query parameter separately. The parsing is done by the browser own URL implementation rather than by a pattern we wrote, which matters because the browser is what will actually resolve the address.

Result

The answer appears here as you type.

  • Where it runs

    Nothing is uploaded, because there is no file — it is worked out in this page.

  • No queue, no account

    It answers as fast as your machine can, and it never asks who you are.

  • As often as you like

    Nothing is counted and nothing is capped — answering again costs us nothing.

How it works

  1. Paste the URL into the box. It parses as you type.
  2. Read the parts. Anything the address does not have is left out rather than shown empty.
  3. Query parameters are listed underneath, already decoded.

Never write a regular expression for this

The grammar in RFC 3986 has more corners than it looks: userinfo with a colon in the password, IPv6 literals in square brackets, an empty port after a colon, percent-encoded characters in the host, a path that is only dots. Every hand-rolled pattern disagrees with the browser somewhere, and the disagreement is always found by a real address rather than by a test.

This page calls `new URL()`, which is the same code that resolves the address when you press enter. Where they disagree there is no argument to have — the browser is right by definition, because the browser is what fetches it.

The parts are shown normalised, and that is the answer

The scheme and host come back lower-cased. `.` and `..` in the path are resolved. A port that is the default for the scheme — 443 for https, 80 for http — is dropped, because it is implied. Characters that have to be percent-encoded are encoded.

None of that is the tool being lossy. It is what the address *means*, which is the question being asked; the text you typed is still in the box above if you want to compare. `HTTPS://Example.COM:443/a/./b/../c` and `https://example.com/a/c` are the same request, and seeing that stated is often the whole reason to be here.

Origin is the field that decides whether a request is allowed

Scheme, host and port together are the origin, and it is the unit that the same-origin policy, CORS and cookie scoping all work in. Two addresses are the same origin only if all three match — so `http://example.com` and `https://example.com` are different origins, and so are `example.com` and `www.example.com`.

That is why the origin is listed separately rather than left to be assembled by eye. Most confusion about a blocked request or a cookie that is not being sent resolves the moment somebody compares two origins character by character rather than glancing at two hostnames.

The fragment never leaves the browser

Everything after the `#` is handled entirely on the client. It is not sent in the request, it does not reach the server, it is absent from access logs. That is a fact about HTTP rather than a privacy feature, and it cuts both ways.

It means a fragment is a poor place to look for something in a server log — it was never there. It also means a fragment is where single-page applications put state, and where OAuth implicit flow used to put a token, precisely so that it would not be logged by every proxy in between.

Credentials in a URL, and why the password is hidden here

The `user:password@host` form is still legal and still turns up in configuration files, database connection strings and scripts. Browsers have progressively restricted it because it is an excellent phishing device — an address reading `https://[email protected]/` has a host of `attacker.example`, and this page will show you that.

The password is reported as present, with its length, rather than printed. A URL with credentials is nearly always one somebody is debugging, and the screen it is on is nearly always about to become a screenshot in a ticket. That the secret is there is the finding; the secret itself is not.

An address with no scheme is completed, and it says so

Browsers hide the scheme in the address bar, so what people copy often starts at the host. `new URL()` rejects that, correctly — a URL without a scheme is not a URL — and rejecting it here would be technically right and useless.

So `https://` is assumed and a note appears saying that it was. The two cases are kept apart deliberately: a misspelt scheme, `htp://example.com`, is not repaired. It parses, with `htp` as the scheme, which is the diagnosis you came for. Prefixing `https://` onto it would produce an address whose host is `htp` — confidently wrong, which is worse than either alternative.

URL parser: common questions

Why is the port missing from my parsed URL?

Because it was the default for the scheme and is therefore implied. `https://example.com:443/` and `https://example.com/` are the same address, and the URL parser normalises the first to the second. A non-default port is always shown.

Why is my path different from what I pasted?

Because `.` and `..` segments are resolved, the way a browser resolves them before making a request. `/a/./b/../c` really is `/a/c`. Seeing that stated is often exactly the point — a redirect landing somewhere unexpected usually has a `..` in it that nobody counted.

What is the origin, and how is it different from the host?

The origin is the scheme, host and port together, and it is the unit that the same-origin policy, CORS and cookie scoping all use. Two addresses with the same host but different schemes are different origins, which is behind a great deal of confusion about blocked requests.

Why does the fragment not appear in my server logs?

Because it is never sent. Everything after the # is handled by the browser alone and does not form part of the request. That is a fact about HTTP, and it is why single-page applications keep state there and why OAuth implicit flow used to put tokens there.

Is the URL I paste sent anywhere?

No. It is parsed in this page by the browser own URL implementation, and nothing is requested. URLs pasted into a tool like this routinely carry session identifiers and tokens in their query strings, which is exactly why the parsing happens locally.

Other tools