URL

Living Standard — Last Updated

Participate:
GitHub whatwg/url (new issue, open issues)
Chat on Matrix
Commits:
GitHub whatwg/url/commits
Snapshot as of this commit
@urlstandard
Tests:
web-platform-tests url/ (ongoing work)
Translations (non-normative):
日本語
简体中文
한국어

Abstract

The URL Standard defines URLs, domains, IP addresses, the application/x-www-form-urlencoded format, and their API.

Goals

The URL standard takes the following approach towards making URLs fully interoperable:

As the editors learn more about the subject matter the goals might increase in scope somewhat.

1. Infrastructure

This specification depends on Infra. [INFRA]

Some terms used in this specification are defined in the following standards and specifications:


To serialize an integer, represent it as the shortest possible decimal number.

1.1. Writing

A validation error indicates a mismatch between input and valid input. User agents, especially conformance checkers, are encouraged to report them somewhere.

A validation error does not mean that the parser terminates. Termination of a parser is always stated explicitly, e.g., through a return statement.

It is useful to signal validation errors as error-handling can be non-intuitive, legacy user agents might not implement correct error-handling, and the intent of what is written might be unclear to other developers.

Error type Error description Failure
IDNA
domain-to-ASCII

Unicode ToASCII records an error or returns the empty string. [UTS46]

If details about Unicode ToASCII errors are recorded, user agents are encouraged to pass those along.

Yes
domain-invalid-code-point

The input’s host contains a forbidden domain code point.

Hosts are percent-decoded before being processed when the URL is special, which would result in the following host portion becoming "exa#mple.org" and thus triggering this error.

"https://exa%23mple.org"

Yes
domain-to-Unicode

Unicode ToUnicode records an error. [UTS46]

The same considerations as with domain-to-ASCII apply.

·
Host parsing
host-invalid-code-point

An opaque host (in a URL that is not special) contains a forbidden host code point.

"foo://exa[mple.org"

Yes
IPv4-empty-part

An IPv4 address ends with a U+002E (.).

"https://127.0.0.1./"

·
IPv4-too-many-parts

An IPv4 address does not consist of exactly 4 parts.

"https://1.2.3.4.5/"

Yes
IPv4-non-numeric-part

An IPv4 address part is not numeric.

"https://test.42"

Yes
IPv4-non-decimal-part

The IPv4 address contains numbers expressed using hexadecimal or octal digits.

"https://127.0.0x0.1"

·
IPv4-out-of-range-part

An IPv4 address part exceeds 255.

"https://255.255.4000.1"

Yes
(only if applicable to the last part)
IPv6-unclosed

An IPv6 address is missing the closing U+005D (]).

"https://[::1"

Yes
IPv6-invalid-compression

An IPv6 address begins with improper compression.

"https://[:1]"

Yes
IPv6-too-many-pieces

An IPv6 address contains more than 8 pieces.

"https://[1:2:3:4:5:6:7:8:9]"

Yes
IPv6-multiple-compression

An IPv6 address is compressed in more than one spot.

"https://[1::1::1]"

Yes
IPv6-invalid-code-point

An IPv6 address contains a code point that is neither an ASCII hex digit nor a U+003A (:). Or it unexpectedly ends.

"https://[1:2:3!:4]"

"https://[1:2:3:]"

Yes
IPv6-too-few-pieces

An uncompressed IPv6 address contains fewer than 8 pieces.

"https://[1:2:3]"

Yes
IPv4-in-IPv6-too-many-pieces

An IPv6 address with IPv4 address syntax: the IPv6 address has more than 6 pieces.

"https://[1:1:1:1:1:1:1:127.0.0.1]"

Yes
IPv4-in-IPv6-invalid-code-point

An IPv6 address with IPv4 address syntax:

  • An IPv4 part is empty or contains a non-ASCII digit.
  • An IPv4 part contains a leading 0.
  • There are too many IPv4 parts.

"https://[ffff::.0.0.1]"

"https://[ffff::127.0.xyz.1]"

"https://[ffff::127.0xyz]"

"https://[ffff::127.00.0.1]"

"https://[ffff::127.0.0.1.2]"

Yes
IPv4-in-IPv6-out-of-range-part

An IPv6 address with IPv4 address syntax: an IPv4 part exceeds 255.

"https://[ffff::127.0.0.4000]"

Yes
IPv4-in-IPv6-too-few-parts

An IPv6 address with IPv4 address syntax: an IPv4 address contains too few parts.

"https://[ffff::127.0.0]"

Yes
URL parsing
invalid-URL-unit

A code point is found that is not a URL unit.

"https://example.org/>"

" https://example.org "

"ht
tps://example.org
"

"https://example.org/%s"

·
special-scheme-missing-following-solidus

The input’s scheme is not followed by "//".

