-
Notifications
You must be signed in to change notification settings - Fork 1.3k
[wrangler] fix: propagate zone from unstable_getMiniflareWorkerOptions so vite dev outbound CF-Worker reflects dev.host
#13919
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
petebacondarwin
merged 6 commits into
main
from
fix/13791-zone-on-unstable-get-miniflare-worker-options
May 19, 2026
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
2a84e38
[wrangler] fix: propagate `zone` from `unstable_getMiniflareWorkerOpt…
petebacondarwin 98746d3
Scope fix to routes only; do not consult dev.host
petebacondarwin 6cd9379
Update .changeset/fix-zone-on-unstable-get-miniflare-worker-options.md
petebacondarwin 1bfd0c4
Prefer route `zone_name` over pattern hostname when deriving the CF-W…
petebacondarwin ef7c188
Inline wrangler dev zone derivation; drop the `dev.zone` field
petebacondarwin 6bcea69
Add test for unparseable pattern + no zone_name; move CF-Worker zone …
petebacondarwin File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
16 changes: 16 additions & 0 deletions
16
.changeset/fix-zone-on-unstable-get-miniflare-worker-options.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| --- | ||
| "wrangler": patch | ||
| "@cloudflare/vite-plugin": patch | ||
| "@cloudflare/vitest-pool-workers": patch | ||
| --- | ||
|
|
||
| Fix the outbound `CF-Worker` header reflecting the route pattern hostname instead of the parent zone, and falling back to `<worker-name>.example.com` under `vite dev`, `vitest-pool-workers`, and `getPlatformProxy` | ||
|
|
||
| Two related issues affected the `CF-Worker` header on outbound subrequests in local development: | ||
|
|
||
| 1. Under `@cloudflare/vite-plugin`, `@cloudflare/vitest-pool-workers`, and `getPlatformProxy`, the header fell back to `<worker-name>.example.com` even when `routes` were configured, because `unstable_getMiniflareWorkerOptions` and the equivalent `getPlatformProxy` worker-options path did not propagate a `zone` value to Miniflare. This broke local development against services that reject unknown `CF-Worker` hosts (for example, Apple WeatherKit returns `403 Forbidden`). | ||
| 2. Across the above paths and `wrangler dev --local`, when a route used the `zone_name` field (for example `{ pattern: "foo.example.com/*", zone_name: "example.com" }`), the header was set to the pattern's hostname (`foo.example.com`) rather than the zone name (`example.com`). Production [sets `CF-Worker` to the zone name that owns the Worker](https://developers.cloudflare.com/fundamentals/reference/http-headers/#cf-worker), so this was inconsistent with deployed behaviour. | ||
|
|
||
| Both bugs are fixed: the new `unstable_getMiniflareWorkerOptions` / `getPlatformProxy` path now propagates a `zone` derived from the first configured route, and all four local-dev paths now prefer a route's explicit `zone_name` over the pattern hostname when computing that zone. When `zone_name` isn't set, the existing best-effort behaviour is preserved — for `wrangler dev` this means `dev.host` is still honoured as a local override and the pattern hostname is used as a final fallback. Resolving the parent zone for `zone_id`-only, `custom_domain`, or plain-string routes would require an API lookup, so locally we still approximate it with the pattern hostname. | ||
|
|
||
| Note: `dev.host` is intentionally not consulted by the `unstable_getMiniflareWorkerOptions` / `getPlatformProxy` paths — the `dev` config block is specific to `wrangler dev`. |
54 changes: 54 additions & 0 deletions
54
packages/vite-plugin-cloudflare/playground/cron-triggers/__tests__/cf-worker-header.spec.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| import http from "node:http"; | ||
| import { afterAll, beforeAll, test } from "vitest"; | ||
| import { fetchJson } from "../../__test-utils__"; | ||
|
|
||
| // Regression test for https://github.com/cloudflare/workers-sdk/issues/13791. | ||
| // | ||
| // Under `vite dev`, outbound subrequests should carry a `CF-Worker` header | ||
| // matching the zone name that owns the Worker (see this fixture's | ||
| // `wrangler.jsonc`). Before the fix the plugin dropped the zone before | ||
| // handing the Worker to Miniflare and Miniflare fell back to | ||
| // `<worker-name>.example.com`. The fixture's route declares | ||
| // `zone_name: "example.com"` explicitly, so we expect that — not the | ||
| // pattern's hostname (`cf-worker-header-test.example.com`) — to be sent, | ||
| // matching production behaviour documented at | ||
| // https://developers.cloudflare.com/fundamentals/reference/http-headers/#cf-worker. | ||
|
|
||
| let echoServer: http.Server; | ||
| let echoUrl: string; | ||
|
|
||
| beforeAll(async () => { | ||
| echoServer = http.createServer((req, res) => { | ||
| res.setHeader("Content-Type", "application/json"); | ||
| res.end( | ||
| JSON.stringify({ | ||
| cfWorker: req.headers["cf-worker"] ?? null, | ||
| }) | ||
| ); | ||
| }); | ||
| // Bind to port 0 so the OS assigns a free port — avoids collisions with | ||
| // other playground fixtures that use fixed ports (e.g. `stream-binding`). | ||
| await new Promise<void>((resolve) => | ||
| echoServer.listen(0, "127.0.0.1", resolve) | ||
| ); | ||
| const address = echoServer.address(); | ||
| if (!address || typeof address === "string") { | ||
| throw new Error("Expected echoServer.address() to be an AddressInfo"); | ||
| } | ||
| echoUrl = `http://127.0.0.1:${address.port}/`; | ||
| }); | ||
|
|
||
| afterAll(async () => { | ||
| await new Promise<void>((resolve, reject) => | ||
| echoServer.close((err) => (err ? reject(err) : resolve())) | ||
| ); | ||
| }); | ||
|
|
||
| test("outbound subrequests use the first configured route's zone name as the `CF-Worker` header", async ({ | ||
| expect, | ||
| }) => { | ||
| const response = await fetchJson( | ||
| `/cf-worker-header?echoUrl=${encodeURIComponent(echoUrl)}` | ||
| ); | ||
| expect(response).toEqual({ cfWorker: "example.com" }); | ||
| }); |
16 changes: 15 additions & 1 deletion
16
packages/vite-plugin-cloudflare/playground/cron-triggers/src/index.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
136 changes: 136 additions & 0 deletions
136
packages/wrangler/src/__tests__/unstable-get-miniflare-worker-options.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,136 @@ | ||
| import { writeWranglerConfig } from "@cloudflare/workers-utils/test-helpers"; | ||
| import { describe, it } from "vitest"; | ||
| import { unstable_getMiniflareWorkerOptions } from "../api"; | ||
| import { runInTempDir } from "./helpers/run-in-tmp"; | ||
|
|
||
| describe("unstable_getMiniflareWorkerOptions", () => { | ||
| runInTempDir(); | ||
|
|
||
| describe("zone derivation (used for the outbound CF-Worker header)", () => { | ||
| it("derives the zone from a single `route` string", ({ expect }) => { | ||
| writeWranglerConfig( | ||
| { | ||
| name: "test-worker", | ||
| main: "./index.js", | ||
| compatibility_date: "2024-10-04", | ||
| route: "https://example.com/api/*", | ||
| }, | ||
| "./wrangler.json" | ||
| ); | ||
| const { workerOptions } = | ||
| unstable_getMiniflareWorkerOptions("./wrangler.json"); | ||
| expect(workerOptions.zone).toBe("example.com"); | ||
| }); | ||
|
|
||
| it("uses the first entry in `routes`, preferring its `zone_name`", ({ | ||
| expect, | ||
| }) => { | ||
| writeWranglerConfig( | ||
| { | ||
| name: "test-worker", | ||
| main: "./index.js", | ||
| compatibility_date: "2024-10-04", | ||
| routes: [ | ||
| { pattern: "foo.example.com/*", zone_name: "example.com" }, | ||
| "bar.example.com/*", | ||
| ], | ||
| }, | ||
| "./wrangler.json" | ||
| ); | ||
| const { workerOptions } = | ||
| unstable_getMiniflareWorkerOptions("./wrangler.json"); | ||
| // Per https://developers.cloudflare.com/fundamentals/reference/http-headers/#cf-worker | ||
| // the `CF-Worker` header is the zone name that owns the Worker, not | ||
| // the route pattern's hostname. | ||
| expect(workerOptions.zone).toBe("example.com"); | ||
| }); | ||
|
|
||
| it("falls back to the pattern hostname when `zone_name` is absent", ({ | ||
| expect, | ||
| }) => { | ||
| writeWranglerConfig( | ||
| { | ||
| name: "test-worker", | ||
| main: "./index.js", | ||
| compatibility_date: "2024-10-04", | ||
| routes: [{ pattern: "foo.example.com/*", zone_id: "abc123" }], | ||
| }, | ||
| "./wrangler.json" | ||
| ); | ||
| const { workerOptions } = | ||
| unstable_getMiniflareWorkerOptions("./wrangler.json"); | ||
| // Without `zone_name` or an account-scoped API lookup we can't | ||
| // determine the parent zone locally, so the pattern hostname is | ||
| // the closest approximation. | ||
| expect(workerOptions.zone).toBe("foo.example.com"); | ||
| }); | ||
|
|
||
| it("uses `zone_name` for unparseable patterns like `*/*`", ({ expect }) => { | ||
| writeWranglerConfig( | ||
| { | ||
| name: "test-worker", | ||
| main: "./index.js", | ||
| compatibility_date: "2024-10-04", | ||
| routes: [{ pattern: "*/*", zone_name: "example.com" }], | ||
| }, | ||
| "./wrangler.json" | ||
| ); | ||
| const { workerOptions } = | ||
| unstable_getMiniflareWorkerOptions("./wrangler.json"); | ||
| expect(workerOptions.zone).toBe("example.com"); | ||
| }); | ||
|
|
||
| it("ignores `dev.host` (the `dev` config block is `wrangler dev`-only)", ({ | ||
| expect, | ||
| }) => { | ||
| writeWranglerConfig( | ||
| { | ||
| name: "test-worker", | ||
| main: "./index.js", | ||
| compatibility_date: "2024-10-04", | ||
| dev: { | ||
| host: "ignored.example.com", | ||
| }, | ||
| }, | ||
| "./wrangler.json" | ||
| ); | ||
| const { workerOptions } = | ||
| unstable_getMiniflareWorkerOptions("./wrangler.json"); | ||
| expect(workerOptions.zone).toBeUndefined(); | ||
| }); | ||
|
|
||
| it("derives the zone from `routes` even when `dev.host` is also set", ({ | ||
|
petebacondarwin marked this conversation as resolved.
|
||
| expect, | ||
| }) => { | ||
| writeWranglerConfig( | ||
| { | ||
| name: "test-worker", | ||
| main: "./index.js", | ||
| compatibility_date: "2024-10-04", | ||
| dev: { | ||
| host: "ignored.example.com", | ||
| }, | ||
| routes: [{ pattern: "foo.example.com/*", zone_name: "example.com" }], | ||
| }, | ||
| "./wrangler.json" | ||
| ); | ||
| const { workerOptions } = | ||
| unstable_getMiniflareWorkerOptions("./wrangler.json"); | ||
| expect(workerOptions.zone).toBe("example.com"); | ||
| }); | ||
|
|
||
| it("returns undefined when no routes are configured", ({ expect }) => { | ||
| writeWranglerConfig( | ||
| { | ||
| name: "test-worker", | ||
| main: "./index.js", | ||
| compatibility_date: "2024-10-04", | ||
| }, | ||
| "./wrangler.json" | ||
| ); | ||
| const { workerOptions } = | ||
| unstable_getMiniflareWorkerOptions("./wrangler.json"); | ||
| expect(workerOptions.zone).toBeUndefined(); | ||
| }); | ||
| }); | ||
| }); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.