# Adding Domains

- [Introduction](#introduction)
- [Adding a Domain](#adding-a-domain)
    - [Adding a Domain Using AI](#adding-a-domain-using-ai)
    - [Adding a Domain From the Command Line](#adding-a-domain-from-the-command-line)
    - [Domain Fields](#domain-fields)
    - [Host Headers](#host-headers)
- [Hosting Platforms](#hosting-platforms)
    - [Your Own Server](#your-own-server)
    - [Laravel Vapor](#laravel-vapor)
    - [AWS CloudFront](#aws-cloudfront)
    - [Vercel](#vercel)
    - [Laravel Cloud, Netlify, Heroku, and Render](#laravel-cloud-netlify-heroku-and-render)
    - [Reading the Visitor's Host](#reading-the-visitors-host)
- [Going Live](#going-live)
    - [Verifying Ownership](#verifying-ownership)
    - [Trying the Preview Link](#trying-the-preview-link)
    - [Pointing DNS at the Edge](#pointing-dns-at-the-edge)
- [Protection Modes](#protection-modes)
- [Origin Settings](#origin-settings)
- [Pausing and Removing Domains](#pausing-and-removing-domains)
- [Domain Limits](#domain-limits)

## Introduction

A domain is a front door: the hostname your visitors type, such as `shop.example.com`. When you add a domain to NiceTryBot, you also tell it where your application actually lives, which we call the **origin**. Once the domain's DNS points at the edge, every visitor passes through NiceTryBot on the way to your origin.

Adding a domain never interrupts your visitors. Nothing changes until you verify the domain and point its DNS records at the edge, and even then every domain starts in [observe mode](#protection-modes), where NiceTryBot watches and tags traffic but lets everyone through.

## Adding a Domain

To add a domain, open the cog menu in the top right of the control panel and choose **Domains & origins**. Then click **Add domain**.

### Adding a Domain Using AI

The **Add domain** modal includes an **Add with AI** button, which copies a prompt for Claude Code, Cursor, or another coding agent. Paste it into an agent that is working in your application's repository and it will:

<div class="content-list" markdown="1">

- Ask you to connect it to NiceTryBot's [MCP server](https://docs.nicetrybot.com/docs/0.x/mcp.md) with an API token, which an administrator creates in **Admin → API tokens**.
- Work out your public hostname, an origin hostname, and the endpoints machines call, and check them with you.
- Serve your application at the origin hostname.
- Add the domain to NiceTryBot and verify it.
- Make your origin refuse requests that skip the edge.
- Test everything, then stop so you can try it before any DNS changes.
- Point DNS at the edge only when you say so.

</div>

If you would rather point an agent at this page directly, you may use a prompt like the following:

```text
Read https://docs.nicetrybot.com/docs/0.x/domains.md and https://docs.nicetrybot.com/docs/0.x/origins.md, then help me put my app behind NiceTryBot. Check every hostname and DNS record with me before you change it.
```

### Adding a Domain From the Command Line

You may also add a domain using the [`nicetrybot` command](https://docs.nicetrybot.com/docs/0.x/cli.md), on your server or from your laptop:

```shell
nicetrybot domains add claims.example.com claims-origin.example.com
```

The command accepts the same settings as the modal, such as `--www` and `--host-header=rewrite`, and prints the records and origin secret you need for the steps below.

### Domain Fields

The **Add domain** modal asks for the following:

| Field | Description |
| --- | --- |
| **Domain** | The hostname visitors type, without `https://` or a path. For example, `claims.example.com`. Each domain may only be added once per installation. |
| **Serve www. too** | When checked, visitors to `www.` in front of your domain get the same site, counted under the same domain. Turn it off for a subdomain nobody types `www.` in front of. |
| **Origin URL** | Where the edge forwards human traffic, such as `https://app.example.net`. It must use `http` or `https` and must be a different host than the domain, or requests would loop back to the edge. |
| **Host header** | Which `Host` header your origin receives. See [Host Headers](#host-headers) below. |

Once you click **Add domain**, NiceTryBot creates the domain, generates its verification token and origin secret, and takes you to the domain's page to finish setup.

### Host Headers

By default, NiceTryBot will **pass the visitor's host through**, so your origin receives the same `Host` header the visitor sent. This is what most applications expect.

Some platforms route requests by `Host` header, such as Laravel Vapor, Vercel, Netlify, Heroku, Render, and CloudFront. For these, choose **Rewrite to the origin's host** instead. [Hosting Platforms](#hosting-platforms) has the exact command for each. Your origin will receive the origin URL's host, or the value you enter under **Host header sent to the origin**. When rewriting, your application should build links from its configured URL rather than the incoming `Host` header.

If a shared origin needs to know which site a visitor asked for, enter one or more header names under **Forward the visitor's host in a header**, such as `User-Custom-Domain`. The edge always sends `X-Forwarded-Host` as well.

## Hosting Platforms

The right settings depend on where your origin is hosted. Find your platform below, copy its command, and swap in your own hostnames. Each command prints the steps to [go live](#going-live), including the origin secret.

> [!TIP]
> Not sure which platform you're on? Your agent can tell from your repository. Files like `vapor.yml`, `vercel.json`, `netlify.toml`, `Procfile`, and `render.yaml` give it away. Ask it to read this section and give you the command for your app.

### Your Own Server

If your application runs on a server you manage, such as one provisioned by Laravel Forge or Ploi, or any server running Nginx, Caddy, or Apache, the defaults are all you need. Point a second hostname, such as `shop-origin.example.com`, at your server and add it to your site's domains (an **alias** in Forge) so it gets a TLS certificate. Then add the domain:

```shell
nicetrybot domains add shop.example.com shop-origin.example.com
```

Your application keeps receiving the visitor's own `Host` header, so nothing else about it needs to change.

### Laravel Vapor

Vapor serves your application through CloudFront, which only answers hostnames it knows. Give your environment an origin hostname, as you would any Vapor custom domain, and deploy:

```yaml filename=vapor.yml
environments:
    production:
        domain:
            - docs-edge.example.com
```

Then add the domain, rewriting the `Host` header to the origin's own:

```shell
nicetrybot domains add docs.example.com docs-edge.example.com \
  --host-header=rewrite \
  --forwarded-host-header=X-Forwarded-Host
```

Your application now receives `docs-edge.example.com` as its host, so set `APP_URL` to your public URL and Laravel will build links that point back through the edge:

```ini
APP_URL=https://docs.example.com
```

### AWS CloudFront

If your origin sits behind a CloudFront distribution you manage, such as one in front of a load balancer or an EC2 instance, use the distribution's domain name as the origin:

```shell
nicetrybot domains add shop.example.com d111111abcdef8.cloudfront.net \
  --host-header=rewrite \
  --forwarded-host-header=X-Forwarded-Host
```

CloudFront only passes along the request headers its origin request policy allows, so by default your application would never see the origin secret. In the distribution's behavior, set the **origin request policy** to the managed `AllViewerExceptHostHeader` policy, which forwards every header the edge sends, including `X-NiceTry-Origin-Secret` and `X-Forwarded-Host`.

> [!WARNING]
> CloudFront answers cached responses without asking your origin, and anyone can request your distribution's domain directly. Use the managed `CachingDisabled` cache policy for your pages so every request reaches your [origin lock](https://docs.nicetrybot.com/docs/0.x/origins.md). Caching static assets is fine.

### Vercel

Vercel replaces `X-Forwarded-Host` with its own hostname before your application sees it, so name a header of your own to carry the visitor's host:

```shell
nicetrybot domains add shop.example.com my-app.vercel.app \
  --host-header=rewrite \
  --forwarded-host-header=X-Visitor-Host
```

Use your project's production hostname as the origin, not a preview deployment URL. Then configure your application's public URL, such as `https://shop.example.com`, so the links it builds point back through the edge.

### Laravel Cloud, Netlify, Heroku, and Render

These platforms route by `Host` header too. Use the hostname your platform gave your application as the origin:

| Platform | Origin |
| --- | --- |
| Laravel Cloud | `my-app-main-a1b2c3.laravel.cloud` |
| Netlify | `my-site.netlify.app` |
| Heroku | `my-app-1a2b3c4d5e6f.herokuapp.com` |
| Render | `my-app.onrender.com` |

Then add the domain. A header name of your own, such as `X-Visitor-Host`, reaches your application untouched on every platform:

```shell
nicetrybot domains add shop.example.com my-site.netlify.app \
  --host-header=rewrite \
  --forwarded-host-header=X-Visitor-Host
```

As with Vercel, configure your application's public URL so its links point back through the edge. For a Laravel application, that is `APP_URL`.

### Reading the Visitor's Host

An application that serves a single site only needs its public URL configured. If one application serves several sites, read the visitor's host from the header you chose:

```php
$site = $request->header('X-Visitor-Host', $request->getHost());
```

Anyone can send that header to an unlocked origin, so only trust it once your origin [refuses requests that skip the edge](https://docs.nicetrybot.com/docs/0.x/origins.md#locking-your-origin).

## Going Live

Each domain's page includes a **Go live** panel with the steps needed to route the domain through NiceTryBot. The steps are designed to be completed in order.

### Verifying Ownership

First, prove the domain is yours by adding a `TXT` record at your DNS provider. The record's name and value are shown on the domain's page, and look like this:

| Type | Name | Value |
| --- | --- | --- |
| `TXT` | `_nicetrybot.shop.example.com` | `nicetrybot-verify=3f9a…` |

Once the record exists, click **Check TXT record**. NiceTryBot looks up the record using public DNS and marks the domain as **Live** when the value matches. A brand new record can take a few minutes to appear, so if the check doesn't find it right away, wait a moment and try again.

After the domain is verified, you may remove the `TXT` record whenever you like.

> [!NOTE]
> The verification check runs on your queue. If the check never finishes, make sure the `worker` service is running using `nicetrybot status`.

### Trying the Preview Link

If your installation has a preview zone configured using the `NICETRY_PREVIEW_ZONE` environment variable, each domain gets a preview hostname. For example, `shop.example.com` becomes `shop-example-com.preview.example.com`. The preview link serves your site through the edge before you change any DNS, and your origin is asked for the real hostname, so it serves exactly what your visitors will get.

Preview hostnames are disabled when `NICETRY_PREVIEW_ZONE` is empty, which is the default.

### Pointing DNS at the Edge

Next, point the domain at your NiceTryBot server. Subdomains use a `CNAME` record pointing at your edge's hostname, which is the value of `NICETRY_EDGE_HOSTNAME`:

| Type | Name | Value |
| --- | --- | --- |
| `CNAME` | `shop.example.com` | `panel.example.com` |
| `CNAME` | `www.shop.example.com` | `panel.example.com` |

Apex domains, such as `example.com`, can't use a `CNAME` record. Instead, add an `A` record for each of your edge's addresses, which are the values of `NICETRY_EDGE_ADDRESSES`:

| Type | Name | Value |
| --- | --- | --- |
| `A` | `example.com` | `203.0.113.10` |

The domain's page shows the exact records for your installation. Then, click **Check DNS** to confirm the records are in place.

> [!WARNING]
> Delete any other `A`, `AAAA`, or `ALIAS` records for the name first. The edge only listens on IPv4, so a leftover `AAAA` record would let IPv6 visitors skip NiceTryBot entirely. The **Check DNS** button only passes once every address points at the edge and no `AAAA` records remain.

If you would like to be able to roll back quickly, lower the TTL on your existing records a few hours before you switch them. Once DNS points at the edge, TLS certificates are issued automatically the first time a visitor arrives.

Finally, the last step of the **Go live** panel is to lock your origin so that only the edge may reach it. This step is important enough to have its own page: [Protecting Your Origins](https://docs.nicetrybot.com/docs/0.x/origins.md).

## Protection Modes

Every domain has a protection mode, which decides what NiceTryBot does with the traffic it tags. You may change a domain's mode at any time from the **Protection mode** panel on its page:

| Mode | Description |
| --- | --- |
| **Off** | Proxy and log only. No tagging. |
| **Observe** | Tag suspicious traffic, but let it through. This is the default for new domains. |
| **Enforce** | Send tagged traffic to the gym or block it. |

We recommend leaving a new domain in **Observe** mode for a few days. You can watch what NiceTryBot would have done in **Activity**, tune your [rules](https://docs.nicetrybot.com/docs/0.x/concepts.md#rules), and switch to **Enforce** once you are comfortable. The [bot gym](https://docs.nicetrybot.com/docs/0.x/concepts.md#the-bot-gym) and tarpits only hold visitors on domains in **Enforce** mode.

> [!NOTE]
> If the edge can't reach the control panel, it fails open and sends everyone to your origin. An outage of the panel will never take your sites down with it.

## Origin Settings

The **Origin** panel on a domain's page controls how the edge talks to your application:

| Setting | Description |
| --- | --- |
| **Origin URL** | Where human traffic is forwarded. |
| **Timeout (seconds)** | How long the edge waits for your origin, from 5 to 120 seconds. Defaults to 30. |
| **Host header** | Pass the visitor's host through, or rewrite it. See [Host Headers](#host-headers). |
| **Server header visitors see** | The `Server` header the edge sends. Defaults to `nginx`. Pretending to be something old and unpatched can make a honeypot more tempting. |
| **HSTS** | Off, on for a year, or on with subdomains and preload. Preloading is hard to undo, so enable it with care. |
| **Let browsers cache /img, /css and /js** | Caches images for a day and CSS and JavaScript for a week. |
| **Serve www. too** | Whether `www.` in front of the domain serves the same site. |

The edge picks up new domains and changed settings within about 30 seconds. There is no need to restart anything.

## Pausing and Removing Domains

Once a domain is verified, you may **Pause** protection from its page. A paused domain still routes through the edge, but protection stops and traffic flows straight to your origin. Click **Resume** to turn protection back on.

To remove a domain entirely, click **Remove domain**, or run `nicetrybot domains remove` with its hostname. Removing a domain stops protection and deletes its request history, which can't be undone.

> [!WARNING]
> Point the domain's DNS back at your origin **before** removing it. Otherwise, visitors will still arrive at the edge, which no longer knows where to send them.

## Domain Limits

Each NiceTryBot installation protects up to 10 domains for free. Every domain on the **Domains & origins** page counts toward the limit, including domains that are still awaiting verification. To protect more, an administrator may add a **More domains** licence key in **Admin → Add-ons**. Each pack adds 100 domains, and packs stack. You can learn more about licence keys in the [add-ons documentation](https://docs.nicetrybot.com/docs/0.x/addons.md#licence-keys).
