Edge functions used to be a niche choice. In October 2026 they are closer to default infrastructure for anyone shipping an API that needs to respond fast, worldwide, without managing a server fleet. Cloudflare Workers sit at the center of that shift, and the platform keeps moving: new Web Crypto algorithms, production-grade profiling tools, and tighter Durable Objects behavior all landed in the past few weeks alone.

This tutorial walks through building and securing a real Cloudflare Workers API from a blank terminal to a deployed, monitored endpoint. You will scaffold a project with Wrangler, add request validation and rate limiting, persist data in Workers KV, hold state in a Durable Object, lock down secrets and access, harden the edge with WAF rules, and wire up deployment and monitoring. Along the way you get the current 2026 pricing numbers, a head-to-head against AWS Lambda and Fastly Compute, and a troubleshooting list built from the errors developers actually hit.

Why Cloudflare Workers Are Worth Learning Right Now

Cloudflare Workers run your code in data centers distributed across Cloudflare’s global network rather than in a single region, which cuts the distance between your API and your users. The free plan allows 100,000 requests per day with a 10 millisecond CPU time budget per invocation, according to Cloudflare’s own Workers documentation. The Workers Paid plan starts at $5 per month and bundles Workers, Pages Functions, Workers KV, Hyperdrive, and Durable Objects usage into that single charge, with CPU time extended up to 5 minutes per invocation and no hard cap on monthly request volume.

The platform is not standing still. Cloudflare’s October 2026 changelog shows the Workers Web Crypto API now supports post-quantum algorithms including ML-KEM-768, ML-KEM-1024, ML-DSA-44, ML-DSA-65, and ML-DSA-87, letting teams start experimenting with quantum-resistant key exchange and signatures directly inside a Worker. On-demand CPU and memory profiling, including flamegraphs generated in production, also shipped this month. Cloudflare’s developer platform changelog separately tightened the disk-to-memory ratio available to custom compute instance types, a sign the company keeps iterating on resource limits well beyond Workers alone.

None of that matters if your Worker ships with a security hole or falls over under load, so this tutorial treats security and reliability as part of the build, not an afterthought bolted on at the end.

What do people actually build on Workers? Webhook receivers that need to answer in milliseconds, geolocation-based routing that picks a backend based on where a request originated, lightweight A/B testing logic that rewrites a response before it reaches the browser, and proxies in front of a model host that add auth, caching, or rate limiting without touching the model-serving code itself. The common thread is latency sensitivity combined with logic too small to justify a dedicated server. That is exactly the shape of the API this tutorial builds: a form-submission endpoint that has to validate, rate-limit, authenticate, and store data, all inside a budget measured in milliseconds rather than seconds.

Prerequisites: Tools, Accounts, and Versions

You do not need exotic tooling for this build, but a few pieces need to be in place before step one.

  • A free Cloudflare account (upgrade to the Workers Paid plan, $5/month minimum, before Step 7 if you want to use Durable Objects)
  • Node.js 18 or later installed locally, since Wrangler and the npm ecosystem require it
  • npm (bundled with Node.js) or an equivalent package manager
  • The latest version of Wrangler, Cloudflare’s CLI, installed via npm as shown in Step 1
  • A terminal and a code editor; VS Code works well with Cloudflare’s Workers extension, but any editor is fine
  • Basic familiarity with JavaScript or TypeScript, since the sample project uses JavaScript with optional TypeScript types

Budget around 60 to 90 minutes for the full walkthrough if you are typing along, less if you are skimming for a specific step.

Step 1: Create Your Cloudflare Account and Install Wrangler

Sign up for a Cloudflare account if you do not already have one, then install Wrangler globally or as a project dependency. Most tutorials install it as a dev dependency so every teammate gets the same version through package.json rather than whatever happens to be on their machine.

npm install -D wrangler
npx wrangler --version
npx wrangler login

The wrangler login command opens a browser window where you authorize the CLI against your Cloudflare account. Once authorized, Wrangler stores an OAuth token locally so subsequent commands do not prompt you again. If you are working in a CI environment instead of a laptop, skip this interactive login and use an API token through the CLOUDFLARE_API_TOKEN environment variable instead, which is what Step 11 relies on for automated deploys.

