Skip to content

[bug] routes inheritance silently transfers ownership of custom domains between env Workers on every deploy #13925

Description

@brettdavies

What versions & operating system are you using?

  • Wrangler 4.81.0 (deployed via cloudflare/wrangler-action@v3.14.1, also reproduced locally)
  • Linux Ubuntu 24.04 on ubuntu-latest runners; Linux 6.8 locally
  • cloudflare/wrangler-action pinned at da0e0dfe58b7a431659754fdf3f186c529afbe65

Please provide a link to a minimal reproduction

Reproduction is on the live agentnative-site and agentnative-site-staging Workers on Cloudflare account 6c1bafea907fecbd4ad665b8d0a78e53 in the brettdavies/agentnative-site repo. A self-contained external repro can be built on request.

Describe the Bug

Summary

When the top-level Wrangler config declares a routes array containing a custom domain, and an env.<name> block does not override routes, the custom domain gets silently attached to BOTH Workers because routes is an inheritable key. Every wrangler deploy --env <name> re-asserts the inherited custom domain on the env Worker, which steals it away from the top-level Worker on every deploy.

This is documented behavior per the Inheritable keys list (and the contrast with the legacy v1 docs, which explicitly listed routes as not-inherited, makes the change easy to miss when porting older configs forward). The deploy log is the only practical signal that the inheritance has fired, and that signal arrives after the binding has already moved.

Setup that triggers it

{
  "name": "agentnative-site",
  "main": "src/worker/index.ts",
  "compatibility_date": "2026-04-01",
  "routes": [{ "pattern": "anc.dev", "custom_domain": true }],
  "workers_dev": false,
  "env": {
    "staging": {
      "name": "agentnative-site-staging",
      "workers_dev": true
      // No `routes` field here. Looks like "staging has no custom domain".
      // Actually inherits the top-level routes and re-attaches anc.dev
      // to the staging Worker on every `wrangler deploy --env staging`.
    }
  }
}

Observed sequence

  1. wrangler deploy (top-level): attaches anc.dev to agentnative-site. Log shows Deployed agentnative-site triggers ... anc.dev (custom domain).
  2. wrangler deploy --env staging: attaches anc.dev to agentnative-site-staging. Log shows Deployed agentnative-site-staging triggers ... anc.dev (custom domain). The production binding is gone; the custom-domain record id is reassigned to the staging Worker.
  3. Any subsequent wrangler deploy --env staging keeps anc.dev bound to staging.
  4. Any subsequent wrangler deploy (top-level) flips it back.

This is the entire mechanism behind a "routing drift" bug we have been chasing for two weeks. It originally looked like a one-time manual misconfiguration during launch; in reality, every push to either branch was flipping the binding.

Why it surprised us

routes being inheritable interacts poorly with custom domains because the resulting CF resource is account-scoped, not Worker-scoped, and only one Worker can own a given custom-domain record. Other inheritable keys (assets, migrations, observability) are either no-ops to share or harmlessly redundant when inherited; routes for a custom domain is destructive when inherited because it transfers ownership.

The Wrangler deploy output does include the line Deployed <worker> triggers ... <hostname> (custom domain) on every deploy, but the framing reads as a normal "your Worker is live at these URLs" message, not as a "this attached the domain, possibly stealing it from another Worker" warning. The destination Worker doesn't have its own way to declare "I should NEVER own this domain"; the only way to break the inheritance is to set routes: [] (empty array) in the env block.

Custom-domain record ids appear to be deterministic

The custom-domain record id 8721a2ad00ee0c10c50d3357dba7b4e4efc7487a was assigned during the original v0.1 launch, then survived multiple delete+recreate cycles intact. After a DELETE on the record, a subsequent PUT of a new binding (different service, same hostname + zone_id) returned the same id back. This is undocumented, observed empirically. Useful context for anyone debugging "the record id matches what I had earlier but the binding is wrong" - the id is not stale, it's reusable.

What workarounds we ended up using

  • Explicit empty override in the env block: "routes": []. This breaks the inheritance and the env Worker stops asserting anc.dev. Verified to work locally; pending live confirmation on next staging deploy.
  • A gh api ... -X DELETE followed by gh api ... -X PUT to manually move the binding back to production after each accidental rebind. Recovery-only, not a fix.

Suggested improvements

  1. Wrangler deploy-time warning when an env block silently inherits a routes array that contains an entry where custom_domain: true. Something like "warning: env.staging inherits route anc.dev from the top-level config; the deploy will reassign this custom domain from agentnative-site to agentnative-site-staging. Set routes: [] (or an explicit list) in env.staging to override."
  2. Documentation callout under Inheritable keys and on the Environments page noting that inheriting routes from a parent with custom domains will reassign the domain on each deploy.
  3. Document the deterministic record id behavior for workers/domains (record id derived from (account_id, zone_id, hostname), recycled across delete/recreate cycles).

What this issue is NOT

This is being filed as a docs/UX bug, not a Wrangler defect. The behavior matches the documented inheritance semantics. The cost is that the documented behavior, in a common multi-env setup with a production custom domain, silently mis-routes production traffic to staging on every dev push.

Please provide any relevant error logs

No errors. The deploys succeed silently. The relevant log lines are the Deployed <worker> triggers sections in two staging deploy runs:

Both runs contain the line anc.dev (custom domain) under the Deployed agentnative-site-staging triggers heading. That single log line is the only deploy-time signal that ownership of anc.dev just transferred.

Metadata

Metadata

Assignees

Labels

documentationImprovements or additions to documentationpackage:wranglerRelating to the `wrangler` packagequick-winPotentially easy/straightforward issue to tackle

Type

Fields

No fields configured for Task.

Projects

Status
Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions