wrangler kv/r2 (v4) targets the local simulator unless --remote

Wrangler 4 runs kv key and r2 object commands against local dev storage by default; pass --remote or your production write never happens.

KV/R2/D1

· Chapter

6

·

2

min read

The answer. Since Wrangler 4, wrangler kv key put|get|delete and wrangler r2 object put|get|delete run against the local Miniflare storage under .wrangler/state unless you pass --remote. The command succeeds, prints its success line, and touches nothing in production. So the JWKS cache you "flushed" is still serving the old keys, and the object you "uploaded" exists only on your laptop. Pass --remote for every production data operation, then read the value back — also with --remote — before you believe it.

The pattern.

# Wrangler 4: these hit .wrangler/state (local) unless told otherwise
wrangler kv key delete --binding MY_KV "jwks:idp:v1" --env prod            # local!
wrangler kv key delete --binding MY_KV "jwks:idp:v1" --env prod --remote   # prod

# Verify against the same target you wrote to
wrangler kv key get --binding MY_KV "jwks:idp:v1" --env prod --remote
# -> "Value not found" is the success you want after a flush

# Same rule for R2 objects
wrangler r2 object put my-media/covers/hero.webp --file ./hero.webp --remote

The receipt. HarperFlow's API Worker validates user JWTs against the identity provider's JWKS, cached in KV. When we moved from self-hosted to hosted Supabase (cutover 2026-07-10) the new project signed with different keys, so the cached JWKS had to go. The cutover log's note is emphatic: the flush only counted with --remote, "wrangler-4 defaults to LOCAL sim". Proof was a freshly minted hosted session hitting /me and getting 200. The same rule came back in August 2026 when seeding the R2 media bucket for the care-package feature: r2 object put needed --remote too. We didn't measure how long a stale JWKS would have lived; the read-back caught it.

Watch out.

  • wrangler dev reads the same local store, so a value you put without --remote shows up there and makes the bug look fixed.
  • --env prod selects which namespace id to use; it does not choose local versus remote. The two flags are orthogonal.
  • Secrets are the opposite case: wrangler secret put is always remote.

Related: r2-custom-domain-choose-once · cache-api-poisoned-by-transient-error