Step 2: Scaffold a New Worker Project

Cloudflare ships a scaffolding tool that generates a working Worker with sane defaults. Run it from an empty directory and answer the prompts for a plain JavaScript Worker (you can switch to TypeScript later if you prefer static types).

npm create cloudflare@latest -- secure-api-worker
cd secure-api-worker
npx wrangler dev

wrangler dev starts a local development server that emulates the Workers runtime on your machine, printing a local URL you can hit with curl or a browser. Changes to your source file hot-reload automatically, so you get a fast feedback loop before anything touches Cloudflare’s network. Keep this terminal open; you will use it throughout the next several steps.

Step 3: Set the Compatibility Date and Understand the Runtime

Every Worker project has a configuration file, either wrangler.toml or the newer wrangler.jsonc, and the single most consequential setting in it is the compatibility date. This date tells the Workers runtime which version of its own behavior to apply, which matters because Cloudflare ships runtime changes constantly and needs a way to avoid breaking existing Workers.

name = "secure-api-worker"
main = "src/index.js"
compatibility_date = "2026-10-01"
compatibility_flags = []

Set this to a recent date and you get useful defaults for free. For compatibility dates of 2026-08-04 or later, Cloudflare enables both nodejs_compat and nodejs_compat_v2 automatically, so built-in Node.js APIs and polyfills work without extra flags. Compatibility dates from 2024-09-23 through 2026-08-03 still need nodejs_compat added explicitly to the flags array. The 2026-10-01 compatibility date also changes Durable Object behavior: Durable Objects can keep handling work after a client disconnects by default, where earlier dates require an explicit durable_object_io_tasks_prevent_eviction flag to get the same guarantee. Pin a real date here rather than copying an old tutorial’s value verbatim.

Step 4: Write a Secure API Route With Input Validation

Replace the generated handler with a route that validates its input before doing anything else. Treating every field from the request body as untrusted is the single habit that prevents the largest share of API vulnerabilities, a point OWASP’s API Security Top 10 has made a central theme across every edition it has published.

export default {
  async fetch(request, env, ctx) {
    if (request.method !== "POST") {
      return new Response("Method Not Allowed", { status: 405 });
    }

    let body;
    try {
      body = await request.json();
    } catch (err) {
      return Response.json({ error: "Invalid JSON body" }, { status: 400 });
    }

    const { email, message } = body;
    if (typeof email !== "string" || !email.includes("@")) {
      return Response.json({ error: "A valid email is required" }, { status: 400 });
    }
    if (typeof message !== "string" || message.length === 0 || message.length > 2000) {
      return Response.json({ error: "Message must be 1-2000 characters" }, { status: 400 });
    }

    return Response.json({ status: "accepted" }, { status: 202 });
  },
};

Test it from a second terminal while wrangler dev keeps running:

curl -X POST http://localhost:8787 \
  -H "Content-Type: application/json" \
  -d '{"email":"[email protected]","message":"hello"}'

# {"status":"accepted"}

Notice that malformed JSON and missing fields both return structured error responses with explicit status codes, instead of letting an uncaught exception produce a generic error page. Workers that throw unhandled exceptions return a bare error to the client and make debugging in production harder than it needs to be.

Step 5: Add Rate Limiting and Abuse Protection

An unauthenticated POST endpoint without rate limiting is an open invitation for abuse, whether that is credential stuffing, scraping, or someone’s misconfigured retry loop. A simple approach uses Workers KV to track request counts per client IP inside a time window.

async function checkRateLimit(env, ip) {
  const key = `rl:${ip}`;
  const current = await env.RATE_LIMIT_KV.get(key);
  const count = current ? parseInt(current, 10) : 0;

  if (count >= 20) {
    return false;
  }

  await env.RATE_LIMIT_KV.put(key, String(count + 1), { expirationTtl: 60 });
  return true;
}

