Internal-only Worker still reachable on its default workers.dev URL — which public triggers need disabling?
0 reputation · 22 Feb 2023, 16:16 UTC
0 reputation · 22 Feb 2023, 16:16 UTC
A Worker is intended to be invoked only through a Service Binding from another Worker, yet every deployed Worker also gets a default <name>.<account-subdomain>.workers.dev URL that exists independently of any zone route or custom domain. That makes a supposedly internal Worker publicly reachable unless the route is explicitly disabled.
The configuration surface is split across several places: the workers_dev setting in Wrangler (whose key name and default behavior vary by Wrangler version), separately controlled preview URLs, plus any zone routes or custom domains attached in the dashboard or via the API. Workers also have no built-in authentication, so a Service Binding alone does not remove the public URL — protection would otherwise require Cloudflare Access on a controlled hostname or an in-Worker check such as a shared-secret header.
The open decision is whether the CI pipeline should default new Workers to workers_dev = false and opt in to public URLs only for deliberately internet-facing services, instead of relying on manual per-Worker toggles and audits.
Specific questions:
workers_dev, preview_urls) and defaults apply, and does disabling the production workers.dev route also cover preview URLs?32775 reputation · 22 Feb 2023, 21:54 UTC
To make a Worker reachable only via a Service Binding, you must disable every public trigger:
workers_dev key in wrangler.toml.preview_urls (or the legacy preview_url key).Disabling workers_dev does not automatically disable preview URLs; both flags must be set to false for a fully internal Worker.
Cloudflare Workers were designed for quick public prototyping. The workers_dev flag turns on a <name>.<account-subdomain>.workers.dev hostname so you can test without a custom domain. When set to false, that hostname is removed.
# wrangler.toml – pin the Wrangler version you use
wrangler_version = "3.0.0"
# Disable public workers.dev route
workers_dev = false
# Disable preview URLs (preview_urls is a boolean; if you only need one, use preview_url)
preview_urls = false
# (Optional) Remove any zone routes in the dashboard or via the API
# No wrangler key controls this – you must delete them manually.
Workers → Triggers for the Worker. Confirm no .workers.dev hostname, no preview URL, and no zone routes are listed.curl -X GET "https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/workers/scripts/${SCRIPT_NAME}/routes" \
-H "Authorization: Bearer ${API_TOKEN}" \
-H "Content-Type: application/json"
The response should show an empty result array for a fully internal Worker.wrangler routes list --script ${SCRIPT_NAME}
Again, expect an empty list..workers.dev URL and any preview URLs. They should return 404 Not Found or a 403 if the route is still present.Once the public routes are disabled, the Worker remains functional for Service Bindings because bindings use the internal Cloudflare runtime, not the public hostname.
Because disabling the public route is a breaking change, inventory all callers:
.workers.dev patterns.Only after confirming no external callers exist should you commit the workers_dev and preview_urls changes.
Do you have any zone routes or custom domains attached to this Worker that might still expose it publicly?
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.