DNS & Routing Setup
This page documents the DNS and Worker routing configuration required for Aero2's subdomain-based multi-tenancy.
DNS Configuration
Two DNS records are required in the Cloudflare zone:
Type Name Content Proxy
A @ 192.0.2.1 Proxied (bare domain, placeholder IP)
CNAME * aero2.dev Proxied (wildcard catches all subdomains)- The
Arecord for@(bare domain) uses a placeholder IP address. The actual traffic is handled by the Worker, not the origin server. Cloudflare's proxy intercepts the request before it reaches the IP. - The
CNAME *(wildcard) record catches all subdomains (dashboard.aero2.dev,swift-maple.aero2.dev, etc.) and routes them through Cloudflare's proxy to the Worker.
Worker Route Configuration
In wrangler.json, Worker routes are configured per environment to match both the bare domain and all subdomains:
{
"env": {
"production": {
"routes": [
{ "pattern": "aero2.dev/*", "zone_name": "aero2.dev" },
{ "pattern": "*.aero2.dev/*", "zone_name": "aero2.dev" }
],
"vars": {
"PLATFORM_DOMAIN": "aero2.dev"
}
},
"qa": {
"routes": [
{ "pattern": "qa.aero2.dev/*", "zone_name": "aero2.dev" },
{ "pattern": "*.qa.aero2.dev/*", "zone_name": "aero2.dev" }
],
"vars": {
"PLATFORM_DOMAIN": "qa.aero2.dev"
}
}
}
}The QA environment uses a subdomain prefix (qa.aero2.dev), so QA applications appear at dashboard.qa.aero2.dev, swift-maple.qa.aero2.dev, etc.
Environment Variables
| Variable | Purpose | Example |
|---|---|---|
PLATFORM_DOMAIN | Base domain for the platform | aero2.dev |
These are set in wrangler.json under the vars section of each environment. The Worker uses them to determine:
- Whether a request is for the bare domain (marketing page)
- Whether a request is for the dashboard (
dashboard.{PLATFORM_DOMAIN}) - Which subdomain slug to extract for application lookup
Custom Hostnames (Cloudflare for SaaS)
Developers can map their own domains to their applications using Cloudflare's Custom Hostnames feature.
How It Works
- Developer adds a custom domain in the dashboard (e.g.,
auth.myapp.com) - Aero2 calls the Cloudflare API to create a Custom Hostname:
POST /zones/{zone_id}/custom_hostnames { "hostname": "auth.myapp.com", "ssl": { "method": "http", "type": "dv" } } - Cloudflare auto-provisions a DV SSL certificate for the custom domain
- Developer adds a CNAME record at their DNS provider:
auth.myapp.com -> swift-maple.aero2.dev - Traffic for
auth.myapp.comflows through Cloudflare to the same Worker - The Worker's tenant middleware resolves
auth.myapp.comviaSELECT * FROM applications WHERE custom_domain = ?
Verification Flow
After the developer adds the CNAME, they trigger verification:
POST /api/applications/:slug/custom-domain/verifyThis calls the Cloudflare API to check the hostname status. Once the hostname is active (CNAME verified, SSL provisioned), custom_domain_verified is set to true.
Removal
DELETE /api/applications/:slug/custom-domainThis removes the Custom Hostname via the Cloudflare API and clears the custom_domain column.
Required API Permissions
The following Cloudflare credentials are required for custom domain management:
| Binding | Type | Purpose |
|---|---|---|
CF_ZONE_ID | var | Cloudflare zone ID where custom hostnames are managed |
CF_API_TOKEN | secret | Cloudflare API token with Custom Hostnames (ssl_and_certificates:edit) permission |
Store CF_API_TOKEN as a Worker secret:
wrangler secret put CF_API_TOKEN --env productionLocal Development
During local development (npm run dev), the Worker runs at http://localhost:8787. Subdomain routing is not active locally since localhost does not support subdomains by default.
To test subdomain routing locally, you can:
- Edit
/etc/hoststo add entries like127.0.0.1 dashboard.localhost,127.0.0.1 myapp.localhost - Or use a tool like
dnsmasqto resolve*.localhostto127.0.0.1 - Access the Worker at
http://dashboard.localhost:8787
For most development work, the single-origin mode (no subdomain routing) is sufficient.