Call this at the top of your fetch handler, using request.headers.get("CF-Connecting-IP") to identify the client, and return a 429 status when the limit is exceeded. This approach works well for moderate traffic, but remember that Workers KV is eventually consistent rather than strongly consistent, so under very high concurrency a small number of requests can slip past the exact limit during propagation delay. For hard guarantees on abuse-sensitive endpoints, route the counting logic through a Durable Object instead, since each Durable Object instance processes requests one at a time and avoids the race entirely. Step 7 covers that pattern.

Step 6: Persist Data With Workers KV

Workers KV gives you a globally distributed key-value store without standing up a database. Create a namespace and bind it to your Worker through the configuration file.

npx wrangler kv namespace create "SUBMISSIONS"

Add the returned namespace ID to your config under a kv_namespaces binding, then read and write through env.SUBMISSIONS inside your handler. The free plan includes 100,000 KV reads per day, 1,000 writes, 1,000 deletes, and 1,000 list requests per day, plus 1 GB of storage, with daily allowances resetting at 00:00 UTC. The Paid plan raises that to 10 million reads, 1 million writes, and 1 million deletes per month, with overages billed at $0.50 per million reads and $5 per million writes or deletes, plus $0.50 per GB-month for storage beyond the included 1 GB. Those are real limits worth checking against your expected traffic before you commit to KV as your primary store rather than a caching layer in front of a proper database.

Step 7: Add Stateful Logic With a Durable Object

Durable Objects solve the problem Workers KV cannot: strongly consistent, single-threaded state. Each Durable Object instance is addressed by a unique ID and processes one request at a time, which makes it the right tool for counters, locks, WebSocket coordination, or exactly the rate limiter from Step 5 when you need it airtight.

export class RateLimiter {
  constructor(state, env) {
    this.state = state;
  }

  async fetch(request) {
    let count = (await this.state.storage.get("count")) || 0;
    count += 1;
    await this.state.storage.put("count", count);

    const allowed = count <= 20;
    return Response.json({ allowed, count });
  }
}

Durable Objects are included in the Workers Paid plan, which carries that same $5 per month minimum charge, so this step assumes you upgraded during Prerequisites. With a compatibility date of 2026-10-01 or later, a Durable Object you called can keep running cleanup or write-through work even after the original caller's connection closes, which previously required the explicit eviction-prevention flag mentioned back in Step 3. That matters for anything that needs to finish a database write or external API call that outlives the client's patience.

Step 8: Manage Secrets the Right Way

Never put an API key, database password, or signing secret directly in wrangler.toml or anywhere else that gets committed to git. Wrangler has a dedicated secrets mechanism that encrypts values at rest and injects them into your Worker's environment without ever writing them to a file in your repository.

npx wrangler secret put TURNSTILE_SECRET_KEY
# Wrangler prompts for the value, then stores it encrypted

Inside your handler, the secret is available as env.TURNSTILE_SECRET_KEY, identical to how you would read a regular environment variable. One easy mistake: setting a secret does not automatically redeploy a Worker that is already live, so run wrangler deploy again after adding or rotating a secret to make sure the running version actually picks it up.

Step 9: Lock Down Access With Turnstile and API Keys

Public-facing endpoints that accept form submissions benefit from Cloudflare Turnstile, a CAPTCHA alternative that verifies a request came from a real browser without forcing users through a puzzle. Verify the token server-side, inside the Worker, before processing anything the client sent.

async function verifyTurnstile(token, secret, ip) {
  const resp = await fetch("https://challenges.cloudflare.com/turnstile/v0/siteverify", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ secret, response: token, remoteip: ip }),
  });
  const data = await resp.json();
  return data.success === true;
}

For server-to-server traffic where a CAPTCHA makes no sense, issue API keys instead and check them against a KV lookup or a Durable Object, rejecting with a 401 before any validation or business logic runs. Doing the authentication check first, ahead of parsing or touching storage, keeps unauthenticated requests cheap to reject and avoids wasting CPU time budget on traffic you were always going to refuse.

Step 10: Harden the Edge With WAF and Zero Trust Rules

