TS-63: URL Design
A URL is an address that identifies a resource on the web. Well-designed URLs are readable, predictable, durable, and shareable. They are part of the user interface, because they appear in browser address bars, links, bookmarks, printed material, and API client code. Poor URL design degrades usability, maintainability, and search ranking.
This technical standard covers the design of URLs for all kinds of HTTP services, including websites, web applications, and HTTP APIs. It is concerned with the structure and semantics of URLs themselves, not with the resources they identify or the protocols used to transfer them.
For general user interface design principles, see TS-15: User Interfaces. For web GUI implementation, see TS-18: Web GUIs. For search engine optimization, see TS-19: Search Engine Optimization (SEO). For HTTP API resource modeling and versioning, see TS-21: HTTP APIs.
Anatomy of a URL
A URL is composed of several parts, each with a distinct role. The RFC 3986 grammar defines them. The WHATWG URL Standard defines how browsers parse them in practice.
https://example.com:8443/v1/users/42?sort=name#profile
\___/ \___________/\__/\_______/\_/ \_______/\______/
| | | | | | |
scheme host port path path query fragment
segments- Scheme —
httporhttps. HTTPS is REQUIRED for any production service. URLs in documentation and links SHOULD use thehttpsscheme. - Host. The domain name (or IP address) that identifies the server.
- Port. Omitted when it is the default for the scheme (80 for HTTP, 443 for HTTPS). Non-default ports SHOULD be avoided in production URLs.
- Path. A slash-delimited sequence of segments that locates a resource within the service. Path design is the subject of Path design.
- Query string. Key-value pairs that parameterize the request. See Query strings.
- Fragment. A client-side anchor that identifies a secondary resource or position within the primary resource. See Fragments.
Case sensitivity
The scheme and host components of a URL are case-insensitive and SHOULD be written in lowercase. The path, query string, and fragment are case-sensitive (except for the percent-encoding of reserved characters, which is case- insensitive). To avoid ambiguity, path segments and query parameter names SHOULD be treated as case-sensitive and SHOULD be written in lowercase.
Path design
The path is the most consequential part of a URL. It identifies a resource and is the part most often read, shared, and remembered.
Use readable, meaningful path segments
Path segments SHOULD be human-readable words that describe the resource they identify. Opaque identifiers, internal codes, and implementation details SHOULD NOT appear in paths intended for human consumption.
# Good: https://example.com/blog/2023/url-design # Poor: https://example.com/p/12345?c=789
Use lowercase letters, digits, and hyphens
Path segments SHOULD use lowercase letters, digits, and hyphens only. Words within a segment SHOULD be separated with hyphens (kebab-case), not underscores or camelCase.
# Good: https://example.com/charge-points/42 https://example.com/blog/how-to-design-urls # Poor: https://example.com/ChargePoints/42 https://example.com/charge_points/42 https://example.com/chargePoints/42
Hyphens are preferred because they are the standard word separator in URLs and are treated as word boundaries by search engines and screen readers.
Keep paths shallow
Path depth SHOULD be kept to the minimum necessary to identify a resource. Deeply nested paths are harder to read, harder to type, and harder to restructure. As a rule of thumb, paths SHOULD NOT exceed three or four segments for websites, or five or six for HTTP APIs that model resource hierarchies.
# Good: https://example.com/blog/url-design # Poor: https://example.com/site/content/blog/posts/2023/01/url-design
Trailing slashes
A trailing slash indicates that the path identifies a collection or a directory- like resource. A URL without a trailing slash identifies a specific resource.
Services SHOULD be consistent in their use of trailing slashes. It is RECOMMENDED that canonical URLs omit the trailing slash for individual resources and use it only for collections. A service SHOULD accept requests with or without a trailing slash, but SHOULD NOT respond with a redirect to the canonical version — redirects add a round trip and complicate caching.
File extensions
File extensions (.html, .php, .aspx) SHOULD NOT appear in URLs. They
expose implementation details and make it harder to change the underlying
technology. The content type is conveyed by the Content-Type header, not by
the URL.
Exceptions MAY be made where the extension carries semantic meaning for the
resource, such as a downloadable file (report.pdf) or a format variant
(data.json, data.xml).
Identifiers in paths
When a path includes a resource identifier, the identifier SHOULD be the final
segment. Opaque, sequential identifiers (eg. 42) MAY be used, but
human-readable slugs are RECOMMENDED where the identifier is visible to end
users.
# Good: https://example.com/blog/url-design https://example.com/users/42 # Poor: https://example.com/42/url-design
For HTTP APIs, see TS-21: HTTP APIs for guidance on resource identifiers, collections, and sub-resource hierarchies.
Query strings
The query string carries parameters that modify or filter the request without identifying a different resource. It is the appropriate place for sorting, filtering, pagination, and other request options.
Use query strings for parameters, not for identity
The path identifies the resource; the query string parameterizes the request. Values that change which resource is identified belong in the path. Values that change how the resource is represented or selected belong in the query string.
# Good: https://example.com/articles?sort=date&order=desc https://example.com/search?q=url+design # Poor: https://example.com/articles?category=web
The second example is poor because category identifies a different collection
of articles; it belongs in the path (https://example.com/categories/web/articles).
Naming query parameters
Query parameter names SHOULD be short, lowercase, and descriptive. Multi-word names SHOULD use hyphens or underscores consistently within a single service; hyphens are RECOMMENDED for consistency with path segments.
# Good: https://example.com/articles?sort-by=date # Poor: https://example.com/articles?SortBy=date https://example.com/articles?sortBy=date
Boolean parameters
Boolean query parameters MAY be specified as key-only flags (?verbose) or as
key-value pairs (?verbose=true). A service SHOULD pick one convention and
apply it consistently. Key-value pairs are RECOMMENDED for HTTP APIs, as they
are easier to document and to compose programmatically.
Encoding
Reserved characters in query strings MUST be percent-encoded. Spaces SHOULD
be encoded as + (in application/x-www-form-urlencoded contexts) or %20
(in all other contexts); a service SHOULD accept both.
Fragments
The fragment identifies a secondary resource — typically a section within an HTML document — and is not sent to the server. It is resolved client-side by the user agent.
Use fragments for in-page navigation
In HTML documents, fragments SHOULD identify sections by their heading text or
by an explicit id attribute. Fragments that match heading text are
auto-generated by many tools and are stable across content edits; fragments
based on auto-generated numeric IDs are not.
https://example.com/blog/url-design#path-design
Fragments and APIs
Fragments are not part of the request that reaches the server and have no defined semantics for non-HTML resources. HTTP APIs SHOULD NOT rely on fragments to convey request parameters.
Permanence
A URL is a contract. Once a URL is published — linked from another page, printed in a book, bookmarked by a user, or indexed by a search engine — it SHOULD continue to resolve to the intended resource for as long as the resource exists. This principle was articulated by Tim Berners-Lee in Cool URIs don’t change.
Design for permanence from the start
The surest way to keep URLs stable is to design them so that they never need to change. Avoid embedding implementation details, technology choices, or transient organizational structures in URLs.
# Good: https://example.com/blog/url-design # Poor: https://example.com/wordpress/2023/01/url-design.php
The poor example exposes the CMS (wordpress), the publication date (which
may be wrong or revised), and the server technology (.php). Each is a detail
that could change and break the URL.
Redirects for moved resources
When a resource moves and its URL must change, the old URL SHOULD redirect to
the new one with an HTTP 301 Moved Permanently response. Redirects SHOULD be
maintained indefinitely, since links to the old URL may persist anywhere on the
Web. A 410 Gone response MAY be used for resources that have been permanently
removed and have no replacement.
Versioning and change
For HTTP APIs, breaking changes are managed through versioning in the URL path (see TS-21: HTTP APIs). For websites and web applications, content changes SHOULD NOT require a URL change; the URL identifies the resource, not a particular version of its representation.
Avoid dates in URLs unless they are part of the identity
Including a publication date in a URL (/blog/2023/01/url-design) makes the
URL brittle: if the date is corrected, the URL breaks. Dates SHOULD be
included only when the date is an intrinsic part of the resource’s identity —
for example, a daily archive (/news/2023-01-15) or a recurring event
(/events/2023/agenda).
References
- Cool URIs don’t change — Tim Berners-Lee: The seminal essay on URL permanence, written by the inventor of the world-wide web.
- Examples of Great URLs — Jim Nielsen: A collection of real-world URLs that illustrate good design, with commentary.
- RFC 3986 — Uniform Resource Identifier (URI): Generic Syntax: The IETF specification that defines the syntax of URLs.
- URL Standard — WHATWG: The living standard that defines parsing, serialization, and conformance for URLs as implemented by web browsers.
- URL structure — Google Search Central: Guidance on URL design from the perspective of search engine optimization.