# Use your own domain for a join address

> Let players join a game server at a domain you own, such as play.example.com, with a CNAME record.

Source: https://docs.coritan.com/proxies/join-addresses/custom-domain/

In the dashboard:

- /dashboard/proxies/game/…/domain: https://www.coritan.com/dashboard/proxies/game
- /dashboard/proxies/external/…/domain: https://www.coritan.com/dashboard/proxies/external

A *custom domain* lets players join a game server at a name you own, such as `play.example.com`, as well as at its join address. You point the name at the join address with a `CNAME` record, and Coritan checks the record before the name starts to work. Each server has at most one custom domain, and so does each external server.

## Before you begin

- You need a **Minecraft Java** or **Minecraft Bedrock** join address ([Create a join address](/proxies/join-addresses/create-a-join-address/)), or an [external server](/proxies/external-servers/). A **TCP service** join address cannot take a custom domain.
- The server must not be suspended. While it is, the **Custom domain** tab shows the domain, but you cannot change it.
- You need to be able to add DNS records for the domain. When the domain's DNS zone is on your Coritan account, or the name is under a `coritan.gg` name you hold, Coritan writes the record for you.

## Choose the name

You enter the name in two parts: the **Root domain**, such as `example.com`, and a **Label** in front of it.

| Label | Players join at | Record Coritan checks |
|---|---|---|
| `play`, or another single label | `play.example.com` | A `CNAME` from `play.example.com` to the join address |
| `@` | The root domain itself, such as `example.com` | The root's own record ([Link the root of a domain](#link-the-root-of-a-domain)) |
| `*` | Any name one level below the root domain, such as `survival.example.com` | A `CNAME` from `_coritan-verify.example.com` to the join address |

A label takes lowercase letters, digits and hyphens, with no hyphen at either end, and up to 63 characters. The whole name can be up to 253 characters. To link a name deeper in your domain, such as `mc.example.com`, enter `example.com` in **Root domain** and `mc` in **Label**.

You can link a name under `coritan.gg` only below a name you hold. With `alex.coritan.gg` claimed, you can link `play.alex.coritan.gg` ([Claim a free coritan.gg name](/proxies/coritan-gg-names/)).

### Link the root of a domain

DNS does not allow a `CNAME` at the root of a zone, such as `example.com`. Some DNS providers can flatten one there, under names such as CNAME flattening, `ALIAS` or `ANAME`: the provider looks up the join address and publishes its addresses at the root. Coritan accepts the root when every address it finds there belongs to Coritan's edge.

You cannot link the root of a DNS zone on your Coritan account, or of a `coritan.gg` name you hold. Link a label such as `play` there instead.

### Link a wildcard

A wildcard covers every name one level below the root domain: `*.example.com` covers `survival.example.com`, but not `lobby.survival.example.com`. Coritan cannot look up a wildcard name, so it checks `_coritan-verify.example.com` instead. Publish both records that the tab lists.

## Link a domain