Application-level checks inside your Worker code are one layer. Cloudflare's dashboard gives you a second layer that runs before your Worker even executes. Under Security in the Cloudflare dashboard for your zone, configure a WAF custom rule that blocks or challenges requests matching patterns you know are abusive, such as unusually large request bodies on your API route or traffic from ASNs you do not serve.

Rate limiting rules configured at the zone level complement the KV-based limiter from Step 5 by stopping obviously automated traffic before it reaches your code at all, which saves CPU time and request quota. If this Worker is internal, consider putting it behind Cloudflare Access instead of exposing it publicly: Access integrates with your identity provider and blocks requests that are not authenticated through your organization's SSO, closing off the entire endpoint to the open internet rather than relying solely on in-code checks.

Treat these dashboard-level controls as the outer wall and the in-code checks from Steps 4, 5, and 9 as the inner wall. Neither one alone is a complete answer: a WAF rule tuned too loosely lets bad traffic through to your handler, and application code alone cannot stop a volumetric flood before it consumes your request quota. Running both means a mistake in one layer does not translate directly into a breach.

Step 11: Deploy With Wrangler and Workers Builds

Manual deploys work fine for solo projects. For anything a team touches, connect the repository to Workers Builds so every push to your main branch deploys automatically.

npx wrangler deploy

# Output:
# Total Upload: 12.4 KiB / gzip: 4.8 KiB
# Uploaded secure-api-worker (1.2 sec)
# Published secure-api-worker (0.4 sec)
#   https://secure-api-worker.your-subdomain.workers.dev
# Current Deployment ID: a1b2c3d4-...

Workers Builds on the free plan gives you 3,000 build minutes per month with one concurrent build; the Paid plan raises that to 6,000 build minutes per month, 6 concurrent builds, and bills overage at $0.005 per minute, while both tiers cap an individual build at 20 minutes. Deploy Hooks, which let external systems trigger a build via webhook, are limited to 10 per minute per Worker and 100 per minute per account. If your build regularly approaches the 20-minute ceiling, trim dependencies or move heavy compilation steps out of the Workers Builds pipeline and into a separate CI job that produces an artifact Wrangler simply uploads.

Step 12: Monitor, Profile, and Debug in Production

A deployed Worker is not a finished Worker. Stream live logs from the edge with Wrangler's tail command to watch real requests as they happen.

npx wrangler tail secure-api-worker

# Example output:
# POST https://secure-api-worker.your-subdomain.workers.dev/ - Ok @ 10/11/2026, 3:42:11 PM
#   (log) rate limit check passed for 203.0.113.42
#   (log) turnstile verification: success

Reading Flamegraphs in Production

Cloudflare shipped on-demand CPU and memory profiling for Workers and Durable Objects this month, generating interactive flamegraphs directly against live traffic. Pull one up from the dashboard when a Worker approaches its CPU time budget, rather than guessing which function is expensive from reading source code alone, and you get a direct view of where milliseconds actually go.

Tracing RPC Calls Across Workers

If your architecture splits work across multiple Workers calling each other over JavaScript RPC, Cloudflare's tracing now follows those calls across Worker boundaries and into Durable Objects, so a slow request shows its full path instead of stopping at the first hop.

Testing Your Worker Before It Reaches Production

Everything above gets tested manually through wrangler dev and curl, which is fine for a tutorial but not enough for a Worker a team depends on. Cloudflare publishes a Vitest integration built specifically for Workers that runs your tests against the actual Workers runtime instead of a Node.js approximation of it, so behavior that depends on edge-specific APIs gets exercised honestly rather than mocked away. Add it as a dev dependency alongside Wrangler and point your test files at the same fetch handler you deployed, passing in constructed Request objects and asserting on the Response that comes back.

Write at least one test per validation branch in Step 4 (missing email, malformed JSON, oversized message), one for the rate limiter tripping after the twentieth request, and one for a Turnstile failure returning a 401 rather than silently passing the request through. Wire the test command into the same CI pipeline that runs Workers Builds in Step 11, and fail the build if tests fail, rather than finding out a validation branch broke after the bad deploy is already live on your workers.dev subdomain.

