Skip to content
Cloudflare Docs

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.

Start a development server

You can start a local development server using:

  1. Our official CLI Wrangler, using the built-in wrangler dev command.
Terminal window
npx wrangler dev
  1. Vite, using the Cloudflare Vite plugin.
Terminal window
npx 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.

Default Behavior (Fully local)

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 during local development

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.

Connect to remote resources (Hybrid)

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,
},
],
}

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.

How it works

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 as env.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.

Targeting Preview vs. Production resources

To protect production data, you can create and specify preview resources in your Wrangler configuration, such as:

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,
},
],
}

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 the preview-screenshots-bucket resource, rather than the production screenshots-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 an mtls_certificates binding to make the call.

Behavior and configuration:

  • 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 to false, 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
}

Limitations - Unsupported remote bindings

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.

Important Considerations

  • 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.

API

TO-DO

Remote development

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.

Terminal window
npx wrangler dev --remote

How It Works

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.

When to use Remote development

  • 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.

Considerations

  • Iteration is significantly slower than local development due to the upload/deployment step for each change.

Isolating from Production

To protect production data, you can specify preview resources in your Wrangler configuration, such as:

This separation ensures your development activities don't impact production data while still providing a realistic testing environment.

Limitations

  • 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.