← Back to Blog

The Anatomy of a URL: scheme, host, path, query and fragment

A link that stumps you

https://user:pw@example.com:8080/path/to/page?q=hello%20world&lang=zh#section-2

Seven out of ten people can't enumerate every segment in this URL. It isn't ciphertext—you've simply copied it whole so often you never split it apart. Here is the full anatomy, plus which parts hit the server and which stay in the browser.

The standard anatomy

A canonical URL has up to eight segments (bracketed ones optional):

  scheme  userinfo    host    port     path        query          fragment
 ┌──────┐┌───────┐┌─────────┐┌────┐┌───────────┐┌──────────────┐┌────────┐
 https:// user:pw @ example.com :8080 /path/to/page ?q=hello&lang=zh #section-2
 └──┴──┘                  └──────────────┘
   authority
Segment Example Role
scheme https protocol; sets the default port and encryption
userinfo user:pw rarely used; plaintext creds in http, deprecated by browsers
host example.com target hostname or IP
port 8080 port; defaults to the scheme's when omitted
path /path/to/page resource path on the server, /-delimited
query q=hello&lang=zh key=value pairs, &-separated, values percent-encoded
fragment #section-2 browser-side anchor, never sent to the server

What reaches the server and what doesn't

This is the most useful split:

server sees:   path + query + host + port + scheme
server never sees: fragment (after #) — purely a local browser anchor

Hence:

  • query lands in server access logs and analytics; putting sensitive values (tokens, IDs) here is equivalent to intentionally leaking them to logs;
  • fragment never reaches the server, so the backend can't authenticate with it—know this trade-off before "hiding a token in the fragment".

Encoding rules for path and query

URLs fundamentally allow only ASCII alphanumerics + . / - _ ~ and a few safe symbols. Everything else (Chinese, spaces, %, &, ?, #, + …) must be percent-encoded:

space  →  %20
Chinese → each byte as UTF-8, then %xx; e.g. 『中』 = %E4%B8%AD
&     →  %26 (must be encoded inside a value, or it's read as a separator)

Stop hand-joining URLs: use encodeURIComponent for parameter values and encodeURI for the whole URL. Hand-built strings stumble on some Chinese or special char nine times out of ten.

Common parse pitfalls

  • Treating the fragment as a query: nothing after # triggers a request; a fragment jump only scrolls locally;
  • Bare ?/# inside a value: an unescaped ? or # in a value chops the URL in half;
  • The + ambiguity: in a query + often means a space (as in application/x-www-form-urlencoded), while in a path it's a literal plus—one character, two meanings;
  • Default ports depend on the scheme: omitting a port isn't "no port"; parsers fill in 80/443.

Verify each segment with a parser

Don't hunt for delimiters in a long link by eye—throw it into a URL parser tool that lists scheme/host/port/path/query/fragment individually, then compare a normal/anomalous pair. That exposes which segment you assembled wrong. Pair it with a URL encoder/decoder to expand the %xx of Chinese and special chars, and the boundary intuition forms in one go.

Self-check

For this URL, write out its eight segments without a tool:

https://a.com:8443/api/v1/items?tag=dev%20tools&lang=en#top
  • Which segments land in server logs?
  • What character does %20 represent?
  • To put a & inside a query value, how would you write it?

Answer those three and a URL stops being "an unrecognizable string" in your hands.

Frequently Asked Questions

Which part of a URL goes to the server, and which does not?

The **fragment** (everything after `#`) never reaches the server—it's purely a browser-side anchor used to scroll to an element. The path and query **are** visible to the server (and land in server logs and site analytics). Understand the boundary: the server sees from after the scheme through the query, minus the fragment, which exists only in the browser's address bar. That's also why 'put a token in the fragment to avoid leaking it' is common but unreliable—it never hits the server, so the backend can't authenticate with it.

Why do Chinese characters or spaces in a URL fail sometimes but work other times?

Standard URLs only allow ASCII alphanumerics and a few symbols; Chinese, spaces and friends **must be percent-encoded**. Browsers usually auto-encode them in the address bar, so 'it won't open' mostly happens when: ①the backend concatenates raw Chinese into the URL without encoding; ②logs/links keep an unencoded space that truncates the link; ③the browser doesn't auto-encode in certain contexts (cookie, hand-assigned location). Correct practice: use `encodeURIComponent` for parameter *values* and `encodeURI` for the whole URL—never hand-join strings. Verify the %xx mapping with a URL encoder/decoder tool.

What is the relationship between URL, URI and URN?

In one line, **URI ⊇ URL, URN**. URI (Uniform Resource Identifier) is the widest concept—anything that *identifies* a resource, whether reachable or not. URL (Locator) is one subtype: it identifies *and* gives access method and location (scheme + host + path), like `https://a.com/x`. URN (Name) is another subtype: a stable, location-independent name, like `urn:isbn:...`. In practice 'URL' and 'URI' are used interchangeably; almost any endpoint/documentation saying URI accepts a URL. URI is merely the broader superset.

Why does the same address parse to different port/path across browsers and sites?

Because many parts are optional with defaults: ①a missing port takes the scheme default (http→80, https→443); ②prefixes like 'http://' or 'www.' may be filled in; ③a relative path resolves against the current page into an absolute one; ④whether an empty query parameter is kept, and whether the fragment participates in key-sorting, varies across implementations. So 'visually identical' hrefs can split into different ports/paths under different parsers. Don't eyeball it—feed the same URL into a URL parser/formatter and compare fields; most seeming mysticism is just default-value rules.

← Back to Blog