The Complete Project: Putting All 12 Steps Together

Stack the twelve steps and you end up with a Worker that validates every incoming request, rate-limits by IP through KV or a Durable Object, stores submissions in KV, verifies Turnstile tokens before processing, reads secrets through Wrangler's encrypted store instead of plaintext config, sits behind zone-level WAF rules, and deploys automatically on every push with live tailing and production flamegraphs available the moment something looks slow. That is a genuinely production-shaped API, not a toy demo, and every piece of it runs on Cloudflare's free or $5/month Paid plan depending on whether you need Durable Objects.

Wiring the pieces from Steps 4, 5, 8, and 9 into a single handler looks like this, with the KV-based rate limiter swapped in ahead of validation so abusive traffic never reaches the parsing or storage logic at all:

export default {
  async fetch(request, env, ctx) {
    const ip = request.headers.get("CF-Connecting-IP") || "unknown";

    if (!(await checkRateLimit(env, ip))) {
      return Response.json({ error: "Too many requests" }, { status: 429 });
    }
    if (request.method !== "POST") {
      return new Response("Method Not Allowed", { status: 405 });
    }

    let body;
    try {
      body = await request.json();
    } catch (err) {
      return Response.json({ error: "Invalid JSON body" }, { status: 400 });
    }

    const { email, message, turnstileToken } = body;
    const valid = await verifyTurnstile(turnstileToken, env.TURNSTILE_SECRET_KEY, ip);
    if (!valid) {
      return Response.json({ error: "Turnstile verification failed" }, { status: 401 });
    }
    if (typeof email !== "string" || !email.includes("@")) {
      return Response.json({ error: "A valid email is required" }, { status: 400 });
    }

    await env.SUBMISSIONS.put(`sub:${Date.now()}`, JSON.stringify({ email, message }));
    return Response.json({ status: "accepted" }, { status: 202 });
  },
};

From here, the natural next additions are a proper database through Hyperdrive if you outgrow KV's key-value model, and Workers AI if your API needs inference at the edge rather than a round trip to a separate model host.

Cloudflare Workers Pricing: Free vs. Paid Plan

Pricing decisions should come from the actual limits, not guesswork. Here is what Cloudflare currently publishes for Workers, KV, and Durable Objects.

ResourceFree PlanPaid Plan ($5/mo minimum)
Requests100,000/dayNo hard limit; 10M/month included, then $0.30/million
CPU time per invocation10 ms30 sec default, up to 5 min configurable
Memory128 MB128 MB
Max script size64 MiB64 MiB
KV reads100,000/day10M/month, then $0.50/million
KV writes/deletes1,000/day each1M/month each, then $5/million
KV storage1 GB1 GB included, then $0.50/GB-month
Durable ObjectsNot includedIncluded in Paid plan

Most small APIs never leave the free tier until they add Durable Objects, since 100,000 requests a day comfortably covers early traffic for most side projects and internal tools. The moment you need Durable Objects, you are on the Paid plan regardless of volume, which is a reasonable trade given that $5 also unlocks the longer CPU time window and higher KV allowances in the same package.

Cloudflare Workers vs. AWS Lambda vs. Fastly Compute

Workers is not the only edge or serverless option, and picking it blind is a bad habit. AWS Lambda remains the default for teams already standardized on AWS, and it just extended its own ceiling: as of a September 9, 2026 announcement, Lambda Managed Instances support a 90-minute timeout for asynchronous and event source mapping invocations, a 6x increase over the long-standing 15-minute cap, though synchronous invocations and initialization still max out at 15 minutes. AWS has also focused heavily on cold starts, documenting SnapStart and, more recently, work aimed specifically at eliminating Java cold starts on Managed Instances, since the Lambda execution environment lifecycle otherwise has to spin up a fresh environment per concurrent execution path. Independent cold-start research from lambdabench.dev breaks down exactly where that startup latency hides between a zip-based deployment and a container image, which is worth a read if Lambda cold starts are a recurring pain point for your workload.

