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
  • Schemehttp or https. HTTPS is REQUIRED for any production service. URLs in documentation and links SHOULD use the https scheme.
  • 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