"file:c:/my-secret-folder"

"https:example.org"

const url = new URL("https:foo.html", "https://example.org/");
·
missing-scheme-non-relative-URL

The input is missing a scheme, because it does not begin with an ASCII alpha, and either no base URL was provided or the base URL cannot be used as a base URL because it has an opaque path.

Input’s scheme is missing and no base URL is given:

const url = new URL("💩");

Input’s scheme is missing, but the base URL has an opaque path.

const url = new URL("💩", "mailto:[email protected]");
Yes
invalid-reverse-solidus

The URL has a special scheme and it uses U+005C (\) instead of U+002F (/).

"https://example.org\path\to\file"

·
invalid-credentials

The input includes credentials.

·
host-missing

The input has a special scheme, but does not contain a host.

"https://#fragment"

"https://:443"

"https://user:pass@"

Yes
port-out-of-range

The input’s port is too big.

"https://example.org:70000"

Yes
port-invalid

The input’s port is invalid.

"https://example.org:7z"

Yes
file-invalid-Windows-drive-letter

The input is a relative-URL string that starts with a Windows drive letter and the base URL’s scheme is "file".

const url = new URL("/c:/path/to/file", "file:///c:/");
·
file-invalid-Windows-drive-letter-host

A file: URL’s host is a Windows drive letter.

"file://c:"

·

1.2. Parsers

The EOF code point is a conceptual code point that signifies the end of a string or code point stream.

A pointer for a string input is an integer that points to a code point within input. Initially it points to the start of input. If it is −1 it points nowhere. If it is greater than or equal to input’s code point length, it points to the EOF code point.

When a pointer is used, c references the code point the pointer points to as long as it does not point nowhere. When the pointer points to nowhere c cannot be used.

When a pointer is used, remaining references the code point substring from the pointer + 1 to the end of the string, as long as c is not the EOF code point. When c is the EOF code point remaining cannot be used.

If "mailto:username@example" is a string being processed and a pointer points to @, c is U+0040 (@) and remaining is "example".

If the empty string is being processed and a pointer points to the start and is then decreased by 1, using c or remaining would be an error.

1.3. Percent-encoded bytes

A percent-encoded byte is U+0025 (%), followed by two ASCII hex digits.

It is generally a good idea for sequences of percent-encoded bytes to be such that, when percent-decoded and then passed to UTF-8 decode without BOM or fail, they do not end up as failure. How important this is depends on where the percent-encoded bytes are used. E.g., for the host parser not following this advice is fatal, whereas for URL rendering the percent-encoded bytes would not be rendered percent-decoded.

To percent-encode a byte byte, return a string consisting of U+0025 (%), followed by two ASCII upper hex digits representing byte.

To percent-decode a byte sequence input, run these steps:

Using anything but UTF-8 decode without BOM when input contains bytes that are not ASCII bytes might be insecure and is not recommended.

  1. Let output be an empty byte sequence.

  2. For each byte byte in input:

    1. If byte is not 0x25 (%), then append byte to output.

    2. Otherwise, if byte is 0x25 (%) and the next two bytes after byte in input are not in the ranges 0x30 (0) to 0x39 (9), 0x41 (A) to 0x46 (F), and 0x61 (a) to 0x66 (f), all inclusive, append byte to output.

    3. Otherwise:

      1. Let bytePoint be the two bytes after byte in input, decoded, and then interpreted as hexadecimal number.

      2. Append a byte whose value is bytePoint to output.

      3. Skip the next two bytes in input.

  3. Return output.

To percent-decode a scalar value string input:

  1. Let bytes be the UTF-8 encoding of input.

  2. Return the percent-decoding of bytes.

In general, percent-encoding results in a string with more U+0025 (%) code points than the input, and percent-decoding results in a byte sequence with less 0x25 (%) bytes than the input.


The C0 control percent-encode set are the C0 controls and all code points greater than U+007E (~).