Fastly Compute runs code compiled to WebAssembly rather than JavaScript directly, with official SDK support for Rust, JavaScript on Node.js 16.6 or later, and Go 1.21 or later, as described in Fastly's own introduction to Compute. Fastly's published default limits cap CPU time at 50 milliseconds per request execution, runtime at 2 minutes (60 seconds for trial accounts), and memory at 128 MB of heap plus a 1 MB stack. Unlike Workers, Fastly Compute offers a free trial rather than a standing free tier, and that trial cannot serve production traffic, so budgeting for Fastly means budgeting for its usage-based Compute pricing from day one rather than staying free indefinitely like a low-traffic Worker can.

PlatformFree TierMax Timeout / RuntimeCPU / Memory Limit
Cloudflare Workers100,000 req/day, ongoing5 min CPU (Paid), 10ms (Free)128 MB memory
AWS LambdaSeparate AWS Free Tier (not Workers-equivalent)15 min standard; 90 min on Managed Instances (async/ESM only)Up to 10,240 MB memory
Fastly ComputeTrial only, no production traffic2 min (60 sec on trial)128 MB heap + 1 MB stack

The practical takeaway: Workers wins on a free tier you can run indefinitely and the lowest-friction JavaScript developer experience. Lambda wins when your workload genuinely needs minutes of continuous execution and you are already inside the AWS ecosystem. Fastly wins when WebAssembly portability or Fastly's existing CDN relationship matters more than JavaScript-first ergonomics.

Common Pitfalls When Deploying Cloudflare Workers

  • Skipping the compatibility date. An old or missing compatibility_date silently disables newer runtime defaults like automatic Node.js compatibility, producing confusing "module not found" errors that have nothing to do with your actual code.
  • Treating KV as strongly consistent. Workers KV replicates globally with eventual consistency. Code that assumes a write is instantly visible everywhere, such as a naive rate limiter under heavy concurrent load, can let a handful of requests through that should have been blocked.
  • Committing secrets to wrangler.toml. Plaintext vars in your config file get committed to git the moment someone forgets. Use wrangler secret put for anything sensitive, every time, with no exceptions for "just testing."
  • Ignoring subrequest limits. The free plan allows 50 subrequests per incoming request; the Paid plan raises that to 10,000. A Worker that fans out to several third-party APIs per request can hit that ceiling faster than expected, especially with retries.
  • Enabling Durable Objects without the Paid plan. Durable Objects require the Workers Paid plan. Trying to deploy a Worker that uses them on the free tier fails at deploy time, not at runtime, which catches teams off guard mid-sprint.

Troubleshooting Cloudflare Workers Deployment Problems

Even a careful build hits friction. These are the issues that come up most often once a Worker is live.

  • Error 1101 (Worker threw exception): an unhandled error in your fetch handler. Wrap risky code in try/catch and run wrangler tail to see the actual stack trace from live traffic.
  • Error 1015 (You are being rate limited): you tripped Cloudflare's own abuse protection, separate from any rate limiting you built. Check the zone-level security settings from Step 10 if legitimate traffic is getting blocked.
  • "Exceeded CPU limit" on the free plan: a 10ms budget is tight. Profile with the flamegraph tool from Step 12, cut unnecessary JSON parsing or regex work, or upgrade to Paid for the much larger CPU window.
  • Stale reads from Workers KV: expected behavior given eventual consistency, not a bug. Move consistency-critical state into a Durable Object instead of fighting KV's replication model.
  • CORS errors calling the Worker from a browser: add explicit Access-Control-Allow-Origin and related headers in your response, and handle OPTIONS preflight requests separately from your main logic.
  • "Authentication error [code: 10000]" on deploy: your Wrangler session expired or your API token lacks the right scope. Run wrangler login again, or regenerate the token with Workers edit permissions.
  • Secrets not appearing in env after wrangler secret put: the running deployment has not picked up the change yet. Run wrangler deploy again; setting a secret does not trigger a redeploy automatically.
  • Workers Builds timing out near 20 minutes: trim dependencies, cache node_modules between builds where possible, or move heavyweight compilation into a separate pipeline that hands Wrangler a finished artifact.

