Docs / How it works
How it works
How Custom Domain API verifies a customer's hostname, issues its certificate and routes each request to the right workspace, and what each domain status and check means.
The path of a request
- TLS. A visitor opens
https://forms.acme.com. The customer’s CNAME sends them to the edge, which presents a certificate for that exact hostname. Certificates are issued from Let’s Encrypt on demand, only for hostnames that passed verification, and renewed automatically. - Route. The edge finds the application the hostname belongs to. A hostname that belongs to no application gets a 404 and never reaches an origin.
- Assert. The edge removes any
X-Custom-Domain-*header the visitor sent, checks that the domain may be served right now, and adds a fresh signedX-Custom-Domain-Assertionnaming your application, the domain and the workspacereference. - Proxy. The request goes to your origin with
Hostset to the customer’s hostname andX-Forwarded-Proto: https, so absolute URLs, cookies and redirects your app builds are correct.
Your origin verifies the assertion and selects the workspace from its
reference (Verifying requests). It never trusts
the Host header to pick a tenant.
The four checks
A domain goes live only when all four checks pass. They are re-checked regularly, and serving stops the moment one fails.
| Check | Passes when |
|---|---|
ownership |
The TXT record _custom-domain-challenge.<hostname> holds this registration’s token. |
routing |
The hostname’s CNAME chain ends at your edge name (on Free, edge.customdomainapi.com). |
certificate |
The edge serves a valid certificate for the hostname. |
origin |
Your origin is verified, and answers GET /.well-known/custom-domain-workspace through the edge with the workspace it selected from the assertion. |
Each check has a status (pending, passing or failing) and, when
failing, a stable error_code and a message in plain words:
| Check | error_code |
Meaning |
|---|---|---|
| ownership | txt_record_not_found |
No TXT record at that name yet. |
| ownership | txt_token_mismatch |
A TXT record exists, but not with this registration’s value. |
| ownership | txt_token_stale |
The TXT holds the token of an earlier registration. |
| routing | cname_not_found |
No CNAME, or A/AAAA records instead of a CNAME. |
| routing | cname_target_mismatch |
The CNAME points somewhere else. |
| either | dns_timeout |
DNS didn’t answer; retried automatically. |
| origin | origin_not_ready |
No verified origin yet. |
| origin | workspace_probe_failed |
The workspace path didn’t answer 200. |
| origin | workspace_probe_invalid |
It answered 200 without a JSON reference. |
| origin | workspace_mismatch |
The origin selected another workspace: it isn’t using the assertion. |
Failing checks are retried after 1, 2, 5, 15 and 30 minutes, then hourly;
passing ones every 6 hours. Ask for an early re-check with
POST /v1/domains/{id}/checks after your customer fixes a record.
Statuses
| Status | Meaning | Served |
|---|---|---|
pending_dns |
Waiting for the TXT and CNAME records. | no |
provisioning |
Records verified; certificate and origin checks running. | no |
ready |
All four checks pass. | yes |
attention_required |
A check that passed is failing again (often a changed CNAME). | no |
suspended |
Ownership lost for 24 hours, or suspended by policy. | no |
deleting |
Deleted. The hostname can be registered again at once. | no |
ready, attention_required, recovering to ready, and deletion each send
a webhook.