# Allowed domains

> What the list of allowed domains of a site protects, how a page's hostname is matched against it, and what it does not protect.

Each site has a list of allowed domains. When the page's hostname is not allowed, the loader mounts no
component, the page stays as it is, and the console gets one warning that links here. The same rule is
used by the API (when you save the list) and by data ingest.

## What it protects, and what it does not

- **It protects** against a snippet copied to another site, or a forgotten staging page: the component
  does not mount where it is not meant to.
- **It does not protect** the manifest, the loader or the bundles: they are public. It is not access
  control. The real barrier for collected data is the ingest, which rejects requests whose `Origin` is not
  in the list.
- A visitor who forges their own browser only fools themselves; a third-party site cannot change its own
  hostname.

## How a hostname is matched

The hostname is the page's `location.hostname`: no port, no scheme, no path. Case and a trailing dot are
ignored.

| Page hostname            | List                          | Mounts? | Why                                                         |
| ------------------------ | ----------------------------- | ------- | ----------------------------------------------------------- |
| `allowed.test`           | `allowed.test`                | yes     | equal                                                       |
| `www.allowed.test`       | `allowed.test`                | yes     | `www.` counts as the plain entry                            |
| `blog.allowed.test`      | `allowed.test`                | no      | a plain entry does not cover a subdomain                    |
| `blog.allowed.test`      | `*.allowed.test`              | yes     | the wildcard covers exactly one label                       |
| `a.b.allowed.test`       | `*.allowed.test`              | no      | two labels                                                  |
| `allowed.test`           | `*.allowed.test`              | no      | the wildcard does not cover the bare domain: list both      |
| `evilallowed.test`       | `allowed.test`                | no      | a similar suffix is not a subdomain                         |
| any IP address           | any                           | no      | an IP never matches (the API refuses an IP in the list)     |
| `localhost`              | `localhost`                   | yes     | the API accepts `localhost` in the list of a `staging` site |
| any page                 | empty list, `staging` site    | yes     | development                                                 |
| any page                 | empty list, `production` site | no      | it fails closed                                             |
| `file://`, `about:blank` | any                           | no      | no hostname                                                 |

An internationalized name is stored in punycode (`xn--bcher-kva.example` for `bücher.example`), which is
what the browser reports. If the check itself fails in the browser, the component is not mounted and the
page is untouched.

## The warning appears once per page

The warning is printed only when `window.taggor` does not exist yet, so five components or a second
snippet on the same page produce one message.

## Fix it

Add the hostname to the site's allowed domains in the portal (edit the site), or pass `--domain` when
creating the site with the CLI, then wait up to about two minutes for the change to reach visitors. More
cases are on [Troubleshooting](https://taggor.dev/docs/troubleshooting.md).
