Skip to content

feat(miniflare): add unsafeEntrypointSubdomains for localhost subdomain routing#12584

Draft
edmundhung wants to merge 1 commit intomainfrom
edmundhung/miniflare-expose-entrypoints
Draft

feat(miniflare): add unsafeEntrypointSubdomains for localhost subdomain routing#12584
edmundhung wants to merge 1 commit intomainfrom
edmundhung/miniflare-expose-entrypoints

Conversation

@edmundhung
Copy link
Member

@edmundhung edmundhung commented Feb 17, 2026

Fixes n/a.

This is the first part for landing the localhost subdomain routing feature from #12550.

This adds the ability to access worker entrypoints directly via localhost subdomains during local development. In multi-worker setups, each worker and its entrypoints get their own subdomain URLs:

  • http://{worker}.localhost:{port}: routes to the worker's default entrypoint
  • http://{entrypoint}.{worker}.localhost:{port}: routes to a specific entrypoint

Not all systems resolve *.localhost subdomains. A DNS check will run on startup and warns if the system doesn't support it. Browsers (Chrome, Edge, Firefox) resolve these independently and will always work. Non-browser tools like curl might not resolve on some systems.

To enable this feature, developer will need to set the unsafeEntrypointSubdomains options on each worker which is a map between export name and the alias (subdomain).


  • Tests
    • Tests included/updated
    • Automated tests not possible - manual testing has been completed as follows:
    • Additional testing not necessary because:
  • Public documentation
    • Cloudflare docs PR(s):
    • Documentation not necessary because: We will update the docs when we land the integration with Wrangler / Vite

A picture of a cute animal (not mandatory, but encouraged)


Open with Devin

@edmundhung edmundhung requested a review from a team as a code owner February 17, 2026 16:14
@changeset-bot
Copy link

changeset-bot bot commented Feb 17, 2026

🦋 Changeset detected

Latest commit: 687cf8f

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@claude
Copy link
Contributor

claude bot commented Feb 17, 2026

Claude finished @edmundhung's task —— View job


Changeset Review

  • Read changeset guidelines
  • Review changeset file
  • Check for cute animal image

✅ All changesets look good

The changeset follows all guidelines:

  1. ✅ Version type is minor (appropriate for a new feature/configuration option)
  2. ✅ Description is clear and meaningful, includes usage explanation
  3. ✅ No h1/h2/h3 headers used
  4. ✅ Not applicable (no new analytics collection)
  5. ✅ Not a dependabot change
  6. ✅ Experimental feature consideration: The feature is prefixed with unsafe, clearly indicating its experimental nature

Copy link
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 potential issue.

View 5 additional findings in Devin Review.

Open in Devin Review

serviceEntryBindings.push({
name: `${CoreBindings.SERVICE_USER_ENTRYPOINT_PREFIX}${workerName}:${entry.export}`,
service: {
name: getUserServiceName(workerName),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Lowercased worker name in entrypoint service bindings causes mismatch with actual service names

When a worker has a mixed-case name (e.g., "MyApi"), the entrypoint service bindings reference a non-existent service, which will cause workerd to fail or the binding to be undefined at runtime.

Root Cause

In getEntrypointSubdomains() at packages/miniflare/src/index.ts:801, the worker name is lowercased when used as a key:

result[workerName.toLowerCase()] = aliasToEntry;

Then in getGlobalServices() at packages/miniflare/src/plugins/core/index.ts:1056-1064, the code iterates over allEntrypointSubdomains and uses these lowercased worker names to create service bindings:

for (const [workerName, entrypoints] of Object.entries(allEntrypointSubdomains)) {
    // workerName is already lowercased from getEntrypointSubdomains
    service: {
        name: getUserServiceName(workerName), // produces "core:user:myapi"
        ...
    }
}

However, the actual worker service is registered with the original (non-lowercased) name at packages/miniflare/src/plugins/core/index.ts:752:

const serviceName = getUserServiceName(options.name); // produces "core:user:MyApi"

So for a worker named "MyApi", the entrypoint binding points to core:user:myapi while the actual service is core:user:MyApi. This mismatch means workerd will reference a non-existent service.

All tests use lowercase worker names ("api", "admin", "app", etc.), so this bug is not caught by the test suite.

Impact: Entrypoint subdomain routing will fail for any worker with uppercase characters in its name.

Prompt for agents
The fix needs to be applied in two coordinated places:

1. In packages/miniflare/src/index.ts, the getEntrypointSubdomains function (around line 801): Instead of only storing the lowercased worker name as the key, also preserve the original worker name. One approach is to change the data structure to include the original name, e.g. store a mapping from lowercased name to { originalName, aliasToEntry }.

2. In packages/miniflare/src/plugins/core/index.ts, the getGlobalServices function (around line 1056-1064): When creating service bindings, use the original (non-lowercased) worker name for getUserServiceName() calls, while keeping the lowercased name for the binding name prefix (since the entry worker looks up by lowercased hostname).

Alternatively, a simpler fix would be to not lowercase the worker name in getEntrypointSubdomains at all, and instead lowercase the hostname parts in resolveHostnameRoute (in entry.worker.ts) before looking up in the config. Since URL hostnames are already lowercased by the URL spec, and the config keys would use the original case, you would need to lowercase the config keys at lookup time in the entry worker. The cleanest approach is probably to store both the original and lowercased names.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 17, 2026

create-cloudflare

npm i https://pkg.pr.new/create-cloudflare@12584

@cloudflare/kv-asset-handler

npm i https://pkg.pr.new/@cloudflare/kv-asset-handler@12584

miniflare

npm i https://pkg.pr.new/miniflare@12584

@cloudflare/pages-shared

npm i https://pkg.pr.new/@cloudflare/pages-shared@12584

@cloudflare/unenv-preset

npm i https://pkg.pr.new/@cloudflare/unenv-preset@12584

@cloudflare/vite-plugin

npm i https://pkg.pr.new/@cloudflare/vite-plugin@12584

@cloudflare/vitest-pool-workers

npm i https://pkg.pr.new/@cloudflare/vitest-pool-workers@12584

@cloudflare/workers-editor-shared

npm i https://pkg.pr.new/@cloudflare/workers-editor-shared@12584

@cloudflare/workers-utils

npm i https://pkg.pr.new/@cloudflare/workers-utils@12584

wrangler

npm i https://pkg.pr.new/wrangler@12584

commit: 687cf8f

@edmundhung edmundhung marked this pull request as draft February 17, 2026 21:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Untriaged

Development

Successfully merging this pull request may close these issues.

1 participant

Comments