Every developer uses URLs. Most can't cleanly name the difference between the origin and the authority, or say when percent-encoding is required versus cosmetic — and have broken something because of it.
URLs are infrastructure. You type them, build them, parse them, and pass them around all day. And yet the URL is one of those things everyone uses fluently and few can describe precisely — which is fine until the imprecision causes a bug: a broken link from an unencoded space, a CORS failure you don't understand, a signature that won't validate because you reordered the query string. The formal definition, from the RFC that governs the syntax, is deceptively simple:
"A Uniform Resource Identifier (URI) is a compact sequence of characters that identifies an abstract or physical resource."
— IETF RFC 3986, "Uniform Resource Identifier (URI): Generic Syntax"
Simple to state, full of edges in practice. Here are the parts that actually matter.
The anatomy, properly labelled
A full URL is scheme://userinfo@host:port/path?query#fragment. Most people can name scheme, host, path, and query. The ones worth knowing are the ones people skip. userinfo lets you embed credentials directly in a URL (https://user:pass@host) — a genuinely dangerous pattern that leaks passwords into logs and browser history, and one the standard itself deprecates. port is implicit when it's the scheme default (443 for HTTPS) and explicit otherwise, and forgetting it matters more than you'd think, as we'll see. Get the vocabulary straight and the rest of this article has somewhere to hang.
Origin vs authority
These two look almost identical and fail in different places. Origin is scheme + host + port. Authority is userinfo + host + port. The distinction is load-bearing: CORS and SameSite cookie decisions are made on origin, which is why http:// and https:// versions of the same host are different origins, and why example.com and example.com:8080 are too. If a cross-origin request is being blocked and the hosts "look the same," check the scheme and the port — one of them is almost always different, and the browser is treating them as separate origins exactly as specified.
Percent-encoding: required vs cosmetic
Encoding isn't uniform across the URL, which is the source of a classic bug. A space in the path must be %20 — a literal + stays a plus sign. A space in the query string can be either %20 or +, because application/x-www-form-urlencoded defines + as a space there. So the same space encodes differently depending on which part of the URL it's in, and code that assumes one rule everywhere breaks the other place. When in doubt, encode explicitly rather than hand-building strings and hoping.
The fragment never reaches the server
Everything after # — the fragment — is stripped by the browser before the HTTP request goes out. It never appears in your server logs, never reaches your backend, and can't be read server-side. This is by design, and it's why single-page apps used #/route style URLs for years: changing the fragment re-routes the app without a server round-trip. If you're trying to read a fragment on the server, stop — it was never sent. If you're putting something in a fragment expecting the server to see it, it won't.
Query-string order is not guaranteed
The HTTP spec treats query parameters as an unordered set. That has real consequences in two directions. Some cache layers canonicalise query-string order, so ?a=1&b=2 and ?b=2&a=1 may or may not be the same cache key depending on the layer. And some request-signing schemes require parameters in a specific (often alphabetical) order, so reordering them silently invalidates the signature. The rule: never write code that depends on query parameters arriving in the order you sent them, and when you're signing a request, sort deliberately to whatever the signer specifies.
Decompose and build, don't concatenate
Nearly every URL bug above comes from treating URLs as strings you can glue together. Don't. To understand any URL — yours or a third party's — drop it into a URL parser and read the labelled parts, including the port and fragment you might otherwise miss. When a value needs to go into a path or query safely, run it through a URL encoder rather than guessing which characters are legal where. And when you're assembling a URL with parameters programmatically, a query string builder handles the encoding and separators so you don't ship the &-vs-? bug that string concatenation invites. Treat URLs as structured data, not text, and most of this class of bug disappears.