> ## Documentation Index
> Fetch the complete documentation index at: https://docs.heymcp.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Custom domains

> Serve your MCP tunnel on a hostname you own.

<Info>Pro. One custom domain is included.</Info>

Instead of `https://amber-jetty.t.heymcp.dev/mcp`, serve on:

```
https://mcp.example.com/mcp
```

Useful when the URL is going into a client someone else configures, or into
documentation, or anywhere the tunnel is meant to look like part of your own
product.

## Add a domain

<Steps>
  <Step title="Add it in the dashboard">
    **Domains** in [app.heymcp.dev](https://app.heymcp.dev), then add your
    hostname — `mcp.example.com`.

    You get back two DNS records to create.
  </Step>

  <Step title="Create the CNAME">
    Points traffic at HeyMCP's edge.

    | Type    | Name  | Value             |
    | ------- | ----- | ----------------- |
    | `CNAME` | `mcp` | `edge.heymcp.dev` |
  </Step>

  <Step title="Create the TXT record">
    Proves you own the hostname.

    | Type  | Name          | Value                            |
    | ----- | ------------- | -------------------------------- |
    | `TXT` | `_heymcp.mcp` | the token shown in the dashboard |

    The full name is `_heymcp.mcp.example.com`. Most DNS providers want just the
    subdomain part relative to your zone, which is `_heymcp.mcp`.
  </Step>

  <Step title="Wait for verification">
    HeyMCP checks both records and activates the domain once they're present.
    DNS propagation is the slow part — usually minutes, occasionally longer.
  </Step>

  <Step title="Use it">
    ```bash theme={null}
    heymcp share 8000 --domain mcp.example.com
    ```
  </Step>
</Steps>

## Why both records

The CNAME alone would be enough to route traffic. The TXT record is what stops
**subdomain takeover**: without it, anyone who noticed a dangling CNAME pointing
at `edge.heymcp.dev` could claim that hostname in their own HeyMCP account and
start serving from it.

Ownership before traffic. A domain with a CNAME and no TXT never routes.

## Certificates

Handled for you. The certificate is obtained from Let's Encrypt on the first
connection to your domain and renewed automatically. There is nothing to
upload and nothing to renew.

### CAA records

If your domain has a CAA record restricting which certificate authorities may
issue for it, Let's Encrypt must be allowed. HeyMCP checks this during
verification and tells you which hostname carries the blocking record rather
than leaving you with a silent certificate failure:

> A CAA record on `example.com` only allows certain certificate authorities, and
> Let's Encrypt is not one of them. Add `0 issue "letsencrypt.org"` and we will
> try again.

```
example.com.  IN  CAA  0 issue "letsencrypt.org"
```

## Removing a domain

Delete it from the **Domains** page. Remove the DNS records too — a CNAME left
pointing at `edge.heymcp.dev` for a domain nobody owns in HeyMCP just returns
404, but tidying up is the right habit.

## Troubleshooting

<AccordionGroup>
  <Accordion title="Stuck waiting for DNS">
    Check both records resolve:

    ```bash theme={null}
    dig +short CNAME mcp.example.com
    dig +short TXT _heymcp.mcp.example.com
    ```

    The CNAME should return `edge.heymcp.dev.` and the TXT should return your
    token. If your provider appended your zone to a name that was already
    fully-qualified, you'll see `_heymcp.mcp.example.com.example.com` — a very
    common mistake.
  </Accordion>

  <Accordion title="CAA blocked">
    Add `0 issue "letsencrypt.org"` to the hostname named in the error. The
    check walks up the domain, so the record may be on a parent, not on the
    subdomain itself.
  </Accordion>

  <Accordion title="--domain refused">
    The domain must be verified and active first, and custom domains are a Pro
    feature. Check the Domains page and `heymcp whoami`.
  </Accordion>

  <Accordion title="404 on the custom domain">
    Nothing is bound to it. Either verification hasn't completed, or you're
    running `heymcp share` without `--domain` — in which case your tunnel is on
    your slug, not your domain.
  </Accordion>
</AccordionGroup>
