Domains and secrets

Attach your own domain with automatic TLS, and configure an app's credentials as per-environment worker secrets — including the reserved names the platform manages for you.

The Spikefrost Team30 Jul 20263 min read

Two pieces of per-environment configuration: the hostname the world reaches your app on, and the credentials your app uses.

Custom domains

Every app gets a platform URL immediately. Attaching your own domain is two steps.

1. Register the domain with the app:

sf add_custom_domain --customDomain www.example.com

The response includes the CNAME record to create — a target hostname to point at — plus the current TLS status.

2. Create that CNAME at your DNS provider:

CNAME   www.example.com   →   <target printed by the command>

TLS is then issued automatically, typically 5–15 minutes after DNS propagates. Check progress with:

sf list_custom_domains

Each entry shows the domain, its environment, an overall status, and separate ssl and hostname states. All three go active when the domain is fully live:

{
  "customDomain": "www.spikefrost.com",
  "environment": "production",
  "status": "active",
  "ssl": { "status": "active", "isActive": true }
}

Notes worth having in advance:

  • Domains are per environment. www.example.com on production and staging.example.com on staging is the normal arrangement.
  • A CNAME can't sit on a bare apex domain under standard DNS rules. Either use www and redirect the apex at your DNS provider, or use a provider that supports apex flattening / ALIAS records.
  • Everything keeps working on the platform URL — adding a domain doesn't take the original away.
  • The domain must be one you control. Point DNS you own; the CNAME step is what actually routes traffic.

Worker secrets

Anything your app needs and must not commit — a payment provider key, a third-party API token, a signing secret.

sf set_worker_secret --secretName STRIPE_SECRET_KEY --secretValue sk_live_...
sf list_worker_secrets
sf delete_worker_secret --secretName STRIPE_SECRET_KEY

Read them in Worker code like any binding, after declaring the type in Env:

export interface Env {
  // ...
  STRIPE_SECRET_KEY?: string;
}

app.post('/api/checkout', async (c) => {
  const key = c.env.STRIPE_SECRET_KEY;
  if (!key) return c.json({ error: 'not configured' }, 500);
  // ...
});

The rules:

  • Per environment. Production and staging hold separate values — which is the point: staging uses test keys.
  • The app must have been deployed at least once before it can hold secrets; there's no environment to attach them to otherwise.
  • Values are never readable back. Listing returns names only. Changing a secret means setting it again.
  • They survive deploys. Each deploy reattaches them, and the deployment event stream includes a SECRETS step.
  • Never put a secret in source, in wrangler.toml, or in client-side code. Server-side only, always.

Reserved names

Some names are managed by the platform and rejected if you try to set them:

Name What it is
SPIKE_TENANT_API_KEY Auth for platform capability calls
SPIKE_ROUTINE_SIGNING_SECRET Verifies scheduled and queue callbacks into your app
SPIKE_PROJECT_ID This app's id
PUBLIC_URL The app's public URL

These are injected at deploy time and always correct — including after a clone, which is exactly why you should read c.env.SPIKE_PROJECT_ID rather than hardcoding an app id anywhere.

Treat the tenant key as highly sensitive. It can reach every integration your team has configured. It belongs on the server, never in a response, never in a log line, never in the browser.

Choosing where configuration lives

Kind of value Where
Credentials, API keys, signing secrets Worker secrets
Values that differ per environment but aren't secret Worker secrets (simplest) or a config table
Settings an admin should be able to change at runtime A database table, edited through your own admin UI
Feature flags you flip without a deploy KV (c.env.KV)
Non-secret constants Source code

The distinction that matters: a secret needs a deploy to take effect for new code paths, while KV and database values change live. Flags you'll flip during an incident belong in KV.

Next

  • Build and deploy — the SECRETS step in a deployment's events.
  • Environments — why per-environment configuration is the default.
  • Files and assets — calling third-party services with platform-held credentials instead of your own keys.

Frequently asked questions

What DNS record do I need for a custom domain?

A CNAME pointing your hostname at the target the add command prints. TLS is issued automatically, usually within 5–15 minutes of DNS propagating.

Can two environments have different domains?

Yes. Domains are attached per environment, so staging.example.com can point at your staging environment while www.example.com serves production.

Are secrets visible after I set them?

No. Listing secrets returns names only, never values. To change one, set it again; to remove it, delete it by name.

Do secrets survive a deploy?

Yes — they're part of the environment's configuration, and each deploy reattaches them. Deployment events include a SECRETS step so you can confirm.

Why was my secret name rejected?

Some names are reserved and provisioned automatically by the platform — SPIKE_TENANT_API_KEY, SPIKE_ROUTINE_SIGNING_SECRET, SPIKE_PROJECT_ID, and PUBLIC_URL. Use your own names for your own credentials.