workos-custom-domains
WorkOS Custom Domains
Step 1: Fetch Documentation (BLOCKING)
STOP. Do not proceed until complete.
WebFetch these URLs in order:
- https://workos.com/docs/custom-domains/index
- https://workos.com/docs/custom-domains/email
- https://workos.com/docs/custom-domains/authkit
- https://workos.com/docs/custom-domains/auth-api
- https://workos.com/docs/custom-domains/admin-portal
These docs are the source of truth. If this skill conflicts with the docs, follow the docs.
Step 2: Environment Validation (Decision Tree)
What environment?
|
+-- Staging --> Custom domains NOT available
| (uses workos.dev for email, authkit.app for AuthKit)
| SKIP to Step 8 (verification only)
|
+-- Production --> Custom domains available (paid feature)
CONTINUE to Step 3
Critical: Custom domains are a production-only, paid feature. Check your WorkOS plan on pricing page before proceeding.
Step 3: Determine Domain Scope (Decision Tree)
Which WorkOS services need custom domains?
|
+-- Email (Magic Auth, verification, password reset, invites)
| --> Configure Email Domain (Step 4)
|
+-- AuthKit (hosted auth UI)
| --> Configure AuthKit Domain (Step 5)
|
+-- Admin Portal (self-service SSO/Directory Sync UI)
| --> Configure Admin Portal Domain (Step 6)
|
+-- Multiple services
--> Configure each domain type separately
Each service type requires separate DNS configuration. You cannot use the same domain for multiple services — they use different CNAME patterns.
Step 4: Email Domain Configuration
(4A) Add Email Domain in Dashboard
- Log in to WorkOS Dashboard
- Select Production environment (top-right environment switcher)
- Navigate to Domains section (left sidebar)
- Click Add Domain button
- Enter your domain (e.g.,
example.com) - Click Add
Checkpoint: Dashboard displays 3 CNAME records to create.
(4B) Create DNS Records
You will see 3 CNAME records like:
Name: em1234._domainkey.example.com
Value: em1234.dkim.workosmail.com
Name: workos._domainkey.example.com
Value: workos.dkim.workosmail.com
Name: bounce.example.com
Value: bounce.workosmail.com
Action: Create these exact records in your DNS provider (Route53, Cloudflare, Namecheap, etc.).
Critical DNS Rules:
- Use CNAME record type, not A or TXT
- Copy values exactly — trailing periods matter on some providers
- Do NOT add
http://orhttps://prefixes - Records must be public (not internal DNS)
(4C) Verify Domain
- After creating DNS records, click Verify now in dashboard
- If verification fails: WorkOS retries automatically for 72 hours
- DNS propagation can take 5 minutes to 48 hours (varies by provider)
Success indicator: Domain status shows "Verified" in dashboard.
Post-verification behavior:
- Emails send from
no-reply@yourdomain.com - Magic Auth links use your domain
- Password reset emails use your domain
Critical: Keep CNAME records in place permanently. Deleting them breaks email delivery.
Step 5: AuthKit Domain Configuration
(5A) Add AuthKit Domain in Dashboard
- In WorkOS Dashboard, navigate to Domains section
- Click Configure AuthKit domain button
- Enter subdomain (e.g.,
auth.example.com) - Click Add
Checkpoint: Dashboard displays 1 CNAME record to create.
(5B) Create DNS Record
You will see a CNAME like:
Name: auth.example.com
Value: <random-phrase>.authkit.app
Action: Create this CNAME in your DNS provider.
Cloudflare Users (CRITICAL):
- Set CNAME to DNS-only mode (gray cloud icon)
- Do NOT enable proxy (orange cloud icon)
- WorkOS uses Cloudflare — proxied CNAMEs across accounts fail
Other DNS Providers:
- Create standard CNAME record
- No special configuration needed
(5C) Verify Domain
Click Verify now in dashboard. DNS propagation applies (see 4C).
Success indicator: AuthKit hosted UI now loads at https://auth.example.com
Post-verification behavior:
- Sign-in/sign-up UI appears on your domain
- OAuth callbacks reference your domain
- Session cookies use your domain
Step 6: Admin Portal Domain Configuration
BLOCKING: WebFetch https://workos.com/docs/custom-domains/admin-portal for exact steps.
Configuration process mirrors Step 5 (AuthKit), but:
- Uses different CNAME target
- Applies to Admin Portal UI only (not AuthKit)
- Requires separate domain or subdomain (e.g.,
admin.example.com)
Do NOT reuse the AuthKit domain — each service needs its own.
Step 7: Application Integration (Decision Tree)
SDK changes needed?
|
+-- Email domain --> NO code changes required
| (WorkOS handles email routing automatically)
|
+-- AuthKit domain --> UPDATE auth flow URLs
| (redirect URIs, sign-in URLs, etc.)
|
+-- Admin Portal domain --> UPDATE Admin Portal links
(if embedding portal in app)
For AuthKit Custom Domain
Required environment variable changes:
# OLD (default authkit.app domain)
WORKOS_REDIRECT_URI=https://youthful-ginger-43.authkit.app/callback
# NEW (your custom domain)
WORKOS_REDIRECT_URI=https://auth.yourdomain.com/callback
Update locations:
.env.localor production env vars- WorkOS Dashboard → AuthKit → Redirect URIs configuration
- Any hardcoded auth URLs in app code
Verification command:
# Check redirect URI uses custom domain
grep -r "authkit.app" .env* && echo "FAIL: Still using default domain"
For Admin Portal Custom Domain
If you generate Admin Portal links programmatically, update the base URL:
// OLD
const portalLink = workos.portal.generateLink({...});
// NEW - check docs for exact API
const portalLink = workos.portal.generateLink({
customDomain: 'admin.yourdomain.com',
...
});
Check fetched docs for exact SDK method — API may vary by SDK version.
Step 8: Verification Checklist (ALL MUST PASS)
Run these checks in order. Do not mark complete until all pass:
DNS Verification
# Check email domain CNAME (replace example.com)
dig +short em1234._domainkey.example.com CNAME
# Expected: em1234.dkim.workosmail.com
# Check AuthKit domain CNAME
dig +short auth.example.com CNAME
# Expected: <phrase>.authkit.app
# Check DNS propagation (if using Cloudflare)
dig @1.1.1.1 +short auth.example.com CNAME
Dashboard Verification
- Open WorkOS Dashboard → Domains
- All configured domains show "Verified" status
- No pending verification warnings
Email Domain Test
If using Magic Auth or email verification:
# Trigger test email via your app
# Then check email headers for:
From: no-reply@yourdomain.com
Manual test:
- Trigger password reset or magic link
- Check email "From" address matches your domain
- Click link → should work without CORS errors
AuthKit Domain Test
# Test custom domain loads AuthKit UI
curl -I https://auth.yourdomain.com
# Expected: HTTP 200, HTML content
# Check SSL certificate
openssl s_client -connect auth.yourdomain.com:443 -servername auth.yourdomain.com | grep "Verify return code"
# Expected: "Verify return code: 0 (ok)"
Browser test:
- Navigate to
https://auth.yourdomain.com - Should see WorkOS AuthKit UI, not DNS error
- Check browser address bar for valid SSL (padlock icon)
Application Integration Test
# Build succeeds with new env vars
npm run build || yarn build
# Auth flow works end-to-end
# 1. Click sign-in link
# 2. Redirects to auth.yourdomain.com
# 3. Complete auth
# 4. Callback returns to app successfully
Error Recovery
"Domain verification failed after 72 hours"
Root causes:
- CNAME record typo in DNS provider
- DNS provider hasn't propagated changes
- CNAME record deleted before verification
Fix:
# Check actual DNS record
dig +short <your-cname-name> CNAME
# Compare to expected value in dashboard
# If mismatch: update DNS record, click "Verify now" again
If using Cloudflare: Ensure CNAME is DNS-only (see Step 5B).
"AuthKit domain shows SSL error in browser"
Root cause: DNS record not pointing to correct WorkOS target, or SSL provisioning incomplete.
Fix:
- Verify CNAME points to exact value from dashboard (check for trailing periods)
- Wait 15 minutes for SSL provisioning (WorkOS auto-provisions via Let's Encrypt)
- If error persists after 1 hour, contact WorkOS support
"Emails still sending from workos.dev in production"
Root causes:
- Environment switcher in dashboard set to Staging
- Email domain not verified yet
- DNS records deleted after initial verification
Fix:
# Check dashboard environment (top-right)
# Must be "Production"
# Check domain status in dashboard
# Must show "Verified", not "Pending"
# Re-verify DNS records exist
dig +short em1234._domainkey.yourdomain.com CNAME
"Cloudflare CNAME verification fails"
Root cause: CNAME proxied (orange cloud) instead of DNS-only (gray cloud).
Fix:
- Log in to Cloudflare DNS settings
- Find your AuthKit/Admin Portal CNAME
- Click orange cloud icon → turns gray (DNS-only)
- Wait 5 minutes, retry verification
Why this matters: WorkOS and your Cloudflare account cannot both proxy the same domain.
"Callback fails with 'redirect_uri_mismatch' error"
Root cause: WORKOS_REDIRECT_URI still uses old authkit.app domain.
Fix:
# Update env vars to use custom domain
WORKOS_REDIRECT_URI=https://auth.yourdomain.com/callback
# Update in WorkOS Dashboard too:
# Dashboard → AuthKit → Redirect URIs → Add new URI → Save
# Restart dev server to load new env vars
"Admin Portal links return 404"
Root cause: Admin Portal custom domain configured, but SDK still generates default domain links.
Fix: Check Step 7 application integration. Update Admin Portal link generation to use customDomain parameter (exact API in fetched docs).
Related Skills
- workos-authkit-nextjs — Integrate AuthKit with custom domain in Next.js apps
- workos-authkit-react — React-specific AuthKit integration with custom domains
- workos-email — Email configuration and Magic Auth setup
- workos-admin-portal — Self-service SSO/Directory Sync portal configuration
- workos-domain-verification — Domain ownership verification for SSO connections
More from workos/skills
workos-widgets
Use when the user is implementing, embedding, or debugging a WorkOS Widget — specifically the User Management, User Profile, Admin Portal SSO Connection, or Admin Portal Domain Verification widgets. Handles the full stack — detecting the frontend (Next.js, React, React Router, TanStack Start, Vite, SvelteKit), generating access tokens via the backend SDK in use (Node, Python, Go, Ruby, PHP, Java, .NET), and wiring up the widget component correctly per the bundled OpenAPI spec. Also use when code imports from @workos-inc/widgets or the user pastes <UserManagement /> or <UserProfile /> JSX.
270workos-authkit-nextjs
Integrate WorkOS AuthKit with Next.js App Router (13+). Server-side rendering required.
64workos-authkit-base
Architectural reference for WorkOS AuthKit integrations. Fetch README first for implementation details.
42workos-authkit-react
Integrate WorkOS AuthKit with React single-page applications. Client-side only authentication. Use when the project is a React SPA without Next.js or React Router.
33workos-authkit-tanstack-start
Integrate WorkOS AuthKit with TanStack Start applications. Full-stack TypeScript with server functions. Use when project uses TanStack Start, @tanstack/start, or vinxi.
28workos-authkit-vanilla-js
Integrate WorkOS AuthKit with vanilla JavaScript applications. No framework required, browser-only. Use when project is plain HTML/JS, doesn't use React/Vue/etc, or mentions vanilla JavaScript authentication.
28