# Staging and production environments

Hostname patterns that decide where the widget loads, which lane feedback goes into, and what counts as production.

Source: https://docs.annot8.app/docs/dashboard/environments



An environment is a name plus a set of hostname patterns. On every page load the
widget reports its hostname and Annot8 matches it against the project's
environments.

Manage them in **Project → Settings → Environments**.

## Why they matter [#why-they-matter]

Three things depend on the match:

1. **Whether the widget loads at all.** A hostname matching no environment gets
   nothing. This is the security boundary — it's what stops your script tag
   being lifted onto someone else's site.
2. **Which lane the feedback lands in.** Threads are filed against the matched
   environment and the inbox can filter on it, so a local experiment never
   pollutes production triage.
3. **Whether production rules apply.** On a production environment the widget
   stays hidden unless you explicitly enable it.

## Patterns [#patterns]

| Pattern            | Matches                        |
| ------------------ | ------------------------------ |
| `app.example.com`  | That host exactly              |
| `*.example.com`    | Any subdomain of `example.com` |
| `*.vercel.app`     | Preview deployments            |
| `pr-*.example.com` | A prefix wildcard              |
| `localhost`        | Local development              |
| `127.0.0.1`        | Local by IP                    |

A single field can hold several patterns separated by spaces or commas:

```
localhost *.local 127.0.0.1
```

The most specific matching pattern wins, so `app.example.com` beats
`*.example.com` when both would match.

<Callout type="warn">
  A bare `*` is rejected. There's no way to make the widget embeddable from any
  origin — that's deliberate, since it's exactly what a stolen script tag would
  need.
</Callout>

Patterns are normalised before matching: a scheme, port, path, query, or leading
`www.` is stripped, so pasting `https://app.example.com/dashboard` stores
`app.example.com`.

## A typical setup [#a-typical-setup]

| Environment | Pattern                            |
| ----------- | ---------------------------------- |
| Production  | `example.com *.example.com`        |
| Staging     | `staging.example.com *.vercel.app` |
| Development | `localhost *.local 127.0.0.1`      |

<Callout type="info">
  The Free plan allows **2 environments** per project. Paid plans are unlimited.
</Callout>

## What counts as production [#what-counts-as-production]

An environment is treated as production if it's the project's default
environment, or if it's literally named "Production".

On a production environment the entire widget panel — annotation *and* live chat
— is hidden unless **Show widget in production** is on in **Settings →
Features**. [Analytics](/docs/analytics/overview) is the exception and keeps
collecting either way.

The default environment can't be deleted.

## Why the widget isn't showing up locally [#why-the-widget-isnt-showing-up-locally]

Nine times out of ten: the hostname doesn't match any environment. Add a
Development environment with `localhost *.local 127.0.0.1` and reload.

If you're on a non-standard host — a `.test` domain, a LAN IP, a tunnel URL from
ngrok or Cloudflare — add that pattern too. The port is ignored, so
`localhost` covers `localhost:3000` and `localhost:5173` alike.

See [Troubleshooting](/docs/troubleshooting) for the rest.
