# How SSL works (https://dodomain.io/docs/ssl)

DNS points customer domains at you and TLS ends at your host, not ours. Certificate notes for Vercel, Cloudflare for SaaS, Caddy, nginx, Render and Fly.io.

DoDomain is a DNS layer, not a proxy. We get your users' domains pointed at your infrastructure and prove when the records are live; then TLS terminates at your host, with certificates it issues, exactly as if the user had configured DNS by hand.

## The model [#the-model]

A connect session writes (or guides the user to write) records like `CNAME app.customer.com → edge.yourproduct.com`. Once verified, requests to `app.customer.com` resolve straight to your load balancer or hosting platform; DoDomain is not in the request path. No traffic transits our servers, we hold no private keys for your users' domains, and an outage on our side cannot take a connected domain down.

The corollary is honest and worth stating plainly: because we never terminate your traffic, we also don't provision certificates for it. Proxy-style competitors do, by sitting in front of your product forever. With DoDomain, certificate issuance stays where your TLS already lives, and on modern hosts that is automatic. The one thing issuance always waits on (DNS actually pointing at you) is exactly what `connection.verified` tells you.

## What records to mint [#what-records-to-mint]

For subdomains (`app.customer.com`), a `CNAME` to your ingress hostname is the norm. For apex domains (`customer.com`), CNAMEs are not allowed by DNS itself; mint `A`/`AAAA` records to your static IPs, or the flattened/alias target your host documents. See [Apex domains](https://dodomain.io/docs/apex-domains) for what each tier does with an apex request and the patterns that work. Add a `TXT` record when your platform (or your own logic) wants a domain-ownership proof before serving.

## Per-host notes [#per-host-notes]

### Vercel [#vercel]

Add the customer domain to your project (dashboard or the Domains API) and point the DoDomain session's CNAME at the target Vercel gives you (typically `cname.vercel-dns.com`). Vercel provisions and renews the certificate automatically once DNS resolves; `connection.verified` is your signal that issuance can proceed.

### Cloudflare (for SaaS) [#cloudflare-for-saas]

With Cloudflare for SaaS, create a custom hostname for the customer domain and have the session CNAME point at your zone's fallback origin. Cloudflare validates via DNS and issues the edge certificate. This pairs well with DoDomain's one-click tier: a customer already on Cloudflare authorizes the record write in a single OAuth consent. The [Cloudflare for SaaS setup guide](https://dodomain.io/guides/cloudflare-for-saas-custom-hostnames-setup) walks through the fallback origin, the hostname and the certificate validation step by step.

### Caddy / nginx (self-hosted) [#caddy--nginx-self-hosted]

Caddy's `on_demand_tls` issues a certificate the first time a new hostname hits your server; pair its `ask` endpoint with your own record of connected domains (populate it from `connection.verified`). On nginx, run certbot or acme.sh with HTTP-01 per domain after the webhook lands, then reload.

### Render / Fly.io [#render--flyio]

Both issue certificates automatically once the domain is added to the service and DNS points at them: Render under Settings → Custom Domains, Fly via `fly certs add customer.com`. Add the domain on your host when you mint the DoDomain session; issuance completes when the records the session wrote go live.

## The sequence, whatever the host [#the-sequence-whatever-the-host]

Add the domain on your platform, mint the DoDomain session pointing at it, and treat `connection.verified` as the "DNS is live: certificates can issue" trigger. For why issuing earlier backfires, Let's Encrypt's rate limits and the rest of a custom-domain feature, see [how to let customers use their own domain](https://dodomain.io/guides/let-customers-use-their-own-domain).

<Callout title="When a certificate stops renewing">
  The usual cause is the records drifting, which is exactly what the free monitor catches with
  `connection.failed`. See [Webhooks](https://dodomain.io/docs/webhooks).
</Callout>

## When a certificate can't be issued at all [#when-a-certificate-cant-be-issued-at-all]

Two records the customer's zone already holds can block issuance after the DNS verifies: a **CAA** policy that doesn't name your CA, and a stale `_acme-challenge` record left by a previous vendor. Set your CA's issuer domain under **Apps → Branding → Certificate authority** (`letsencrypt.org`, `pki.goog`, `sectigo.com`), and every verify pass judges the CAA it reads against it; a policy that excludes you is flagged on the connect page, on the session, and on the `connection.verified` and `session.completed` webhooks. Without it the policy is reported, not judged. DoDomain issues no certificates either way; the advisory only tells you whether yours can. The codes are in [Webhooks](https://dodomain.io/docs/webhooks).
