Skip to content

Cloudflare DNS Records — heritageva.app ​

AB#3680 — DNS and domain-validation records to create in Cloudflare at deploy time. See ADR 0026 for the domain strategy decision and ADR 0028 for the per-portal subdomain decision.

TLS model: Cloudflare Universal SSL (free, covers heritageva.app + *.heritageva.app first-level wildcard) terminates TLS for all portal subdomains at the Cloudflare edge. Azure SWA provides its own managed certificate for the apex domain only. Azure-managed certificates are not used for portal subdomains.

Prerequisites ​

  1. Run az deployment group create with customDomain=heritageva.app
  2. Capture the stack outputs: swaDefaultHostname and swaDomainValidationToken
  3. Capture the Container App FQDN from containerAppFqdn output
  4. Confirm Cloudflare Universal SSL is active for heritageva.app in the Cloudflare dashboard (SSL/TLS → Overview → should show "Universal" with green certificate status)

Step 1 — Apex and API records (Azure-managed certs) ​

Web app root domain → Static Web App ​

TypeNameValueProxyTTL
CNAME@<swaDefaultHostname> (e.g. swa-heritageva-prod-eus2.azurestaticapps.net)DNS onlyAuto
TXT@<swaDomainValidationToken> (from stack output)n/aAuto

Note: The CNAME is set to DNS only (grey cloud) during initial SWA cert provisioning so Azure's HTTP challenge can reach the SWA directly. After the Azure-managed cert for heritageva.app is issued (5–10 min), you may optionally switch to Proxied. The Cloudflare Universal SSL cert covers the apex regardless.

API subdomain → Container App (DNS only, no CF proxy) &ZeroWidthSpace;

TypeNameValueProxyTTL
CNAMEapi<containerAppFqdn> (from stack output)DNS onlyAuto
TXTasuid.apiContainer App domain validation tokenn/aAuto

Retrieve the Container App validation token:

bash
az containerapp hostname add \
  --name ca-heritageva-api-prod-eus2 \
  --resource-group rg-heritageva-prod-eus2 \
  --hostname api.heritageva.app \
  --query properties.customDomainVerificationId -o tsv

api stays DNS only (grey cloud). Azure Container Apps managed-cert validation requires the record to be unproxied during the DNS challenge. Leave it grey-cloud permanently.


Step 2 — www redirect &ZeroWidthSpace;

TypeNameValueProxyTTL
CNAMEwww<swaDefaultHostname>ProxiedAuto

Then add a Cloudflare Redirect Rule (Rules → Redirect Rules → Create rule):

  • Name: www → apex
  • When incoming requests match: http.host eq "www.heritageva.app"
  • Then: Redirect to https://heritageva.app${http.request.uri.path} — 301 (Permanent)

Step 3 — Portal subdomain CNAMEs (Cloudflare-proxied, CF Universal SSL) &ZeroWidthSpace;

All portal subdomains are Proxied (orange cloud). Cloudflare Universal SSL covers *.heritageva.app; no per-subdomain cert provisioning is needed.

TypeNameValueProxyPurpose
CNAMEprofile<swaDefaultHostname>ProxiedMember profile portal
CNAMEfamily<swaDefaultHostname>ProxiedFamily portal
CNAMEhomeschool<swaDefaultHostname>ProxiedHomeschool Education Portal
CNAMEmarketplace<swaDefaultHostname>ProxiedCommunity Marketplace
CNAMEpony-express<swaDefaultHostname>ProxiedPony Express delivery network
CNAMElisten<swaDefaultHostname>ProxiedSermons & Music
CNAMEcalendar<swaDefaultHostname>ProxiedCalendar & events
CNAMEgroups<swaDefaultHostname>ProxiedSmall Groups & Ministries
CNAMEmembers<swaDefaultHostname>ProxiedMember directory
CNAMErideshare<swaDefaultHostname>ProxiedRide share & travel
CNAMEcommunities<swaDefaultHostname>ProxiedSister Communities
CNAMEannouncements<swaDefaultHostname>ProxiedAnnouncements

Step 4 — Cloudflare Origin Rule (Host header rewrite) &ZeroWidthSpace;

Azure SWA only serves responses for hostnames it has been explicitly configured with (the apex). The Origin Rule rewrites the Host header so SWA accepts requests for portal subdomains.

Create a Cloudflare Origin Rule (Rules → Origin Rules → Create rule):

  • Name: Portal subdomains → SWA
  • When incoming requests match:
    (http.host matches ".*\.heritageva\.app")
  • Then — Host Header rewrite: <swaDefaultHostname> (e.g. swa-heritageva-prod-eus2.azurestaticapps.net)

This rule fires for all *.heritageva.app requests that reach Cloudflare. The SPA then reads window.location.hostname on the client side to determine which portal to mount (see apps/web/src/lib/portal-router.ts).


Step 5 — Email (already configured — do not change) &ZeroWidthSpace;

TypeNameValueNotes
MX@Cloudflare routing MXAlready live — do not change

Email forward: any @heritageva.appkris@hybridsolutions.cloud


Verification checklist &ZeroWidthSpace;

Apex and API &ZeroWidthSpace;

  • [ ] dig heritageva.app resolves (A or CNAME via CF)
  • [ ] curl -I https://heritageva.app → HTTP 200 or app redirect
  • [ ] TLS cert for heritageva.app shows valid (Azure-managed or Cloudflare Universal)
  • [ ] dig api.heritageva.app resolves to Container App IP (grey cloud, direct)
  • [ ] curl https://api.heritageva.app/healthz{"status":"ok"}
  • [ ] Email forwarding still works after DNS changes

Portal subdomains &ZeroWidthSpace;

  • [ ] curl -I https://calendar.heritageva.app → HTTP 200 (SPA served via CF proxy)
  • [ ] TLS cert for calendar.heritageva.app is Cloudflare Universal SSL (not Azure)
  • [ ] curl -I https://listen.heritageva.app → HTTP 200
  • [ ] curl -I https://homeschool.heritageva.app → HTTP 200
  • [ ] Browser: visit https://profile.heritageva.app — confirm SPA loads and hostname router mounts the Profile portal (not the root shell)

www redirect &ZeroWidthSpace;

  • [ ] curl -IL https://www.heritageva.app → 301 → https://heritageva.app

Heritage Community Hub — Internal. Access restricted via Cloudflare Access + Entra ID.