Advanced Tips for Production-Grade Workers

Once the basics are solid, a few newer capabilities are worth adopting early. Experiment with the post-quantum ML-KEM and ML-DSA algorithms now available in the Workers Web Crypto API if you handle anything that needs a long shelf life of confidentiality, since migrating cryptographic primitives later under pressure is far more painful than adopting them now while the stakes are low. These algorithms exist specifically because classical key exchange and signature schemes are expected to become breakable once sufficiently capable quantum hardware exists, so data you encrypt today with a classical algorithm could still be exposed years from now even if nobody can break it today.

Use ctx.exports to call Workflows declared in your Wrangler configuration directly, skipping the separate workflows binding that earlier setups required. If you lean on Workers AI for inference inside a Worker, the new rejectIfBusy option on synchronous inference requests lets you fail fast and return a clear error when capacity is tight, instead of leaving a client hanging on a slow queued response, which matters a great deal for a user-facing feature where a fast failure beats a slow success. And if your Workflows retain state you depend on for auditing, note that instances created on or after September 10, 2026 keep completed and errored state for 7 days by default, down from the previous 30, so export anything you need to keep long-term rather than assuming Cloudflare will hold it for a month.

Frequently Asked Questions

Is Cloudflare Workers free to use?

Yes, for low-traffic use. The Free plan allows 100,000 requests per day with a 10ms CPU time budget per invocation and 128 MB of memory. Durable Objects are not available on the free plan; they require the $5/month Paid plan.

Does Cloudflare Workers support Node.js?

Partially, through a compatibility layer rather than a full Node.js runtime. For Workers with a compatibility date of 2026-08-04 or later, both nodejs_compat and nodejs_compat_v2 are enabled by default, exposing built-in Node.js APIs and polyfills without extra configuration.

What is the difference between Workers KV and Durable Objects?

Workers KV is a globally distributed, eventually consistent key-value store, good for caching and data that tolerates brief staleness. Durable Objects provide strongly consistent, single-threaded state tied to a unique object ID, suited to counters, coordination, and anything that cannot tolerate a race condition.

How do Cloudflare Workers compare to AWS Lambda for long-running tasks?

Lambda currently supports longer single-invocation execution: 15 minutes standard, or up to 90 minutes on Lambda Managed Instances for asynchronous and eligible event source mapping invocations as of the September 2026 announcement. Workers Paid plan tops out at 5 minutes of CPU time per invocation, making Lambda the better fit for genuinely long batch-style jobs.

Can I use TypeScript with Cloudflare Workers?

Yes. The scaffolding tool in Step 2 offers a TypeScript template, and Wrangler compiles TypeScript automatically during both local development and deployment without any extra build configuration.

How do I secure a Cloudflare Worker that accepts public form submissions?

Layer defenses rather than relying on one check: validate and sanitize all input server-side, verify a Turnstile token before processing, rate-limit by IP through KV or a Durable Object, and add a zone-level WAF rule for obviously abusive patterns, as covered in Steps 4, 5, 9, and 10.

What happens if I exceed the Workers KV free tier limits?

Requests beyond the free daily allowance of 100,000 reads or 1,000 writes/deletes fail rather than silently queueing. Upgrade to the Paid plan for 10 million reads and 1 million writes/deletes per month included, with metered overage billing beyond that.

Do I need the Workers Paid plan to test Durable Objects locally?

No. Local development through wrangler dev emulates Durable Objects on your machine without touching your Cloudflare account's plan. The Paid plan requirement only kicks in when you actually deploy a Worker that uses Durable Objects to Cloudflare's network, which is why the deploy-time failure described in the pitfalls section catches people who tested everything locally first.

How many requests can a single Cloudflare account handle before needing an enterprise plan?

There is no hard request ceiling on the standard Paid plan; it includes 10 million requests per month and bills additional volume at $0.30 per million. Enterprise agreements exist for organizations that want custom contractual terms, dedicated support, or negotiated rates at very high volume, but the standard Paid plan scales well past what most teams building a single API will ever need.