The fragment percent-encode set is the C0 control percent-encode set and U+0020 SPACE, U+0022 ("), U+003C (<), U+003E (>), and U+0060 (`).

The query percent-encode set is the C0 control percent-encode set and U+0020 SPACE, U+0022 ("), U+0023 (#), U+003C (<), and U+003E (>).

The query percent-encode set cannot be defined in terms of the fragment percent-encode set due to the omission of U+0060 (`).

The special-query percent-encode set is the query percent-encode set and U+0027 (').

The path percent-encode set is the query percent-encode set and U+003F (?), U+005E (^), U+0060 (`), U+007B ({), and U+007D (}).

The userinfo percent-encode set is the path percent-encode set and U+002F (/), U+003A (:), U+003B (;), U+003D (=), U+0040 (@), U+005B ([) to U+005D (]), inclusive, and U+007C (|).

The component percent-encode set is the userinfo percent-encode set and U+0024 ($) to U+0026 (&), inclusive, U+002B (+), and U+002C (,).

This is used by HTML for registerProtocolHandler(), and could also be used by other standards to percent-encode data that can then be embedded in a URL’s path, query, or fragment; or in an opaque host. Using it with UTF-8 percent-encode gives identical results to JavaScript’s encodeURIComponent() [sic]. [HTML] [ECMA-262]

The application/x-www-form-urlencoded percent-encode set is the component percent-encode set and U+0021 (!), U+0027 (') to U+0029 RIGHT PARENTHESIS, inclusive, and U+007E (~).

The application/x-www-form-urlencoded percent-encode set contains all code points, except the ASCII alphanumeric, U+002A (*), U+002D (-), U+002E (.), and U+005F (_).

To percent-encode after encoding, given an encoding encoding, scalar value string input, a percentEncodeSet, and an optional boolean spaceAsPlus (default false):

  1. Let encoder be the result of getting an encoder from encoding.

  2. Let inputQueue be input converted to an I/O queue.

  3. Let output be the empty string.

  4. Let potentialError be 0.

    This needs to be a non-null value to initiate the subsequent while loop.

  5. While potentialError is non-null:

    1. Let encodeOutput be an empty I/O queue.

    2. Set potentialError to the result of running encode or fail with inputQueue, encoder, and encodeOutput.

    3. For each byte of encodeOutput converted to a byte sequence:

      1. If spaceAsPlus is true and byte is 0x20 (SP), then append U+002B (+) to output and continue.

      2. Let isomorph be a code point whose value is byte’s value.

      3. Assert: percentEncodeSet includes all non-ASCII code points.

      4. If isomorph is not in percentEncodeSet, then append isomorph to output.

      5. Otherwise, percent-encode byte and append the result to output.

    4. If potentialError is non-null, then append "%26%23", followed by the shortest sequence of ASCII digits representing potentialError in base ten, followed by "%3B", to output.

      This can happen when encoding is not UTF-8.

  6. Return output.

Of the possible values for the percentEncodeSet argument only two end up encoding U+0025 (%) and thus give “roundtripable data”: component percent-encode set and application/x-www-form-urlencoded percent-encode set. The other values for the percentEncodeSet argument — which happen to be used by the URL parser — leave U+0025 (%) untouched and as such it needs to be percent-encoded first in order to be properly represented.

To UTF-8 percent-encode a scalar value scalarValue using a percentEncodeSet, return the result of running percent-encode after encoding with UTF-8, scalarValue as a string, and percentEncodeSet.

To UTF-8 percent-encode a scalar value string input using a percentEncodeSet, return the result of running percent-encode after encoding with UTF-8, input, and percentEncodeSet.


Here is a summary, by way of example, of the operations defined above:

Operation Input Output
Percent-encode input 0x23 "%23"
0x7F "%7F"
Percent-decode input `%25%s%1G` `%%s%1G`
Percent-decode input "‽%25%2E" 0xE2 0x80 0xBD 0x25 0x2E
Percent-encode after encoding with Shift_JIS, input, and the userinfo percent-encode set " " "%20"
"" "%81%DF"
"" "%26%238253%3B"
Percent-encode after encoding with ISO-2022-JP, input, and the userinfo percent-encode set "¥" "%1B(J\%1B(B"
Percent-encode after encoding with Shift_JIS, input, the userinfo percent-encode set, and true "1+1 ≡ 2%20‽" "1+1+%81%DF+2%20%26%238253%3B"
UTF-8 percent-encode input using the userinfo percent-encode set U+2261 (≡) "%E2%89%A1"
U+203D (‽) "%E2%80%BD"
UTF-8 percent-encode input using the userinfo percent-encode set "Say what‽" "Say%20what%E2%80%BD"

2. Security considerations

The security of a URL is a function of its environment. Care is to be taken when rendering, interpreting, and passing URLs around.

When rendering and allocating new URLs "spoofing" needs to be considered. An attack whereby one host or URL can be confused for another. For instance, consider how 1/l/I, m/rn/rri, 0/O, and а/a can all appear eerily similar. Or worse, consider how U+202A LEFT-TO-RIGHT EMBEDDING and similar code points are invisible. [UTR36]

When passing a URL from party A to B, both need to carefully consider what is happening. A might end up leaking data it does not want to leak. B might receive input it did not expect and take an action that harms the user. In particular, B should never trust A, as at some point URLs from A can come from untrusted sources.

3. Hosts (domains and IP addresses)

At a high level, a host, valid host string, host parser, and host serializer relate as follows:

A parse-serialize roundtrip gives the following results, depending on the isOpaque argument to the host parser:

Input Output (isOpaque = false) Output (isOpaque = true)
EXAMPLE.COM example.com (domain) EXAMPLE.COM (opaque host)
example%2Ecom example%2Ecom (opaque host)
faß.example xn--fa-hia.example (domain) fa%C3%9F.example (opaque host)
0 0.0.0.0 (IPv4) 0 (opaque host)
%30 %30 (opaque host)
0x 0x (opaque host)
0xffffffff 255.255.255.255 (IPv4) 0xffffffff (opaque host)
[0:0::1] [::1] (IPv6)
[0:0::1%5D Failure
[0:0::%31]
09 Failure 09 (opaque host)
example.255 example.255 (opaque host)
example^example Failure

3.1. Host representation

A host is a domain, an IP address, an opaque host, or an empty host. Typically a host serves as a network address, but it is sometimes used as opaque identifier in URLs where a network address is not necessary.

A typical URL whose host is an opaque host is git://github.com/whatwg/url.git.

The RFCs referenced in the paragraphs below are for informative purposes only. They have no influence on host writing, parsing, and serialization. Unless stated otherwise in the sections that follow.

A domain is a non-empty ASCII string that identifies a realm within a network. [RFC1034]

The domain labels of a domain domain are the result of strictly splitting domain on U+002E (.).

The example.com and example.com. domains are not equivalent and typically treated as distinct.

An IP address is an IPv4 address or an IPv6 address.

An IPv4 address is a 32-bit unsigned integer that identifies a network address. [RFC791]

An IPv6 address is a 128-bit unsigned integer that identifies a network address. This integer is composed of a list of 8 16-bit unsigned integers, also known as an IPv6 address’s pieces. [RFC4291]

Support for <zone_id> is intentionally omitted.

An opaque host is a non-empty ASCII string that can be used for further processing.

An empty host is the empty string.

3.2. Host miscellaneous

A forbidden host code point is U+0000 NULL, U+0009 TAB, U+000A LF, U+000D CR, U+0020 SPACE, U+0023 (#), U+002F (/), U+003A (:), U+003C (<), U+003E (>), U+003F (?), U+0040 (@), U+005B ([), U+005C (\), U+005D (]), U+005E (^), or U+007C (|).

A forbidden domain code point is a forbidden host code point, a C0 control, U+0025 (%), or U+007F DELETE.

To obtain the public suffix of a host host, run these steps. They return null or a domain representing a portion of host that is included on the Public Suffix List. [PSL]

  1. If host is not a domain, then return null.

  2. Let trailingDot be "." if host ends with "."; otherwise the empty string.

  3. Let publicSuffix be the public suffix determined by running the Public Suffix List algorithm with host as domain. [PSL]

  4. Assert: publicSuffix is an ASCII string that does not end with ".".

  5. Return publicSuffix and trailingDot concatenated.

To obtain the registrable domain of a host host, run these steps. They return null or a domain formed by host’s public suffix and the domain label preceding it, if any.

  1. If host’s public suffix is null or host’s public suffix equals host, then return null.

  2. Let trailingDot be "." if host ends with "."; otherwise the empty string.

  3. Let registrableDomain be the registrable domain determined by running the Public Suffix List algorithm with host as domain. [PSL]

  4. Assert: registrableDomain is an ASCII string that does not end with ".".

  5. Return registrableDomain and trailingDot concatenated.

Host input Public suffix Registrable domain
com com null
example.com com example.com
www.example.com com example.com
sub.www.example.com com example.com
EXAMPLE.COM com example.com
example.com. com. example.com.
github.io github.io null
whatwg.github.io github.io whatwg.github.io
إختبار xn--kgbechtv null
example.إختبار xn--kgbechtv example.xn--kgbechtv
sub.example.إختبار xn--kgbechtv example.xn--kgbechtv
[2001:0db8:85a3:0000:0000:8a2e:0370:7334] null null

Specifications should prefer the origin concept for security decisions. The notion of "public suffix" and "registrable domain" cannot be relied-upon to provide a hard security boundary, as the public suffix list will diverge from client to client. Specifications which ignore this advice are encouraged to carefully consider whether URLs' schemes ought to be incorporated into any decisions made, i.e. whether to use the same site or schemelessly same site concepts.

3.3. IDNA

The domain to ASCII algorithm, given a string domain and a boolean beStrict, runs these steps:

  1. Let result be the result of running Unicode ToASCII with domain_name set to domain, CheckHyphens set to beStrict, CheckBidi set to true, CheckJoiners set to true, UseSTD3ASCIIRules set to beStrict, Transitional_Processing set to false, VerifyDnsLength set to beStrict, and IgnoreInvalidPunycode set to false. [UTS46]

    If beStrict is false, domain is an ASCII string, and strictly splitting domain on U+002E (.) does not produce any item that