CustomDomain Docs

Domain forwarding

Redirect a connected domain to another URL, or send the apex to www, with a 301 issued at the edge that keeps the path and query and never contacts your origin.

Domain forwarding turns a connected domain into a redirect instead of a proxied origin. When a customer wants oldbrand.example (or a bare apex) to send visitors somewhere else, the edge answers the request with an HTTP 301 Moved Permanently and never contacts an origin.

What it does

There are two forms, both resolved on the edge's hot path right after the ask gate approves the host:

  • Redirect to a target URL. The connection carries a redirect target; every request for the domain 301s to that URL. The incoming request's path and query are preserved, so https://old.example/a/b?x=1 lands on https://new.example.com/a/b?x=1.
  • Apex → www. For a bare apex host (e.g. example.com), the edge 301s to the www. counterpart (https://www.example.com/…), again preserving path + query. A request already on www. is proxied normally, so there is no redirect loop.

Because the 301 is emitted at the edge, the customer's origin never sees the request and no certificate on the origin side is required for the old hostname (only the edge cert, which is issued automatically once the domain is live).

From the console

Open the domain, then its Settings tab (a domain's settings). Forwarding is a field: type an https:// address and Save, or Clear to stop forwarding. www redirect, for a root domain, is a switch with its own Save. After a save the tab says what it did. For the www redirect that is whether we wrote the www record for you or you add it where you manage DNS (the record is shown, with a link to the DNS tab). Members, admins and owners can change them; a viewer reads them.

Enabling it with the API (edge-served)

Forwarding is part of the Power feature set and is driven by the control plane: the redirect target (redirect_to) and the apex→www flag (www_redirect) are attributes of the connection. Change them with PATCH /v1/connections/{id} (API reference), or with POST /v1/connections/{id}/power (and POST /v1/connections/{id}/power:repower to change a connection that is already powered; omitting application_url there keeps the current origin):

curl -X PATCH https://api.customdomain.ai/v1/connections/<ID> \
  -H "Authorization: Bearer $CUSTOMDOMAIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "redirect_to": "https://new.example.com" }'

Send "redirect_to": "" to clear a redirect. The edge picks up the intent within one ask-cache TTL, with no redeploy. When neither is set, the domain is proxied to its origin as usual.

  • The plan. redirect_to needs edge serving, which is included on Growth, Premium and Enterprise: on Starter and Startup, setting or changing it returns 402 with the code plan_upgrade_required (clearing one never does). www_redirect is on every plan, and client_label too. Where the deployment has edge serving off, redirect_to returns 503.
  • Who may call it. An API key of a member, admin or owner, or a token with the app scope for a connection of its own application. A viewer's key, an end user's widget token (its own connection too) and a delegated agent token are refused with 403; the target of a redirect decides where a domain's visitors go, so the platform that owns the domain decides it.
  • The address is checked. redirect_to must be a plain https:// address of a public domain. 400 invalid_request names the reason when it is not https (http:, javascript:, data: and an address with no scheme are all refused), when it holds a user name or password (https://[email protected]), when it is an IP address or a name that cannot be reached from the internet (localhost, .local, .internal, .test), or when it holds spaces, backslashes or hidden characters. The port is read as a number and stored once: :443 is dropped, and :0443 is the same port, so it is the domain itself when it names the domain. A target that names the domain on port 80 is the domain itself too, as the edge reads it. 422 redirect_target_not_allowed is a domain another CustomDomain™ workspace has connected or claimed, whether or not it is serving yet: two workspaces could forward to each other and neither could see the circle. A domain your own workspace serves is yours to forward to, whoever else has a pending or failed claim on it. 422 redirect_loop is an address that would send a visit round in a circle: the domain itself, or a chain through the www redirect and your other forwards that comes back to a host it went through (taking a forward off a domain whose www redirect is on counts: the visit then goes to the www name), or a chain of more than 8 forwards in a row (the title then says "too many forwards"). The answer names only the domains you can see, your workspace's and, for an app token, its own application's; any other reads "another domain". If a check cannot read who owns the address's domain, the answer is 500 and nothing is stored. The same checks apply to redirect_to on /power and /power:repower. An address equal to the one already set is not checked again.
  • Two requests at once. Redirect changes of one workspace are made one at a time, each checked against the domains as they are when its turn comes, so two changes made together cannot make a circle between them: the second answers 422 redirect_loop. A change that waits too long for its turn answers 503 with the code redirects_busy and a Retry-After header, and changes nothing: send it again. A request writes only the settings it changes, and a value it sends back as it read it is not a change, so a setting another request has changed since is not undone. DELETE /v1/connections/{id}/power clears the forward and the root path access through the same checks: on a domain whose www redirect is on, taking the forward off is refused (422 redirect_loop) when www forwards back to the root. A domain that is being removed answers 409 to all of these, also when the removal starts while the change waits.
  • The www redirect and its record. www_redirect is for a root domain only (422 www_redirect_apex_only for a subdomain). Turning it on adds a www record to the records the domain needs. A zone CustomDomain™ hosts is rewritten at once, and a domain whose records were written through a connected provider account is rewritten when move_rewrite is on. For any other domain the response has customer_action_required: true and connection.records_outdated is sent, and the person adds the record. The domain stays live meanwhile. Turning it off also stops the edge answering for www. that domain, unless it is a connection of its own.
  • Test mode. A test mode domain takes client_label only; the two redirects answer 403 sandbox_not_allowed.
  • The audit log. Every change is recorded as connection.updated, with the before and after of the fields that changed, by the member or key that made it (widget:<application id> for an app token). A domain that is being removed answers 409. The redirects are stored before the name: if only client_label cannot be written, the answer is 500, the redirect change is stored and recorded, and sending the same body again writes the name.

The redirect template (provider-written)

When the domain runs on a provider that implements the one-click (Domain Connect) protocol, forwarding can instead be written at the provider. The redirect service template (serviceName: "Forward this domain") declares a single REDIR301 record on the apex (@) whose target is the redirectTarget variable:

{ "type": "REDIR301", "host": "@", "target": "%redirectTarget%", "ttl": 3600 }

REDIR301 is a pseudo-record native to the Domain Connect protocol (like SPFM): the provider's forwarding feature (not our edge) issues the 301 to redirectTarget, preserving path and query. A companion apex-www template covers the apex→www case the same way.

Only the one-click (Domain Connect) rail can apply the redirect template. The OAuth and API-token rails refuse any template carrying a REDIR301/ REDIR302 record with forwarding_not_supported: a redirect is HTTP behavior, not a DNS record, and a provider's DNS API has no way to write one. Before the guard, the literal REDIR301 was handed to the provider's DNS API and the poller then marked the connection live off a write that configured nothing. On those rails, serve the redirect from the edge instead (the section above), or set forwarding manually in the provider's own control panel. (The apex-www template is unaffected: it writes an apex A record plus a www CNAME, both of which those rails apply; the edge issues the apex→www redirect.)

Widget forwarding path

The widget applies forwarding through the ordinary one-click setup rail, using the same POST /v1/connections/{id}/domainconnect:start call it uses for connection records, but naming the redirect template and passing the destination as a variable:

curl -X POST https://api.customdomain.ai/v1/connections/<ID>/domainconnect:start \
  -H "Authorization: Bearer <WIDGET_JWT>" \
  -d '{"service_id":"redirect","vars":{"redirectTarget":"https://new.example.com"}}'
# → 200 { apply_url, service_id: "redirect", dc_provider }

redirectTarget must be an http or https URL, or a bare host, at a public hostname (not an IP address, localhost or an internal name), with no user name or password; anything else answers 400 invalid_request naming it, and the redirect template takes no other variable (see Template variables).

The response's apply_url is opened in the provider popup; once the customer approves, the provider writes the REDIR301 record and the propagation poller promotes the connection to live. Choose this path when the domain stays on its current provider; choose the edge-served redirect_to / www_redirect above when the domain is proxied through the edge.

On this page