| 1 | --- |
| 2 | title: Custom domains |
| 3 | description: Register a Function hostname, configure DNS and CAA, verify HTTPS, and retire the domain safely. |
| 4 | --- |
| 5 | |
| 6 | A Neon custom domain maps one hostname to one Function on one branch. Alchemy |
| 7 | registers that mapping and returns the DNS target; your DNS provider and Neon |
| 8 | certificate provisioning complete the setup. Start with [Setup](/neon/setup) and |
| 9 | a Function whose native URL already works. |
| 10 | |
| 11 | ## Prepare a hostname |
| 12 | |
| 13 | Use a domain you control and arrange permission to edit its DNS. Prefer a |
| 14 | subdomain such as `api.example.com`: a standard CNAME cannot be placed at a zone |
| 15 | apex without provider-specific flattening. Reserve a different hostname for each |
| 16 | preview; custom domains are not inherited by child branches. |
| 17 | |
| 18 | Confirm whether another application already uses the hostname before changing |
| 19 | its records. A hostname can belong to only one Function target at a time. |
| 20 | |
| 21 | ## Register the Function domain |
| 22 | |
| 23 | Inside your stack, using an existing `api` Function: |
| 24 | |
| 25 | ```typescript |
| 26 | import * as Neon from "alchemy/Neon"; |
| 27 | |
| 28 | const domain = yield* Neon.CustomDomain("ApiDomain", { |
| 29 | function: api, |
| 30 | hostname: "api.example.com", |
| 31 | }); |
| 32 | ``` |
| 33 | |
| 34 | Return `domain.cnameTarget` in the stack outputs so the DNS operator can copy the |
| 35 | exact target after deployment. Registration intentionally does not wait for DNS, |
| 36 | which lets you create the DNS record afterward. |
| 37 | |
| 38 | ## Use the Website alternative |
| 39 | |
| 40 | For a Website, use its `domain` prop instead of declaring a second registration |
| 41 | for the same hostname: |
| 42 | |
| 43 | ```typescript |
| 44 | const site = yield* Neon.Website.Vite("Web", { |
| 45 | branch, |
| 46 | rootDir: "./web", |
| 47 | domain: "app.example.com", |
| 48 | }); |
| 49 | ``` |
| 50 | |
| 51 | The live Website exposes the registration as `site.domain` and its target as |
| 52 | `site.domain.cnameTarget`. During native local development there is no domain |
| 53 | registration; use the local dev URL. A returned custom URL is not proof that DNS |
| 54 | or HTTPS is ready. |
| 55 | |
| 56 | ## Publish a DNS-only CNAME |
| 57 | |
| 58 | At your DNS provider, configure: |
| 59 | |
| 60 | | Field | Value | |
| 61 | | --- | --- | |
| 62 | | Type | `CNAME` | |
| 63 | | Name | `api` or `api.example.com`, according to the provider's UI | |
| 64 | | Target | The exact `cnameTarget` output, without `https://` or a path | |
| 65 | | Proxy | Disabled / DNS-only; on Cloudflare this is the grey cloud | |
| 66 | | TTL | Your DNS provider's normal value | |
| 67 | |
| 68 | After reviewing their ownership, remove conflicting A, AAAA, or CNAME records for |
| 69 | that hostname. A proxy can prevent Neon validation even when the browser appears |
| 70 | to reach an edge server. Wait for the applicable DNS TTL before judging propagation. |
| 71 | |
| 72 | ## Check certificate-authority restrictions |
| 73 | |
| 74 | If your domain or an applicable parent has CAA records, the policy must allow |
| 75 | Let's Encrypt, the certificate authority Neon uses. Have the DNS/security owner |
| 76 | approve the change, then authorize it as described in |
| 77 | [Neon's domain guide](https://neon.com/docs/compute/functions/custom-domains#configure-dns): |
| 78 | |
| 79 | ```text |
| 80 | CAA 0 issue "letsencrypt.org" |
| 81 | ``` |
| 82 | |
| 83 | A domain without a restrictive CAA policy does not need this extra record merely |
| 84 | to satisfy Alchemy. |
| 85 | |
| 86 | ## Check registration status |
| 87 | |
| 88 | In the Neon Console, select the project and branch, then open **Settings → |
| 89 | Functions → Custom Domains**. Check the target Function, DNS status, and any |
| 90 | reported reason. The resource also exposes `status`, `dnsStatus`, `bindingStatus`, |
| 91 | and `statusReason` when returned by Neon. |
| 92 | |
| 93 | | Reported problem | Action | |
| 94 | | --- | --- | |
| 95 | | `cname-not-pointing-at-edge` | Correct the target, disable DNS proxying, remove conflicts, and allow propagation | |
| 96 | | `caa-blocks-lets-encrypt` | Review CAA on the hostname and its parent domains; authorize Let's Encrypt | |
| 97 | | `binding-missing` | Verify the intended Function still exists; contact Neon Support if internal routing remains missing | |
| 98 | | Hostname already registered elsewhere | Resolve ownership of the existing mapping; do not bypass it with blanket adoption | |
| 99 | |
| 100 | ## Verify HTTPS and application behavior |
| 101 | |
| 102 | Resolve the public CNAME: |
| 103 | |
| 104 | ```sh |
| 105 | dig +short CNAME api.example.com |
| 106 | ``` |
| 107 | |
| 108 | Then request a known route that your Function implements: |
| 109 | |
| 110 | ```sh |
| 111 | curl --fail-with-body --max-time 30 https://api.example.com/health |
| 112 | ``` |
| 113 | |
| 114 | Check the expected response, not only a successful status. Keep TLS verification |
| 115 | enabled; `curl -k` would hide a certificate failure. Neon may issue the certificate |
| 116 | on the first request. If the registration is active but HTTPS initially fails, |
| 117 | allow a short issuance delay and retry once; persistent failures need diagnostics, |
| 118 | not an unbounded retry loop. |
| 119 | |
| 120 | Open the application through the custom hostname and exercise sign-in, navigation, |
| 121 | redirects, and any streaming/WebSocket features it uses. Registration status |
| 122 | `active` establishes DNS/CAA/routing, not completed application acceptance. |
| 123 | |
| 124 | ## Secure both URLs |
| 125 | |
| 126 | The native Neon Function URL remains public when a custom domain is added. |
| 127 | Authenticate and authorize protected work on both entry points. Browser clients |
| 128 | may also require application CORS configuration. For Auth redirects, register the |
| 129 | custom application's origin separately using |
| 130 | [Production Auth](/neon/guides/production-auth). |
| 131 | |
| 132 | ## Retire the hostname |
| 133 | |
| 134 | Remove the DNS record first, then wait for public DNS to stop returning the Neon |
| 135 | target. Only then remove the CustomDomain declaration and deploy, or destroy its |
| 136 | owning stack. This avoids leaving a dangling CNAME after releasing the hostname. |
| 137 | If DNS is managed in a separate stack or by another team, coordinate the ordering |
| 138 | rather than assuming Function destruction also removes DNS. |
| 139 | |
| 140 | Confirm the custom URL no longer serves the retired application before reusing |
| 141 | the hostname. Keep the original stack/stage state until cleanup finishes; see |
| 142 | [State and recovery](/neon/guides/state-recovery). |
| 143 | |
| 144 | ## Related guides |
| 145 | |
| 146 | - [CustomDomain reference](/providers/neon/customdomain) — inputs, outputs, and lifecycle. |
| 147 | - [Neon custom domains](https://neon.com/docs/compute/functions/custom-domains) — current DNS, CAA, and status behavior. |
| 148 | - [Setup](/neon/setup) — deployment prerequisites. |