The Anatomy of a URL: Scheme, Host, Path, Query and Fragment
July 27, 2026 · DevTools
You paste a URL into a browser and it navigates to the right page. You paste it into a Slack message and it becomes a link. You paste it into curl and it works. You paste it into a Python requests.get() call and get a 400 Bad Request. Somewhere in that string of characters is something the library is interpreting differently than the browser did.
URLs have structure. Understanding that structure is the difference between debugging by trial and error and debugging by inspection.
The URL Parser breaks any URL into its components and shows each field clearly.
The RFC 3986 breakdown
A URL has five primary components:
scheme://userinfo@host:port/path?query#fragment
└─────┘└────────┘└┘└──────┘└──────┘└─┘└─────┘└─┤
always optional optional optional optional optional
Scheme — http, https, ftp, mailto, git. The scheme tells the client which protocol and default port to use. Schemes are case-insensitive (HTTP and http are the same) but conventionally lowercase.
Userinfo — user:password@. Rarely used in browsers (Chrome strips it for security). Still seen in some SVN URLs and FTP access. Putting credentials in a URL is a security risk — they appear in server logs, browser history, and referrer headers.
Host — the server name or IP address. Can include an internationalized domain (IDN) encoded as punycode (xn-- prefix). https://café.com/ is actually https://xn--caf-dma.com/.
Port — :8080. If omitted, defaults to the scheme's well-known port (80 for http, 443 for https).
Path — /api/users/42. The path identifies the resource on the server. In modern REST APIs, it typically includes resource identifiers.
Query — ?page=1&sort=name&order=asc. The ? starts the query string. Parameters are &-separated. Order does not matter for most server frameworks, but some APIs depend on order.
Fragment — #section-2. The fragment is never sent to the server. It identifies a location within the resource (an anchor in HTML), a section in a Markdown file, or a query parameter for JavaScript Single Page Applications.
Percent-encoding (URL encoding)
Characters outside the "unreserved" set must be percent-encoded: a % followed by two hex digits for the byte value.
Unreserved (never encode): A-Z a-z 0-9 - _ . ~
Reserved (encode unless delimiting): : / ? # [ ] @ ! $ & ' ( ) * + , ; =
The most common encodings:
space → %20
/ → %2F (encode inside path segments)
? → %3F (encode in query — or it starts a new query)
& → %26 (encode in query values)
= → %3D (encode in query values)
# → %23 (encode — or it starts a fragment)
% → %25 (encode — prevents interpreting %XX as encoded)
UTF-8 text is encoded character by character:
é (U+00E9) → UTF-8: C3 A9 → %C3%A9
café → cafe%CC%81 (or %C3%A9 depending on normalization)
Two different encoded forms can refer to the same string (é can be one or two bytes in different Unicode normalizations). Servers should normalize before comparison. Clients should encode conservatively.
Relative vs. absolute URLs
A full (absolute) URL includes the scheme: https://example.com/api.
A protocol-relative URL omits the scheme, inheriting the page's: //cdn.example.com/script.js — uses http on HTTP pages, https on HTTPS pages. This pattern is largely abandoned because of mixed-content issues.
A root-relative URL starts with /: /api/users — absolute from the domain root.
A path-relative URL does not start with /: ../users/42 — relative to the current path.
Current URL: https://example.com/api/v1/users/42
Full: https://example.com/api/v1/users/42 (same resource)
Root-relative: /api/v1/users/42 (same domain)
Path-relative: ../users/42 (one level up, then /users/42)
./users/42 (same, explicit)
users/42 (relative to current path)
Relative URLs in HTML <a href>, <img src>, and <link href> resolve against the base URL (the document's URL, or a <base href> tag if present). Misunderstanding this causes broken images and scripts in deployed applications.
The userinfo trap
https://admin:secret@example.com/ has userinfo admin:secret@. Browsers silently strip it. Many server frameworks pass it to application code as part of the request URL. It appears in access logs. It is a security liability.
Modern applications should use the Authorization header for credentials, not the URL. If you receive a URL with userinfo in a security review, reject it.
Using the URL Parser
The URL Parser decodes any URL into its components — scheme, userinfo, host, port, path, query parameters (as a parsed map), and fragment. This is useful for debugging:
- Checking whether a
&in a query value was encoded or is being interpreted as a parameter separator - Extracting the hostname to verify SSL certificate matching
- Comparing two URLs that look the same but differ in encoding
For encoding and decoding query parameters specifically, the URL Encoder / Decoder handles individual strings or full query strings.
For testing an API endpoint with specific query parameters, the HTTP Request Builder lets you construct the request, inspect the resolved URL, and execute it.