Local development (5)
Last updated Oct 22, 2026
You can build, run, and test your Worker code on your own local machine before deploying it to Cloudflare's network. This is made possible through Miniflare, a simulator that executes your Worker code in a local workerd ↗︎ process. workerd also powers the hosted Workers platform.
By default, your Worker's bindings connect to locally simulated resources, but can be configured to interact with the real, production resource with remote bindings.
Security considerations
Miniflare is not a hardened sandbox
Do not use Miniflare to run untrusted or potentially malicious Worker code directly on your machine. Run the entire development process inside an appropriate secure sandbox, such as a virtual machine.
Miniflare starts a local workerd ↗︎ process. This warning applies to default local execution with wrangler dev, vite dev, vite preview, direct Miniflare use, and Miniflare-based testing tools. Setting remote: true on a binding changes which resource your code accesses, but your Worker code still executes locally. By contrast, legacy wrangler dev --remote executes your Worker code on Cloudflare.
Miniflare is not a security boundary for untrusted code. Although workerd attempts to restrict Worker code to configured resources, an implementation bug could allow code to escape those restrictions. The hosted Workers platform applies additional layers of defense in depth that Miniflare does not provide locally.
Core concepts
Worker execution vs Bindings
When developing Workers, it's important to understand two distinct concepts:
- Worker execution: Where your Worker code actually runs (on your local machine vs on Cloudflare's infrastructure).
- Bindings: How your Worker interacts with Cloudflare resources (like KV namespaces, R2 buckets, D1 databases, Queues, Durable Objects, etc). In your Worker code, these are accessed via the
envobject (such asenv.MY_KV).
Start a local development server
You can start a local development server using:
- The Cloudflare Workers CLI Wrangler, using the built-in
wrangler devcommand.
npmyarnpnpm
npx wrangler dev
yarn wrangler dev
pnpm wrangler dev
- Vite ↗︎, using the Cloudflare Vite plugin.
npmyarnpnpm
npx vite dev
yarn vite dev
pnpm vite dev
Both Wrangler and the Cloudflare Vite plugin use Miniflare under the hood, and are developed and maintained by the Cloudflare team. For guidance on choosing when to use Wrangler versus Vite, see our guide Choosing between Wrangler & Vite.
Defaults
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.
- The local
workerdruntime runs withTZ=UTCso thatDateandIntlAPIs inside your Worker observe UTC, matching the production Cloudflare runtime regardless of your machine's timezone.
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. 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 with remote bindings. This lets you connect to real, production resources while still running your Worker code locally.
- When using
wrangler dev, you can temporarily disable all remote bindings (and connect only to local resources) by providing the--localflag (i.e.wrangler dev --local)
Remote bindings
Remote bindings are bindings that are configured to connect to the deployed, remote resource during local development instead of the locally simulated resource. Remote bindings are supported by Wrangler, the Cloudflare Vite plugin, and the @cloudflare/vitest-plugin package. You can configure remote bindings by setting remote: true in the binding definition.
Example configuration
{
"name": "my-worker",
// Set this to today's date
"compatibility_date": "2026-10-27",
"r2_buckets": [
{
"bucket_name": "screenshots-bucket",
"binding": "screenshots_bucket",
"remote": true,
},
],
}
name = "my-worker"
# Set this to today's date
compatibility_date = "2026-10-27"
[[r2_buckets]]
bucket_name = "screenshots-bucket"
binding = "screenshots_bucket"
remote = true
When remote bindings are configured, your Worker still executes locally, only the underlying resources your bindings connect to change. For all bindings marked with remote: true, Miniflare will route its operations (such as env.MY_KV.put()) to the deployed resource. All other bindings not explicitly configured with remote: true continue to use their default local simulations.
Integration with environments
Remote Bindings work well together with Workers Environments. To protect production data, you can create a development or staging environment and specify different resources in your Wrangler configuration than you would use for production.
For example:
{
"name": "my-worker",
// Set this to today's date
"compatibility_date": "2026-10-27",
"env": {
"production": {
"r2_buckets": [
{
"bucket_name": "screenshots-bucket",
"binding": "screenshots_bucket",
},
],
},
"staging": {
"r2_buckets": [
{
"bucket_name": "preview-screenshots-bucket",
"binding": "screenshots_bucket",
"remote": true,
},
],
},
},
}
name = "my-worker"
# Set this to today's date
compatibility_date = "2026-10-27"
[[env.production.r2_buckets]]
bucket_name = "screenshots-bucket"
binding = "screenshots_bucket"
[[env.staging.r2_buckets]]
bucket_name = "preview-screenshots-bucket"
binding = "screenshots_bucket"
remote = true
Running wrangler dev -e staging (or CLOUDFLARE_ENV=staging vite dev) with the above configuration means that:
- Your Worker code runs locally
- All calls made to
env.screenshots_bucketwill use thepreview-screenshots-bucketresource, rather than the productionscreenshots-bucket.
Recommended remote bindings
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 Run:
To interact with a real headless browser for rendering. There is no current local simulation for Browser Run.
{
"browser": {
"binding": "MY_BROWSER",
"remote": true
},
}
[browser]
binding = "MY_BROWSER"
remote = true
Workers AI:
To utilize actual AI models deployed on Cloudflare's network for inference. There is no current local simulation for Workers AI.
{
"ai": {
"binding": "AI",
"remote": true
},
}
[ai]
binding = "AI"
remote = true
Vectorize:
To connect to your production Vectorize indexes for accurate vector search and similarity operations. There is no current local simulation for Vectorize.
{
"vectorize": [
{
"binding": "MY_VECTORIZE_INDEX",
"index_name": "my-prod-index",
"remote": true
}
],
}
[[vectorize]]
binding = "MY_VECTORIZE_INDEX"
index_name = "my-prod-index"
remote = true
mTLS:
To verify that the certificate exchange and validation process work as expected. There is no current local simulation for mTLS bindings.
{
"mtls_certificates": [
{
"binding": "MY_CLIENT_CERT_FETCHER",
"certificate_id": "<YOUR_UPLOADED_CERT_ID>",
"remote": true
}
]
}
[[mtls_certificates]]
binding = "MY_CLIENT_CERT_FETCHER"
certificate_id = "<YOUR_UPLOADED_CERT_ID>"
remote = true
Images:
To connect to a high-fidelity version of the Images API, and verify that all transformations work as expected. Local simulation for Cloudflare Images is limited with only a subset of features.
{
"images": {
"binding": "IMAGES" ,
"remote": true
}
}
[images]
binding = "IMAGES"
remote = true
Note
If remote: true is not specified for Browser Run, Vectorize, mTLS, or Images, Cloudflare will issue a warning. This prompts you to consider enabling it for a more production-like testing experience.
If a Workers AI binding has remote 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.
Dispatch Namespaces:
Workers for Platforms users can configure remote: true in dispatch namespace binding definitions:
{
"dispatch_namespaces": [
{
"binding": "DISPATCH_NAMESPACE",
"namespace": "testing",
"remote":true
}
]
}
[[dispatch_namespaces]]
binding = "DISPATCH_NAMESPACE"
namespace = "testing"
remote = true
This allows you to run your dynamic dispatch Worker locally, while connecting it to your remote dispatch namespace binding. This allows you to test changes to your core dispatching logic against real, deployed user Workers.
Unsupported remote bindings
Certain bindings are not supported for remote connections (i.e. with remote: true) during local development. 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. See all supported and unsupported bindings for remote bindings.
- Durable Objects: Enabling remote connections for Durable Objects may be supported in the future, but currently will always run locally. However, using Durable Objects in combination with remote bindings is possible. Refer to Using remote resources with Durable Objects and Workflows below.
- Workflows: Enabling remote connections for Workflows may be supported in the future, but currently will only run locally. However, using Workflows in combination with remote bindings is possible. Refer to Using remote resources with Durable Objects and Workflows below.
- Environment Variables (
vars): Environment variables are intended to be distinct between local development and deployed environments. They are easily configurable locally (such as in a.dev.varsfile 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.varsfor local secret management. - 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.
