Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Public Suffix List

  • Status: Draft
  • Minimum Server Version: N/A

Abstract

The Public Suffix List (PSL) enumerates the DNS suffixes under which the public may register names. Determining whether a hostname is a public suffix requires consulting this list; it cannot be derived from the hostname alone.

This document vendors the list into this repository as public_suffix_list.dat and specifies how to parse it and determine the public suffix of a domain. Drivers whose language offers a maintained Public Suffix List library may use that instead; drivers that do not MUST use the vendored copy, obtained from this repository, so that they share a single version-controlled copy of the list.

META

The keywords “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.

Specification

Terms

rule

A single entry in the list, consisting of a sequence of labels separated by dots.

public suffix

The portion of a domain under which the public may register names, as determined by the algorithm in Determining the public suffix.

Obtaining the list

Drivers MUST determine the public suffix of a domain using either of the following:

A driver that does not use a library MUST use the copy vendored here, and MUST obtain it from this repository rather than from publicsuffix.org or any other source. How a driver takes delivery of the file is up to it – copying it into its own repository and refreshing that copy from here is expected – but the contents MUST match public_suffix_list.dat as committed.

Whichever a driver chooses, the parsing/usage of the PSL MUST pass the tests described in the Test Plan.

Drivers SHOULD NOT fetch the list from the network at runtime, and SHOULD instead resolve it from a copy shipped with the driver.

A driver using a library is responsible for that library’s behavior. In particular, drivers SHOULD confirm that the library implements wildcard and exception rules, and that the copy of the list it embeds is kept reasonably current.

The vendored file

The vendored file is generated by etc/sync-psl.py and MUST NOT be edited by hand.

It is encoded as UTF-8 and uses LF line endings, and ends with a single trailing newline. Every line is a single rule: comment and blank lines have already been removed, so there are none for a parser to skip.

Rule syntax

This section and Determining the public suffix describe how to parse and apply the vendored file. They apply to drivers that use the vendored file; a driver that uses a library satisfies them through that library.

A rule takes one of three forms:

FormExampleMeaning
Ordinary rulecom.acMatches exactly these labels.
Wildcard rule*.ck* matches exactly one label – never zero, and never more than one.
Exception rule!www.ckOverrides a wildcard rule that would otherwise match.

A * only ever appears as the leftmost label of a rule.

All rules are lowercase, and internationalized labels are stored as Unicode rather than Punycode. A driver using the vendored file MUST therefore convert one side before comparing, so that a Punycode-encoded hostname (one containing xn-- labels) and the Unicode rules it is compared against are in the same form. Comparing the two forms directly will fail to match rules that should match.

Note: The leading * and ! markers in the PSL are not part of a label and MUST NOT be included in that conversion. A driver MUST strip them before converting a rule and reapply them after, so that a rule such as !公司.cn converts to !xn--55qx5d.cn rather than being mangled.

Determining the public suffix

To determine the public suffix of a domain, a driver using the vendored file MUST follow the algorithm published by the publicsuffix folks, pasted below for easy reference.

Before applying this algorithm, the domain MUST be canonicalized in the normal way for hostnames — lower-case and Punycode — and MUST NOT contain empty labels, including a leading or trailing ..

  1. Compare the domain’s labels against each rule’s labels from right to left, treating * as matching any single label. Collect every rule that matches.
  2. If no rule matches, the prevailing rule is * – that is, the rightmost label alone is the public suffix.
  3. If any matching rule is an exception rule, it prevails. Otherwise, the matching rule with the most labels prevails.
  4. If the prevailing rule is an exception rule, remove its leftmost label.
  5. The public suffix is the set of the domain’s labels matched by the prevailing rule.

A domain is itself a public suffix if and only if the public suffix determined by this algorithm is equal to the domain.

For example, given the rules ck, *.ck, and !www.ck: the public suffix of a.b.ck is b.ck, because *.ck prevails; but the public suffix of www.ck is ck, because the exception rule !www.ck prevails and has its leftmost label removed.

Test Plan

See tests/README.md. It defines prose tests covering each rule form, and points at the srvAllowedHostsSuffix-psl-* tests in the Initial DNS Seedlist Discovery tests that cover the two cases observable through a connection string.

Design Rationale

Why the list is vendored

Vendoring the list makes the behavior of a given release reproducible and makes each change to the list an auditable commit, and it gives drivers without a suitable language library something to copy rather than each deciding where to get the list from.

Runtime fetching is discouraged rather than forbidden. It makes behavior depend on network availability and on when the client happened to start, so two clients running the same driver version can disagree about whether a hostname is a public suffix. That is a reason to prefer a copy shipped with the driver, but not a reason to block a driver whose language ecosystem expects a dependency like this to be resolved at install or start time.

Why a language library is permitted

Several languages have a well-established PSL library, and requiring drivers in those languages to hand-roll the matching algorithm instead would mean reimplementing wildcard and exception handling that is already tested upstream. Because the tests assert observable behavior rather than the source of the list, either choice can be verified.

The trade-off is that a library embeds its own copy of the list on its own update schedule, so drivers using one give up the guarantee that every driver agrees on the same snapshot. That is acceptable here: the majority of suffixes that matter for validating a caller-supplied host pattern are likely to be long-established entries rather than recent additions.

Why comments are stripped

Upstream interleaves rules with comments that carry no normative meaning, including Punycode spellings of the internationalized rules. Removing them means every line of the vendored file is a rule, which keeps parsers trivial and keeps the diff of a monthly sync limited to actual rule changes.

Why the ICANN and private sections are not distinguished

Upstream splits the list into an ICANN section, containing suffixes delegated through the DNS registry hierarchy (com, co.uk), and a private section, containing domains whose owners hand out subdomains to unrelated third parties (github.io, herokuapp.com). Because the section markers are comments, they are not preserved, and the two are indistinguishable in the vendored file.

This is deliberate: both kinds of suffix are equally unsuitable as a caller-supplied host pattern, which is the use this list is vendored for.

Maintenance

etc/sync-psl.py regenerates public_suffix_list.dat from upstream:

python source/public-suffix-list/etc/sync-psl.py

The script downloads the list from https://publicsuffix.org/list/public_suffix_list.dat, sanity checks that the expected section markers are present, strips comment and blank lines, and writes the result. Passing --check reports whether the committed file is up to date without writing anything, exiting non-zero if it is stale.

The sync-psl GitHub Actions workflow runs the script on the first day of each month and opens a pull request when the regenerated file differs from the committed one. It can also be triggered manually via workflow_dispatch.

Upstream license and attribution

The Public Suffix List is maintained by the Mozilla Foundation and made available under the Mozilla Public License, v. 2.0. Upstream distributes the list with the following header, which etc/sync-psl.py strips along with the rest of the comments:

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

That notice applies to public_suffix_list.dat in this directory. The rules themselves are reproduced verbatim and in their original order; only the surrounding comments and blank lines have been removed. The notice does not apply to the rest of this repository.

Changelog

  • 2026-09-03: Clarify that markers shouldn’t be converted to Punycode and add a prose test covering internationalized (Punycode vs. Unicode) rules.

  • 2026-08-11: Vendor the Public Suffix List and add a script to sync it.