---
title: "Custom domains"
description: "Serve your directory's portal and sign-in on a hostname you own, such as login.acme.com, with a certificate EZGH SSO issues and renews."
---

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

# Custom domains

A custom domain is a hostname you own, such as `login.acme.com`, for your directory's portal and
sign-in. Once it's active, people who open your portal at `https://<alias>.sso.ezgh.app` are sent
there, and emails link to it.

Once you've created two DNS records, EZGH SSO issues a certificate for the hostname (Let's Encrypt,
renewed automatically) and serves the directory there. An active custom domain is its own issuer,
and the OpenID Connect and SAML endpoints at `https://<alias>.sso.ezgh.app` keep working alongside
it.

You need `sso.customDomains.create` (in `SsoFullAccess`). A directory has one custom domain: to
use another hostname, remove the current one first (adding a second is `409
too_many_custom_domains`).

## Add a custom domain

### Console

Open **EZGH SSO** > **Custom domains**, select **Add custom domain**, enter the **Hostname**, and
select **Add domain**. The domain's page lists the DNS records to create.
### CLI

```sh
ezgh sso custom-domains create login.acme.com
ezgh sso custom-domains get login.acme.com
```
### API

Call [CreateCustomDomain](/sso-api/CreateCustomDomain/) with `{ "hostname": "login.acme.com" }`.

A hostname is lowercase ASCII (an internationalized name in its `xn--` form), and EZGH's own
domains are refused. The domain's `records` are the two records to create:

| Purpose | Type | Name | Value |
| --- | --- | --- | --- |
| `ownership` | `TXT` | `_ezgh-sso-verification.login.acme.com` | `ezgh-sso-verification=<token>` |
| `routing` | `CNAME` | `login.acme.com` | `<alias>.sso.ezgh.app` |

The CNAME always names your directory's current alias. An apex domain (`acme.com`) can't have a
CNAME, so it can't be a custom domain: use a subdomain. If your domain has CAA records, they must
allow `letsencrypt.org`, or the certificate can't be issued.

## Status

| `status` | Meaning |
| --- | --- |
| `pending` | Waiting for both records. |
| `issuing` | Both records were found. The certificate is being issued and the hostname attached; this usually takes a few minutes. |
| `active` | Served: the portal, sign-in and the OpenID Connect and SAML endpoints answer on the hostname. |

EZGH SSO checks the records as soon as you add the domain, then every 15 minutes for a week.
[VerifyCustomDomain](/sso-api/VerifyCustomDomain/) (`ezgh sso custom-domains verify
login.acme.com`) asks for a check within seconds, and restarts the week. Each record shows whether
the last check `found` it; `checkError` says why the last check didn't verify the domain, and
`verifiedAt` when both records were found.

- A verified domain is checked again every 6 hours. If either record is missing twice in a row, it
  goes back to `pending`. A DNS failure or timeout changes nothing.
- The CNAME may point through a chain to `<alias>.sso.ezgh.app`. After an alias change, the old
  alias is accepted during its 30-day redirect.
- A hostname is claimed by one directory at a time. Another directory can add it, but it stays
  `pending`, with `checkError` saying it's claimed elsewhere, until the first directory removes it
  or loses its records.
- Status changes are recorded in [Trails](/sso/trails) as `UpdateCustomDomainStatus`.

The domain's `certificate` shows `notAfter` and, if issuance fails (for example a CAA record that
doesn't allow Let's Encrypt), `error`. Fix the cause and EZGH SSO tries again.

## Using an active custom domain

- **Its own issuer.** An active domain's `issuer` is `https://<hostname>`, with its own
  `/.well-known/openid-configuration`. Point an OpenID Connect application at the issuer it should
  use. Codes and tokens only work on the host that issued them, so an application uses one issuer.
- **SAML.** On a custom domain, a SAML application's identity provider entity ID and metadata are
  `https://<hostname>/saml/metadata/<applicationId>`. Use the metadata from the host the service
  provider should trust.
- **Your portal moves there.** Opening the portal, sign-in or account pages, or an activation or
  reset link, at `https://<alias>.sso.ezgh.app` redirects to the same page on your custom domain.
  The OpenID Connect and SAML endpoints there aren't redirected, so applications set up with that
  issuer or entity ID keep working, and a sign-in an application starts there finishes there.
  Removing the custom domain ends the redirect at once.
- **Emails.** Activation and factor-reset links use your custom domain.
- **Passkeys belong to a host.** A passkey registered at `<alias>.sso.ezgh.app` doesn't work at
  your custom domain, and the other way round. Someone who enrolled a passkey before you added the
  custom domain signs in there with another sign-in method, or adds a passkey after an
  administrator resets their sign-in methods.

## Remove a custom domain

[DeleteCustomDomain](/sso-api/DeleteCustomDomain/) (`ezgh sso custom-domains delete
login.acme.com`) removes it at once. Deleting the directory removes its custom domains too.

Source: https://docs.ezghcloud.com/sso/custom-domains/index.mdx
