# Custom domains

On the Pro plan a site can use the user's own domain, such as `www.myapp.com`. The user adds one DNS record at
their domain provider; heremyapp issues and renews the certificate. The heremyapp link keeps working and leads to the
domain. Commands use `$API` (`https://api.heremyapp.com/v1`) and `$AUTH` (the bearer token header) from the [shell setup](https://heremyapp.com/docs.md#quickstart).

## Connect a domain

Ask the user which domain to use. A subdomain such as `www.myapp.com` works with every DNS provider; for a root domain
see [Root domains](#root-domains).

```bash
curl -sS --fail-with-body -X POST "$API/spaces/my-app/domains" -H "$AUTH" \
  -H "content-type: application/json" -d '{"hostname":"www.myapp.com"}'
```

```json
{"hostname":"www.myapp.com","status":"pending","primary":true,"url":"https://www.myapp.com/",
 "records":[{"type":"CNAME","name":"www.myapp.com","value":"domains.heremyapp.app","when":"always"},
            {"type":"TXT","name":"_cf-custom-hostname.www.myapp.com","value":"5c7d…","when":"moving"}],
 "apex":false,"next_step":"Ask the user to add a CNAME record at their DNS provider: …","docs":"https://heremyapp.com/docs/domains.md"}
```

- `402 plan_limit`: custom domains need Pro (up to 10 per account; Enterprise by contract). Tell the user and give
  them `details.upgrade_url`.
- `400 invalid_hostname`: the message says what to fix (full name, no wildcard, ASCII/punycode, not a heremyapp domain).
- `409 domain_taken`: another heremyapp site uses it. If it is the user's, they remove it there first.

## Add the DNS record

Show the user the `records` and where to add them, in their language:

| Type | Name | Value |
|---|---|---|
| CNAME | `www.myapp.com` | `domains.heremyapp.app` |

- Many providers want only the part before the domain in the name field (`www`), not the full name.
- Remove any existing `A`, `AAAA` or `CNAME` record with the same name first; a CNAME cannot sit next to them.
- On Cloudflare DNS, set the record to **DNS only** (grey cloud).
- The `TXT` record (`"when":"moving"`) is only for a domain that already serves another site: add it first, wait until
  the status is `issuing` or `active`, then switch the CNAME. Once the domain is `active` the TXT record can be removed
  (responses then list only the CNAME).
- A `CAA` record that limits certificate authorities can block the certificate; it must allow Let's Encrypt and
  Google Trust Services, or be removed.

You cannot change the user's DNS yourself unless they explicitly give you access to their DNS provider.

## Wait until it is live

```bash
curl -sS --fail-with-body "$API/spaces/my-app/domains" -H "$AUTH"
```

Check every minute; DNS usually takes a few minutes, sometimes up to an hour. `next_step` says what to do, and
`site_url` is the address the site's pages should use. On a local development server the response has
`"simulated": true`: no DNS is checked and the status turns `active` by itself, so do not tell the user it is live.

| `status` | Meaning | Do |
|---|---|---|
| `pending` | Waiting for the DNS record | Keep checking; confirm the user added the CNAME |
| `issuing` | DNS is right, the certificate is being issued | Check again in a few minutes |
| `active` | Live at `url` | Redeploy (next section) and report |
| `misconfigured` | The domain no longer points to heremyapp | Ask the user to restore the CNAME |
| `disabled` | The plan no longer includes custom domains; it redirects to the heremyapp link | Upgrading within 30 days restores it |
| `failed` | No DNS record within 7 days | Remove it, add the CNAME, connect again |

## After it is live

- `site_url` of the space is now `https://www.myapp.com/`. Build the site from the user's project with that address
  and deploy it again (patch bump) so `og:url`, `og:image`, `canonical`, `hreflang`, JSON-LD and `sitemap.xml` use it;
  the deploy `advice` points out the old address. Publishing an older deployment again does not change its addresses.
  If nothing is published yet, deploy the site for the first time the same way.
- The heremyapp link `https://heremyapp.com/ACCOUNT/SPACE` keeps working and redirects to the domain, and the former site origin
  redirects too (except `/.well-known/` files). Share the domain from now on.
- Installer download links do not change.
- Report to the user: the domain URL, that the heremyapp link still works, and that it renews automatically.

## Root domains

Most DNS providers do not allow a CNAME on the root (`myapp.com`). If the provider offers CNAME flattening, ALIAS or
ANAME (Cloudflare DNS does), connect the root like a subdomain (`"apex": true` in the response); as a second domain it
redirects to the primary one. Otherwise connect `www.myapp.com` and set the provider's URL forwarding to send
`myapp.com` to it. Domains under `.app`, `.dev` and other HTTPS-only endings open only over HTTPS, so the forwarding
must support HTTPS, or the root must be connected as a domain here.

A space can have up to three domains: the first is the primary, and the others (for example the root, or an earlier
domain) redirect to it.

## Remove a domain

```bash
curl -sS --fail-with-body -X DELETE "$API/spaces/my-app/domains/www.myapp.com" -H "$AUTH"
```

The site goes back to its heremyapp address. Tell the user they can delete the CNAME record. Removing the last
domain, deleting the space, or deleting the account also removes the domain from heremyapp's certificate setup.

## Troubleshooting

| Problem | Do |
|---|---|
| `pending` for more than an hour | Look up the record with `dig +short CNAME www.myapp.com`; it must print `domains.heremyapp.app.` |
| `error` mentions CAA | Allow Let's Encrypt and Google Trust Services in the CAA record, or remove it |
| `409 domain_taken` | Remove the domain from the other site or account first; otherwise contact support |
| `503 domains_unavailable` | Try again after `details.retry_after_seconds` |
