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:
- An existing Public Suffix List library available in the driver’s language.
- The copy of the list vendored in this repository as public_suffix_list.dat, parsed as described in Rule syntax and applied as described in Determining the public suffix.
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:
| Form | Example | Meaning |
|---|---|---|
| Ordinary rule | com.ac | Matches exactly these labels. |
| Wildcard rule | *.ck | * matches exactly one label – never zero, and never more than one. |
| Exception rule | !www.ck | Overrides 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 ..
- 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. - If no rule matches, the prevailing rule is
*– that is, the rightmost label alone is the public suffix. - If any matching rule is an exception rule, it prevails. Otherwise, the matching rule with the most labels prevails.
- If the prevailing rule is an exception rule, remove its leftmost label.
- 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.