1. Open the join address or external server from [Edge Proxy](https://www.coritan.com/dashboard/proxies/game), and select the **Custom domain** tab. **Link one** on the **Overview** tab opens it too.
2. In **Root domain**, enter your domain, such as `example.com`. The names you hold under `coritan.gg` are listed below the field. Select one to fill in the field, with `play` in **Label**.
3. In **Label**, enter `play`, another label, `@` or `*` ([Choose the name](#choose-the-name)).
4. At your DNS provider, add the record that the card lists under **Publish this record, then link**, with the **Type**, **Name** and **Value** shown. At most DNS providers, you enter only the part before your domain, such as `play`, as the record's name.

    You can skip this step when Coritan writes the record for you. For a `coritan.gg` name you hold, the card is titled **Link a name you hold here** and lists the record under **Record created when you link**. For a domain whose DNS zone is on your Coritan account, the card still asks you to publish the record, but Coritan writes it when you link.
5. Select **Link domain**. Coritan looks for the record at once, which can take a few seconds.

**Use this hostname in the public server list** chooses which address a public list of servers shows for your server: the domain, once it is verified, or the join address. coritan.com does not publish a list of servers, so the box changes nothing for a server on your coritan.com account.

## Result

The tab shows the domain with a badge:

- `Verified`: Coritan found the record, and players can join at the domain now. When Coritan wrote the record for you, the message reads `play.example.com linked. The CNAME was written in your zone.`
- `Pending`: Coritan saved the domain but cannot see the record yet. The message reads `play.example.com saved; it links once its CNAME is visible.`, and the tab shows `Waiting for the DNS record`. DNS changes can take up to an hour to reach everyone. Coritan checks every few minutes and verifies the domain when the record appears. Select **Check now** to check at once.

Players join at the domain the way they join at the join address: Java players type `play.example.com`, and Bedrock players add port `19132`. The join address keeps working. On [Edge Proxy](https://www.coritan.com/dashboard/proxies/game), the domain appears under the join address, with `CNAME pending` until it is verified.

When the domain's DNS zone is on Coritan, Coritan also issues a certificate for the name once it is verified, and Bedrock players join without a prompt. With the zone at another DNS provider, a Bedrock player's game can ask them to trust the server the first time they join.

## Keep the record published

Coritan checks the record every few minutes for as long as the domain is linked. **Last seen** shows when it last found the record, and **Last checked** shows when it last looked. **Record to keep published** lists the record to leave in place.

A single missed check changes nothing. After 6 checks in a row miss the record, the badge shows `CNAME lost`, the tab shows `The CNAME record is gone`, and players can no longer join at the domain. The join address keeps working. On [Edge Proxy](https://www.coritan.com/dashboard/proxies/game), the row shows `CNAME lost` and counts under **Needs attention**.

Coritan goes on checking a lost domain. It returns to `Verified` at the first check that finds the record again, or when you select **Check now** after you publish it.

## Unlink a domain

1. On the **Custom domain** tab, select **Unlink domain…**.
2. Type the hostname, such as `play.example.com`, to confirm, and select **Unlink domain**.

> [!WARNING]
> Players who join at the domain can no longer reach the server once you unlink it. The join address is not affected.

The record stays in your DNS, whether you published it or Coritan wrote it. Delete it there if you no longer need it ([Add, edit and delete DNS records](/websites/dns/manage-dns-records/)). You can link the domain again later. To link a different domain, unlink this one first.

When you delete a server, or an external server's plan ends, Coritan unlinks its domain, and anyone can link the name again.

## Troubleshooting

**Link domain** stays greyed out
: **Root domain** is empty or is not a full domain such as `example.com`, or **Label** is not `@`, `*` or a single label ([Choose the name](#choose-the-name)). Correct the field, and the button becomes available.

`coritan.gg domains can't be linked as custom domains`
: The name is under `coritan.gg`, and you do not hold the name above it. Claim a name and link a label under it, such as `play.alex.coritan.gg` ([Claim a free coritan.gg name](/proxies/coritan-gg-names/)), or link a domain of your own. The message names a different join domain when your name is under that one.

`A CNAME can't sit at the root of a zone hosted here. Use a subdomain such as play.`
: The name is the root of a DNS zone on your Coritan account, or of a `coritan.gg` name you hold, or it is a deeper name you entered with `@` as its label. Enter a label such as `play`. For a name such as `mc.example.com`, enter `example.com` in **Root domain** and `mc` in **Label**.

`This server has no join address to point a domain at`
: The server has no active **Minecraft Java** or **Minecraft Bedrock** join address. Its only join address may be a **TCP service**, or the server may be suspended. [Create a join address](/proxies/join-addresses/create-a-join-address/) for Java or Bedrock and link the domain there.

`That hostname is already linked to another server`
: Another server has this name as its custom domain. Unlink it there first. If none of your servers has it, [contact support](/support/).

`That hostname is already in use`
: A join address already answers at this name. Choose another name, or [contact support](/support/) if the name is yours.

The badge stays `Pending`
: Coritan cannot see a `CNAME` from the name in **Hostname** to the name in **Points at**. Check the record's name at your DNS provider, and check that it is a `CNAME` to the name in **Points at**. For a server with more than one join address, that is the join address you created first. If your DNS provider can send traffic through its own network, turn that off for this record so that lookups return the `CNAME` itself. Then select **Check now**. When the tab shows `The join address changed`, point the record at the join address that the message names.

The badge stays `Pending` for a domain whose DNS zone is on your Coritan account
: The name already had an `A`, `AAAA` or `CNAME` record, so Coritan did not write its own. Replace that record with a `CNAME` to the name in **Points at** ([Add, edit and delete DNS records](/websites/dns/manage-dns-records/)), then select **Check now**.

The badge shows `Verified`, then `CNAME lost`, for a domain whose DNS zone is on your Coritan account
: Coritan wrote the record in the zone, but the domain does not use Coritan's nameservers yet, so nobody else can see the record. Change the domain's nameservers at your registrar ([Add a domain you already own](/websites/add-an-existing-domain/)).

The badge shows `CNAME lost`
: 6 checks in a row did not find the record ([Keep the record published](#keep-the-record-published)). Publish the record under **Record to keep published** again, then select **Check now**.

Players see `This proxy route is currently suspended.`
: Players' DNS already finds your record, but Coritan has not verified the domain yet, or has marked it `CNAME lost`. Select **Check now**, or wait for the next check.

Players see `There is no proxy associated with this domain.`
: The record points at the join address, but no custom domain with this name is linked. Link it on the **Custom domain** tab. With a wildcard, the name players typed may be more than one level below the root domain.

Java players reach a different server
: An `SRV` record, such as one for `_minecraft._tcp.play.example.com`, still points somewhere else. Java looks that record up before the `CNAME`, so delete it.

`Too many requests for this action. Please wait and try again.`
: You linked a domain, or selected **Check now**, more than 12 times in an hour for this server. Wait up to an hour. Coritan keeps checking the domain meanwhile.

## Related

- [How join addresses work](/proxies/join-addresses/)
- [How external servers work](/proxies/external-servers/)
- [Claim a free coritan.gg name](/proxies/coritan-gg-names/)
- [Add, edit and delete DNS records](/websites/dns/manage-dns-records/)

## With the API

Each request comes in two forms, with the same bodies and answers:

- For a join address, `/api/v1/gameproxy/routes/{route_id}/custom-domain`, where `route_id` is the join address's `id` ([Read join addresses with the API](/proxies/join-addresses/#read-join-addresses-with-the-api)).
- For an external server, `/api/v1/client/external-servers/{service_id}/custom-domain`, where `service_id` is the external server's `id` ([Read external servers with the API](/proxies/external-servers/#read-external-servers-with-the-api)).

Link a domain with `POST`:

```bash
curl -X POST https://api.coritan.com/api/v1/gameproxy/routes/42/custom-domain \
  -H "Authorization: Bearer $CORITAN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"domain": "example.com", "subdomain": "play"}'
```

| Field | Value |
|---|---|
| `domain` | Required. The root domain, such as `example.com`, in 1–255 characters. |
| `subdomain` | The label: a single label such as `play`, `@` for the root domain itself or `*` for a wildcard. Up to 63 characters. Defaults to `@`. |
| `include_in_list` | Whether a public server list shows the domain instead of the join address, once the domain is verified ([Link a domain](#link-a-domain)). Defaults to `false`. |

The answer describes the domain:

```json
{
  "linked": true,
  "hostname": "play.example.com",
  "domain": "example.com",
  "subdomain": "play",
  "probe_hostname": "play.example.com",
  "verified": false,
  "verification_status": "pending",
  "include_in_list": false,
  "expected_cname_target": "survival-smp.coritan.gg",
  "failed_checks": 0,
  "fail_threshold": 6,
  "last_checked_at": "2026-09-25T10:15:00",
  "last_verified_at": null,
  "last_error": "No CNAME from play.example.com to survival-smp.coritan.gg yet",
  "record_written": false
}
```

| Field | What it holds |
|---|---|
| `linked` | `true` while a domain is linked. |
| `hostname` | The name players join at. A wildcard reads `*.example.com`. |
| `domain`, `subdomain` | The root domain and the label. |
| `probe_hostname` | The name Coritan looks up: the hostname, or `_coritan-verify.example.com` for a wildcard. |
| `verification_status` | `pending`, `verified` or `failed`. The dashboard shows `failed` as `CNAME lost`. |
| `verified` | `true` while `verification_status` is `verified`. |
| `include_in_list` | The public server list setting. |
| `expected_cname_target` | The join address that the record must point at. |
| `failed_checks`, `fail_threshold` | How many checks in a row missed the record, and how many misses turn a verified domain into `failed`. |
| `last_checked_at`, `last_verified_at` | When Coritan last looked for the record, and when it last found it, in UTC. |
| `last_error` | Why the last check failed, or `null`. |
| `record_written` | Only in the answer to a link: `true` when Coritan wrote the record in a DNS zone on your account. |

A `POST` while a domain is linked replaces it with the new one. The other requests work like this:

- `GET` reads the domain. With nothing linked, `linked` is `false`, `expected_cname_target` is the join address, `fail_threshold` is `6`, and the other fields are `null`, `false` or `0`.
- `POST` to `/verify` checks the record at once and answers the same shape as `GET`.
- `PATCH` with `{"include_in_list": true}` or `{"include_in_list": false}` changes the public server list setting. The field is required.
- `DELETE` unlinks the domain and answers the shape for nothing linked. It succeeds when nothing is linked too.

Check an external server's domain now:

```bash
curl -X POST https://api.coritan.com/api/v1/client/external-servers/1203/custom-domain/verify \
  -H "Authorization: Bearer $CORITAN_TOKEN"
```

A link that Coritan refuses answers `400` with one of the messages in [Troubleshooting](#troubleshooting), or with `Enter a valid root domain (e.g. example.com)`, `Subdomain must be a DNS label, '@', or '*'` or `Enter a valid hostname` for a name that breaks the rules. `/verify` and `PATCH` answer `400` with `No custom domain is linked to this server` when nothing is linked. Other errors:

- `404` with `Route not found` for a join address that is not yours, or `External server not found` for an external server that is not yours.
- `409` with `This route is not attached to a service, so a domain cannot be linked to it`, for a join address created with an address instead of a server.
- `422` for a body without `domain`, or with a `subdomain` longer than 63 characters.
- `429` for more than 12 links, or more than 12 checks, of one server's domain in an hour, as [Rate limits](/api/rate-limits/) describes.

## API

- `GET /api/v1/gameproxy/routes/{route_id}/custom-domain`: Vanity CNAME state for this route's join address (https://docs.coritan.com/api/reference/client/game-proxy/#op-get-api-v1-gameproxy-routes-route-id-custom-domain)
- `POST /api/v1/gameproxy/routes/{route_id}/custom-domain`: Link route custom domain (https://docs.coritan.com/api/reference/client/game-proxy/#op-post-api-v1-gameproxy-routes-route-id-custom-domain)
- `PATCH /api/v1/gameproxy/routes/{route_id}/custom-domain`: Show the vanity hostname, or the hosted one, in the public server list (https://docs.coritan.com/api/reference/client/game-proxy/#op-patch-api-v1-gameproxy-routes-route-id-custom-domain)
- `DELETE /api/v1/gameproxy/routes/{route_id}/custom-domain`: Unlink route custom domain (https://docs.coritan.com/api/reference/client/game-proxy/#op-delete-api-v1-gameproxy-routes-route-id-custom-domain)
- `POST /api/v1/gameproxy/routes/{route_id}/custom-domain/verify`: Check the CNAME now rather than waiting for the next maintenance pass (https://docs.coritan.com/api/reference/client/game-proxy/#op-post-api-v1-gameproxy-routes-route-id-custom-domain-verify)
- `GET /api/v1/client/external-servers/{service_id}/custom-domain`: Get custom domain (https://docs.coritan.com/api/reference/client/external-servers/#op-get-api-v1-client-external-servers-service-id-custom-domain)
- `POST /api/v1/client/external-servers/{service_id}/custom-domain`: Link custom domain (https://docs.coritan.com/api/reference/client/external-servers/#op-post-api-v1-client-external-servers-service-id-custom-domain)
- `PATCH /api/v1/client/external-servers/{service_id}/custom-domain`: Show the vanity hostname, or the hosted one, in the public server list (https://docs.coritan.com/api/reference/client/external-servers/#op-patch-api-v1-client-external-servers-service-id-custom-domain)
- `DELETE /api/v1/client/external-servers/{service_id}/custom-domain`: Unlink custom domain (https://docs.coritan.com/api/reference/client/external-servers/#op-delete-api-v1-client-external-servers-service-id-custom-domain)
- `POST /api/v1/client/external-servers/{service_id}/custom-domain/verify`: Check the CNAME now rather than waiting for the next maintenance pass (https://docs.coritan.com/api/reference/client/external-servers/#op-post-api-v1-client-external-servers-service-id-custom-domain-verify)
