---
name: custom-domains
description: Serve your Anchorify shares from your own domain (e.g. docs.acme.com).
---

# Custom domains

> **Experimental, pro-only.** Ask Sankalp (`sankalpagarwal294@gmail.com`) to flip your org to the `pro` plan if you want in.

A custom domain lets your share URLs live at *your* hostname instead of `anchorify.io`. After setup, every public share owned by your org also resolves at:

```
https://docs.acme.com/<project>/<slug>
```

…in addition to the canonical `https://anchorify.io/<org>/<project>/<slug>`. Both URLs serve the same page; the canonical one keeps working.

## Limits

| What | Limit |
|---|---|
| Domains per org | 1 |
| Plan required | `pro` |
| Hostname format | lowercase, ≥1 dot, ≤253 chars (e.g. `docs.acme.com`) |
| Subdomains | recommended (e.g. `docs.`, `share.`) |
| Apex domains (`acme.com`) | **possible but not supported by every DNS provider** — see below |
| `www.` handling | DIY — see below |

## Setup flow

1. **Get on the pro plan.** Custom domains are gated behind both your org being on `pro` and the server-side `CUSTOM_DOMAINS_ENABLED=1` flag. If either is off, the dashboard shows a "this is a pro feature" stub instead of the form.
2. **Add the domain.** Visit `/dashboard/settings/domain` and enter your hostname (`docs.acme.com`). The page returns a TXT record:

   ```
   Name:  _jf-verify.docs.acme.com
   Value: <random token>
   ```

3. **Add that TXT record at your DNS provider.** Different providers handle this differently — Cloudflare DNS, Namecheap, GoDaddy, Route 53, etc. all have their own UI. Wait a few minutes for propagation.
4. **Click "Verify now".** We resolve the TXT record server-side. On a match, the row flips from `pending` to `provisioning` and we ask Cloudflare to issue an SSL cert for your hostname.
5. **Wait 1–5 minutes for the cert.** The dashboard badge stays `Provisioning` until Cloudflare reports `ssl.status = active`. After that, it flips to `Active` and your domain is live.

If the cert provisioning fails (most commonly a CAA record blocking Let's Encrypt), the badge flips to `Failed` and we show Cloudflare's error verbatim. Fix the underlying DNS issue and click **Retry**.

## Apex vs subdomain

**We strongly recommend a subdomain** (`docs.acme.com`, `share.acme.com`, etc.) over the apex (`acme.com`).

Subdomains use a CNAME pointing at our origin pool, which every DNS provider supports.

Apex domains can't have CNAMEs at the zone root per the DNS spec — you'd need ALIAS / ANAME / CNAME-flattening, which **not every DNS provider supports**:

| Provider | Apex support |
|---|---|
| Cloudflare DNS | yes (CNAME flattening) |
| Route 53 | yes (ALIAS records) |
| Vercel DNS | yes |
| GoDaddy | no |
| Namecheap | no |

If your DNS host doesn't support apex flattening, point a subdomain at us and 301 the apex to it at your DNS host's level. We don't help configure the apex-side DNS.

## `www.` handling

Anchorify only allows **one custom hostname per org**. If you want both `acme.com` and `www.acme.com` to serve, you have two options:

1. **Pick one** — say, `acme.com` — and set up a 301 from `www.acme.com` to it at your DNS host's level (most providers offer "redirect to URL" rules).
2. **Add the other one** as a separate hostname on a different org if you really want both serving Anchorify content.

Most users land on a single subdomain (`docs.acme.com`) and never have to think about this.

## Members-only shares on custom domains

Anchorify session cookies live on `anchorify.io`, not on your custom hostname. Browsers don't let one host see another host's cookies — that's a security feature of the web, not a Anchorify limitation.

So when a visitor hits a `members`-visibility share on your custom domain, **we 302 them to the canonical `anchorify.io` URL** so we can read their session cookie and render the share.

URL bar limitation: post-redirect, the browser shows `anchorify.io/<org>/<project>/<slug>` — not your custom hostname. Public + unlisted shares aren't affected; they render in place on the custom domain.

If your org is mostly publishing internal/members-only content, the custom domain is probably less useful for you than for an org publishing public deliverables.

## Removal

Click **Remove domain** on the dashboard. We delete the row + free the hostname slot immediately, so:

- Your shares stop serving from your custom hostname (they continue to serve from `anchorify.io`).
- Another pro org can claim the same hostname (with their own fresh TXT verification + cert flow).

> **Cleanup note (validation cohort):** the Cloudflare-side custom_hostname row + the issued cert aren't auto-deleted on removal — Sankalp cleans those up by hand. The hostname is fully released on the Anchorify side. This is a known follow-up; see `services/domains.ts` for context.

## Routing details

Once your domain is `Active`, requests to `https://docs.acme.com/<path>` are internally rewritten to `/acme/<path>` and served by the same code that powers the canonical URLs. Some routes are explicitly **not** served on custom domains and 404 instead:

- `/dashboard`, `/onboarding`, `/auth/*`, `/api/*`, `/admin`, `/_admin`
- `/_dashboard`, `/_list`, `/_export`, `/_import`, `/_publish`
- `/docs/*`, `/privacy`, `/terms`

Those are Anchorify surfaces, not your org's surfaces — they always live on `anchorify.io`.

## Why CF gets all the cert work

We use Cloudflare for SaaS to issue + serve the certs. That avoids us managing Let's Encrypt directly per hostname (renewals, ACME challenges, key rotation) and gets us a CDN edge cache for free. The trade-off is that you need a clean DNS path that Cloudflare can validate, and apex support depends on your DNS host (above).
