Anatomy of a URL: what every part of a link does.
You type hundreds of URLs a week and most of them are seven machines wearing one trench coat: a protocol choice, an optional identity, an address, a door number, a file path, a note to the application, and a note the server never even sees. Here's the whole grammar, part by part — and the trap hiding in each one.
The structure comes from RFC 3986, the URI standard, and it's strict enough that every browser and server on Earth splits a URL the same way. One worked example carries the whole article:
The map.
https://user:pw@shop.example.co.uk:8443/products/shoes?color=red&size=42#reviews
└─┬─┘ └──┬──┘ └───────┬────────┘ └┬─┘ └──────┬──────┘ └───────┬───────┘ └──┬──┘
scheme userinfo host port path query fragment
Scheme, then ://, then an optional user:pw@, then the host, an optional :port, the path, an optional ?query, an optional #fragment. Every part except scheme and host can be absent — https://example.com is a complete URL. Now, part by part.
Scheme: which rules apply.
The scheme (https, http, mailto, ftp, file, data) declares which protocol — which rulebook — the rest of the URL should be read with. It's why mailto:someone@example.com has no slashes and no host section: the generic syntax only applies as far as each scheme adopts it. The everyday trap is scheme-dropping: paste example.com/page into contexts expecting a full URL and parsers can read example.com/page as a path, not a host. And the difference between http and https isn't cosmetic — it decides whether the query string and path cross the network readably or encrypted.
Userinfo: the part that's now mostly a scam.
The user:password@ slot predates the modern web — it was built for FTP-era credentials, and browsers have spent two decades removing support because its main surviving use is deception: https://paypal.com@evil.example/login is a URL whose host is evil.example — everything before the @ is just a username the attacker chose to look like a domain. Modern browsers strip or warn on userinfo in web URLs, and credentials belong in headers, not URLs. The lesson generalizes: the real host is what sits between the last @ (if any) and the next /, ?, or # — reading that correctly is the single most security-relevant URL skill.
Host and port: the address.
The host names the machine — a domain (shop.example.co.uk) or an IP address. Domains read right to left: the top-level domain, the registrable domain, then subdomains — which is why secure-bank.evil.example belongs to evil.example, no matter how reassuring its left end looks. Hosts are case-insensitive (EXAMPLE.COM = example.com) — the only part of a URL that is. Non-ASCII domains exist too (bücher.example), but DNS speaks ASCII, so they travel in Punycode (xn--bcher-kva.example) — and lookalike Unicode characters in hostnames are their own phishing genre.
The port is the door number on that machine. You rarely see it because each scheme has a default — 443 for https, 80 for http — and browsers hide the default. An explicit :8443 or :3000 just means "not the usual door," which is why local dev servers wear ports so prominently.
Path: the request.
The path (/products/shoes) tells the server which resource. It looks like a filesystem path and historically was one; on modern sites it's a routing key the application interprets however it likes. Unlike the host, the path is case-sensitive per the standard — /About and /about may be different pages, and whether they are depends on the server. Trailing slashes are similarly server-defined: /docs and /docs/ can differ, which is why canonical URLs pick one form and redirect the other.
Query: parameters for the application.
Everything after ? is the query string: key=value pairs joined by &, the URL's general-purpose parameter channel — search terms, filters, page numbers, and the utm_* tracking crumbs marketing tools append. Order doesn't matter to the syntax, duplicate keys are legal (how multi-select filters travel), and every value must be percent-encoded so the data can't be mistaken for the grammar — that whole story has its own article. Two practical notes: query strings are visible everywhere (logs, history, referrers — never put secrets in them), and cleaning tracking parameters off a shared link is both polite and safe, since the page itself is addressed by host + path.
Fragment: the part the server never sees.
Everything after # is the fragment, and it has the strangest property in the URL: the browser doesn't send it to the server at all. It's a client-side note — classically "scroll to the element with this id" (#reviews, or the #the-map headings on this very page), and in single-page apps a whole routing state. Because it never crosses the wire, changing only the fragment doesn't reload the page, fragments never appear in server logs, and — a corollary that surprises people — the server cannot redirect you based on a fragment it never received. If it's after the #, it's between you and your browser.
Takeaways.
The thing to remember: scheme picks the rulebook, host is what sits between the last @ and the next / (read domains right to left), the port hides when it's the default, the path is the app's case-sensitive routing key, the query is the visible parameter channel (no secrets), and the fragment never reaches the server at all.
A URL is a small contract with seven clauses, and every phishing trick, broken deep-link, and mystery redirect is someone exploiting a clause you skimmed. Read one carefully, once, and you'll never quite skim them again.
Dissect any URL in your browser.
The URL Parser splits a link into scheme, userinfo, host, port, path, and every decoded query parameter — the diagram from this article, live. Entirely client-side, which matters when the URL you're inspecting contains a token.
Open the URL Parser