Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

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 A record 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

VariablePurposeExample
PLATFORM_DOMAINBase domain for the platformaero2.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

  1. Developer adds a custom domain in the dashboard (e.g., auth.myapp.com)
  2. 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" }
    }
  3. Cloudflare auto-provisions a DV SSL certificate for the custom domain
  4. Developer adds a CNAME record at their DNS provider: auth.myapp.com -> swift-maple.aero2.dev
  5. Traffic for auth.myapp.com flows through Cloudflare to the same Worker
  6. The Worker's tenant middleware resolves auth.myapp.com via SELECT * FROM applications WHERE custom_domain = ?

Verification Flow

After the developer adds the CNAME, they trigger verification:

POST /api/applications/:slug/custom-domain/verify

This 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-domain

This 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:

BindingTypePurpose
CF_ZONE_IDvarCloudflare zone ID where custom hostnames are managed
CF_API_TOKENsecretCloudflare 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 production

Local 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:

  1. Edit /etc/hosts to add entries like 127.0.0.1 dashboard.localhost, 127.0.0.1 myapp.localhost
  2. Or use a tool like dnsmasq to resolve *.localhost to 127.0.0.1
  3. Access the Worker at http://dashboard.localhost:8787

For most development work, the single-origin mode (no subdomain routing) is sufficient.