Local development
Local development allows you to build, run, and test your Worker code on your own machine before deploying it to Cloudflare's network. This is made possible through Miniflare, a simulator that executes your Worker code using the same runtime used in production, workerd
↗. By default, your Worker's bindings connect to locally simulated resources, but can be configured to interact with the real, production resource.
You can start a local development server using:
- Our official CLI Wrangler, using the built-in
wrangler dev
command.
npx wrangler dev
yarn wrangler dev
pnpm wrangler dev
- Vite ↗, using the Cloudflare Vite plugin.
npx vite dev
yarn vite dev
pnpm vite dev
Both Wrangler and the Cloudflare Vite plugin are clients to Miniflare, and fully supported by Cloudflare. For guidance on choosing when to use Wrangler versus Vite, see our guide Choosing between Wrangler & Vite.
By default, running wrangler dev
/ vite dev
(when using the Vite plugin) means that:
- Your Worker code runs on your local machine.
- All resources your Worker is bound to in your Wrangler configuration are simulated locally.
Bindings are interfaces that allow your Worker to interact with various Cloudflare resources (like KV namespaces, R2 buckets, D1 databases, Queues, Durable Objects, etc). In your Worker code, these are accessed via the env
object (such as env.MY_KV
).
During local development, your Worker code interacts with these bindings using the exact same API calls (such as env.MY_KV.put()
) as it would in a deployed environment. Miniflare intercepts these calls and directs them to the locally simulated resource. These local resources are initially empty, but you can populate them with data, as documented in Adding local data.
- By default, bindings connect to local resource simulations (except for AI bindings, as AI models always run remotely).
- You can override this default behavior and connect to the remote resource, on a per-binding basis. This lets you connect to real, production resources while still running your Worker code locally.
There are times when you might want to connect to the real, remote resource during local development instead of the locally simulated resource. You can configure this on a per-binding basis, by setting remote: true
in the binding definition.
For example:
{ "name": "my-worker", "compatibility_date": "2025-06-01",
"browser": { "binding": "MY_BROWSER", "remote": true, },
"queues": { "producers": [ { "queue": "queues-web-crawler", "binding": "queues_web_crawler", "remote": false, // Default value, but here for illustration }, ], "consumers": [ { "queue": "queues-web-crawler", "max_batch_size": 5, "max_batch_timeout": 30, "max_retries": 3, "dead_letter_queue": "web-crawler-dlq", "remote": false, // Default value, but here for illustration }, ], },
"r2_buckets": [ { "bucket_name": "screenshots-bucket", "binding": "screenshots_bucket", "preview_bucket_name": "preview-screenshots-bucket", "remote": true, }, ],}
name = "my-worker"compatibility_date = "2025-06-01"
[browser]binding = "MY_BROWSER"remote = true
[[queues.producers]]queue = "queues-web-crawler"binding = "queues_web_crawler"remote = false
[[queues.consumers]]queue = "queues-web-crawler"max_batch_size = 5max_batch_timeout = 30max_retries = 3dead_letter_queue = "web-crawler-dlq"remote = false
[[r2_buckets]]bucket_name = "screenshots-bucket"binding = "screenshots_bucket"preview_bucket_name = "preview-screenshots-bucket"remote = true
In the above example, you’re able to more realistically test out and trust that any Browser Rendering interactions work as intended, as well as validate the end results of the screenshots in R2 – while leaving room to rapidly iterate on the actual logic within your Worker and queues.
When you run you local dev command and a binding definition includes remote: true
, the following takes place:
- Your Worker's code continues to run on your machine within
workerd
, managed by Miniflare (whether initiated by Wrangler or Vite). - For all bindings marked with
remote: true
, Miniflare routes its operations (such asenv.MY_KV.put()
) to the deployed resource. This connection is facilitated through a secure proxy mechanism provisioned on Cloudflare's network. - All other bindings not explicitly configured with
remote: true
continue to use their default local simulations. - Your Worker code remains unchanged, regardless of whether the bindings is fully local or connected remotely.
To protect production data, you can create and specify preview resources in your Wrangler configuration, such as:
- Preview namespaces for KV stores:
preview_id
. - Preview buckets for R2 storage:
preview_bucket_name
. - Preview database IDs for D1:
preview_database_id
If preview configuration is present for a binding, setting remote: true
will make your local session connect to that designated remote preview resource.
If no preview configuration is specified, remote: true
will connect to the main remote production resource.
For example:
{ "name": "my-worker", "compatibility_date": "2025-06-01",
"r2_buckets": [ { "bucket_name": "screenshots-bucket", "binding": "screenshots_bucket", "preview_bucket_name": "preview-screenshots-bucket", "remote": true, }, ],}
name = "my-worker"compatibility_date = "2025-06-01"
[[r2_buckets]]bucket_name = "screenshots-bucket"binding = "screenshots_bucket"preview_bucket_name = "preview-screenshots-bucket"remote = true
Running your local dev command (wrangler dev
/ vite dev
) with the above configuration means that:
- Your Worker code runs locally
- All calls made to
env.screenshots_bucket
will use thepreview-screenshots-bucket
resource, rather than the productionscreenshots-bucket
.
We recommend configuring specific bindings to connect to their remote counterparts. These services often rely on Cloudflare's network infrastructure or have complex backends that are not fully simulated locally.
The following bindings are recommended to have remote: true
in your Wrangler configuration:
-
Browser Rendering: To interact with a real headless browser for rendering.
-
Workers AI: To utilize actual AI models deployed on Cloudflare's network for inference.
-
Vectorize: To connect to your production Vectorize indexes for accurate vector search and similarity operations.
-
Service bindings to mTLS-enabled services: If you have a service binding that points to a deployed Worker or service that requires mTLS authentication from its clients, setting
remote: true
for that service binding is recommended for realistic end-to-end testing. Your Worker would then use anmtls_certificates
binding to make the call.
- If
remote: true
is not specified for Browser Rendering, Vectorize, or for a service binding that is intended to call an mTLS-protected remote service, Cloudflare will issue a warning. This prompts you to consider enabling it for a more production-like testing experience. - For Workers AI, if the
ai.remote
property is set tofalse
, Cloudflare will produce an error. If the property is omitted, Cloudflare will connect to the remote resource and issue a warning to add the property to configuration.
{ "name": "my-worker", "compatibility_date": "2025-06-01", // ... other configurations ... "browser": { "binding": "MY_BROWSER", "remote": true // Recommended }, "vectorize": [ { "binding": "MY_VECTORIZE_INDEX", "index_name": "my-prod-index", "remote": true // Recommended } ], "services": [ { "binding": "API_SERVICE_REQUIRING_MTLS", // A service this worker calls "service": "deployed-api-worker-expecting-mtls", // This target service expects mTLS "remote": true // Recommended } ], "ai": { "binding": "AI", // Access as env.AI "remote": true // Required; setting to false or omitting will error }, "mtls_certificates": [ // Defines how this worker can present a client certificate { "binding": "MY_CLIENT_CERT_FETCHER", // Access as env.MY_CLIENT_CERT_FETCHER "certificate_id": "<YOUR_UPLOADED_CERT_ID>" // Note: 'remote: true' is NOT applicable here. This binding provides a // an object that has a `.fetch()` method. } ] // Note: For other services like KV, R2, D1, Queues, 'remote: true' is optional}
name = "my-worker"compatibility_date = "2025-06-01"
[browser]binding = "MY_BROWSER"remote = true
[[vectorize]]binding = "MY_VECTORIZE_INDEX"index_name = "my-prod-index"remote = true
[[services]]binding = "API_SERVICE_REQUIRING_MTLS"service = "deployed-api-worker-expecting-mtls"remote = true
[ai]binding = "AI"remote = true
[[mtls_certificates]]binding = "MY_CLIENT_CERT_FETCHER"certificate_id = "<YOUR_UPLOADED_CERT_ID>"
While the hybrid approach allows many bindings to connect to remote resources, certain bindings are not supported for remote connections during local development (remote: true
). These will always use local simulations or local values.
If remote: true
is specified in Wrangler configuration for any of the following unsupported binding types, Cloudflare will issue an error.
-
Durable Objects: Durable Objects have synchronous operational aspects in their API. The current hybrid development proxy mechanism primarily supports asynchronous operations. Enabling remote connections for Durable Objects may be supported in the future, but currently will always run locally.
-
Environment Variables (
vars
): Environment variables are intended to be distinct between local development and deployed environments (production
,preview
). They are easily configurable locally (such as in a.dev.vars
file or directly in Wrangler configuration. -
Secrets: Like environment variables, secrets are expected to have different values in local development versus deployed environments for security reasons. Use
.dev.vars
for local secret management. -
Assets (Static Assets) ↗: Static assets are always served from your local disk during development for speed and direct feedback on changes.
-
Version Metadata: Since your Worker code is running locally, version metadata (like commit hash, version tags) associated with a specific deployed version is not applicable or accurate.
-
Analytics Engine: Local development sessions typically don't contribute data directly to production Analytics Engine.
-
Hyperdrive: This is being actively worked on, but is currently unsupported.
-
Rate Limiting: Local development sessions typically should not share or affect rate limits of your deployed Workers. Rate limiting logic should be tested against local simulations.
-
Data modification: Operations (writes, deletes, updates) on bindings connected remotely will affect your actual data in the targeted Cloudflare resource (be it preview or production).
-
Billing: Interactions with remote Cloudflare services through these connections will incur standard operational costs for those services (such as KV operations, R2 storage/operations, AI requests, D1 usage).
-
Network latency: Expect network latency for operations on these remotely connected bindings, as they involve communication over the internet.
-
Environment Variables & Secrets: Standard environment variables (from [vars]) and Secrets always use their locally defined values during local development. The
remote: true
flag does not apply to them.
TO-DO
Separate from Miniflare-powered local development, Wrangler also offers a fully remote development mode via wrangler dev --remote
. Remote development is not supported in the Vite plugin.
npx wrangler dev --remote
yarn wrangler dev --remote
pnpm wrangler dev --remote
The wrangler dev --remote
command creates a temporary preview deployment on Cloudflare's infrastructure.
When you run wrangler dev --remote
:
- All of your code is uploaded to a temporary preview environment on Cloudflare's infrastructure.
- Changes to your code are automatically uploaded as you save.
- All requests and execution happen on Cloudflare's global network.
- The preview automatically terminates when you exit the command.
- For most development tasks, the most efficient and productive experience will be local development along with connections to remote resources.
- You may want to use
wrangler dev --remote
for testing features or behaviors that are highly specific to Cloudflare's network and cannot be adequately simulated locally or tested via hybrid connections.
- Iteration is significantly slower than local development due to the upload/deployment step for each change.
To protect production data, you can specify preview resources in your Wrangler configuration, such as:
- Preview namespaces for KV stores:
preview_id
.- This option is required when using
wrangler dev --remote
.
- This option is required when using
- Preview buckets for R2 storage:
preview_bucket_name
. - Preview database IDs for D1:
preview_database_id
This separation ensures your development activities don't impact production data while still providing a realistic testing environment.
- When you run a remote development session using the
--remote
flag, a limit of 50 routes per zone is enforced. Learn more in Workers platform limits.
Was this helpful?
- Resources
- API
- New to Cloudflare?
- Products
- Sponsorships
- Open Source
- Support
- Help Center
- System Status
- Compliance
- GDPR
- Company
- cloudflare.com
- Our team
- Careers
- 2025 Cloudflare, Inc.
- Privacy Policy
- Terms of Use
- Report Security Issues
- Trademark