Stream request and response bodies
Regardless of memory limits, streaming large requests and responses is a best practice. It reduces peak memory usage and improves time-to-first-byte. Workers have a 128 MB memory limit, so buffering an entire body with await response.text() or await request.arrayBuffer() will crash your Worker on large payloads. For request bodies you consume entirely (JSON payloads, file uploads), enforce a maximum size before reading. Use TransformStream to pipe from a source to a destination without holding it all in memory.
Use waitUntil for work after response
ctx.waitUntil() lets you perform work after the response is sent to the client, such as analytics, cache writes, logging, or webhook notifications. This keeps your response fast while completing background tasks. Use ctx.waitUntil() only for work that does not affect the response. If the response depends on the work, await it before returning or stream the response as the work completes. A Worker still streaming a response body remains active without ctx.waitUntil(). Two common pitfalls: destructuring ctx loses the this binding and throws Illegal invocation at runtime; exceeding the 30-second waitUntil() time limit after the response is sent or the client disconnects.
Example: Stream multiple responses with TransformStream
This example shows how to concatenate multiple responses by piping each response body sequentially into a single writable stream without buffering:
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const urls = [
"https://api.example.com/part-1",
"https://api.example.com/part-2",
"https://api.example.com/part-3",
];
const { readable, writable } = new TransformStream();
const pipeline = (async () => {
for (const url of urls) {
const response = await fetch(url);
if (response.body) {
await response.body.pipeTo(writable, {
preventClose: true,
});
}
}
await writable.close();
})();
return new Response(readable, {
headers: { "Content-Type": "application/octet-stream" },
});
},
} satisfies ExportedHandler<Env>;
Deploy Hook idempotency behavior
If the same Deploy Hook is triggered again before the previous build has fully started, Workers Builds does not create a duplicate build. The first request creates a build. If a second request arrives while that build is still queued or initializing, no second build is created and the response returns the existing build_uuid with already_exists set to true. Once the earlier build moves past initializing, a later POST creates a new build as normal.
Deploy Hook response structure
The Deploy Hook response includes: success (boolean), errors (array), messages (array), and result object containing build_uuid (string), branch (string), and worker (string). When an existing pending build is returned due to idempotency, the result also includes status (string) and created_on (ISO timestamp), along with already_exists set to true.
Deploy Hook build_uuid usage
The build_uuid returned in the Deploy Hook response can be used to monitor build status and retrieve logs via the Builds API.
Worker fetch handler routes MCP and agent requests
The worker fetch handler checks if the URL pathname starts with /mcp and calls createMcpHandler(server) to handle MCP protocol requests. For other requests, call routeAgentRequest() to handle agent communication, or return 404 for non-matching paths.
Accessing cf object in Python Worker
In a Python Worker, access the cf object via request.cf. Check if request.cf is not None before using it. The cf object may not be available in preview environments.
Accessing cf object in Hono framework
In a Hono Worker, access the underlying raw request via c.req.raw to get the cf object. The cf object is available on the raw request: const req = c.req.raw; const data = req.cf !== undefined ? req.cf : { error: 'The cf object is not available inside the preview.' };
Accessing cf object in JavaScript handler
In a JavaScript Worker, access the cf object via req.cf and check if it is defined. Example: const data = req.cf !== undefined ? req.cf : { error: 'The cf object is not available inside the preview.' };
req.cf object contains Cloudflare request data
The req.cf object contains custom Cloudflare properties and allows control over how Cloudflare features are applied to requests. It is available in the request object passed to the fetch handler.
Accessing cf object in TypeScript handler
In a TypeScript Worker with ExportedHandler, access the cf object via req.cf and check if it is defined. The function signature should be async fetch(req): Promise<Response>.
Response immutability in A/B testing
When setting cookies on a response in a Worker, the response object must be reconstructed to avoid immutability issues. This is done by creating a new Response object: `res = new Response(res.body, res)`. This is necessary before appending headers like Set-Cookie to ensure the response is mutable.
Header manipulation methods: append, delete, set
Response headers support three modification methods. The append() method adds a header with a value, even if the header already exists (creates multiple values). The delete() method removes a header entirely. The set() method changes an existing header's value or creates it if it doesn't exist.
Response headers must be cloned before modification
Response objects returned from fetch are immutable. To modify response headers, you must clone the response into a new Response object using new Response(response.body, response). After cloning, you can use headers.append(), headers.delete(), and headers.set() to modify the response headers.
WWW-Authenticate response header prompts for credentials
Returning a 401 response with the WWW-Authenticate header set to 'Basic realm="my scope", charset="UTF-8"' will prompt the browser to display a login dialog asking the user for credentials.
Authorization header format for Basic Authentication
The Authorization header for Basic Authentication must start with the scheme 'Basic' followed by a space and then the base64-encoded credentials. The encoded credentials should be in the format 'username:password'. The username and password are split by the first colon.
Access TLS version from request object
The TLS version of an incoming request can be accessed via request.cf.tlsVersion. This property contains a string value such as 'TLSv1.2' or 'TLSv1.3'. In Hono frameworks, access it via c.req.raw.cf.tlsVersion. Note that request.cf does not exist in the previewer; it only works in production.
request.cf.asn - get Autonomous System Number
The request.cf.asn property contains the Autonomous System Number of the client. Example: if (request.cf && request.cf.asn == 64512) { return new Response("Block the ASN 64512 response"); }
CF-Connecting-IP header - get client IP address
The CF-Connecting-IP header contains the client's IP address. Example: const clientIP = request.headers.get("CF-Connecting-IP");
CF-Device-Type header - device type detection
The CF-Device-Type header indicates the client device type (e.g. "mobile"). This header requires either an Enterprise "CF-Device-Type Header" zone setting or a Page Rule with "Cache By Device Type" setting applied.
request.cf.country provides visitor geolocation
The request.cf object contains geolocation data about the request. The country property returns the country code of the visitor making the request, which can be used for geolocation-based routing and redirects.
Hono framework with Cron Trigger example
import { Hono } from "hono";
interface Env {}
const app = new Hono<{ Bindings: Env }>();
app.get("/", (c) => c.text("Hello World!"));
export default {
fetch: app.fetch,
async scheduled(
controller: ScheduledController,
env: Env,
ctx: ExecutionContext,
) {
console.log("cron processed");
},
};
TypeScript Cron Trigger example
interface Env {}
export default {
async scheduled(
controller: ScheduledController,
env: Env,
ctx: ExecutionContext,
) {
console.log("cron processed");
},
};
Python Cron Trigger example
from workers import WorkerEntrypoint, Response
class Default(WorkerEntrypoint):
async def scheduled(self, controller, env, ctx):
print("cron processed")
JavaScript Cron Trigger example
export default {
async scheduled(controller, env, ctx) {
console.log("cron processed");
},
};
Cron Trigger scheduled handler signature
A Worker exports a scheduled function with the signature: async scheduled(controller: ScheduledController, env: Env, ctx: ExecutionContext). The controller parameter is of type ScheduledController, env contains bindings, and ctx is ExecutionContext.
Workers request handler signature
A Worker handler exports a default object with an async fetch method that receives a Request object and returns a Promise<Response>.
Check content-type header to detect JSON responses
Before parsing a response as JSON, check if the content-type header includes "application/json". If it does, call response.json() to parse it; otherwise, call response.text() to get the raw text. This prevents errors when attempting to parse non-JSON content as JSON.
Access geolocation timezone in request.cf
The Cloudflare request object includes geolocation data via request.cf.timezone. This property contains the user's timezone string (e.g., 'UTC', 'America/New_York') and can be used to localize content or timestamps based on the user's location.
Accessing geolocation data from request object
Geolocation data is available on the request object via the cf property. The latitude and longitude can be accessed as request.cf.latitude and request.cf.longitude in JavaScript/TypeScript workers. In Python workers, this is accessed as request.cf.latitude and request.cf.longitude. For Hono framework, access the raw request via c.req.raw and then use req.cf?.latitude and req.cf?.longitude.
request.cf geolocation fields available
The request object has a cf property containing geolocation data. Available fields are: colo (Cloudflare data center), country, city, continent, latitude, longitude, postalCode, metroCode, region, regionCode, and timezone.
Cloudflare cf property for request modification
The RequestInit object accepts a cf property to change Cloudflare features on the outbound request. For example, {cf: {apps: false}} can be used to disable Cloudflare Apps for that request.
Modifying request headers preserves existing or replaces them
When setting headers via the RequestInit object in the constructor, this method will erase existing headers. To preserve and add headers, either set them individually after construction using request.headers.set(), or include all desired headers in the RequestInit headers object.
Changing request hostname immutably
Request URLs are immutable once constructed, so to change the hostname, create a new URL object with the desired hostname, then pass this URL string as the first argument to the Request constructor along with the modified RequestInit. Do not attempt to modify the URL of an existing Request object.
Request modification example with method, body, headers, and redirect
Example showing how to modify a request by creating a new RequestInit object with: method changed to POST, body changed to JSON.stringify({bar: 'foo'}), redirect mode set to 'follow', headers set to {'Content-Type': 'application/json'}, and cf property set to {apps: false}. The new request is constructed by passing the modified URL and a new Request object created from the original request plus RequestInit. Headers can be further modified using newRequest.headers.set() after construction.
Modifying request properties best practice
To modify a request in Workers, use RequestInit to define new properties and pass it to the Request constructor. Never modify a Request object directly after construction, since URLs and some other properties become immutable. The best practice is to always use the original request to construct the new request to clone all attributes, and then apply specific modifications.
Access triggered cron pattern in scheduled handler
Inside the scheduled handler, access the cron pattern that triggered execution via event.cron (JavaScript) or controller.cron (TypeScript). This allows you to branch logic based on which cron schedule executed.
Scheduled handler function signature
The scheduled handler receives three parameters: event (or controller in TypeScript as ScheduledController), env, and ctx (ExecutionContext). The event/controller object contains a cron property that holds the cron pattern that triggered the execution.
Use ctx.waitUntil for background streaming tasks
When streaming OpenAI responses, wrap the async streaming loop in ctx.waitUntil() to ensure the Worker runtime keeps the execution context alive while the stream is being processed and data is being sent to the client.
Modify response - Python example
Example showing how to modify response status, body, and headers in Python:
from workers import WorkerEntrypoint, Response, fetch
import json
class Default(WorkerEntrypoint):
async def fetch(self, request):
header_name_src = "foo"
header_name_dst = "Last-Modified"
original_response = await fetch(request)
response = Response(original_response.body, status=500, status_text="some message", headers=original_response.headers)
new_body = await original_response.json()
new_body["foo"] = "bar"
response.replace_body(json.dumps(new_body))
response.headers["foo"] = "bar"
src = response.headers[header_name_src]
if src is not None:
response.headers[header_name_dst] = src
print(f'Response header {header_name_dst} was set to {response.headers[header_name_dst]}')
return response
Modify response - Hono framework example
Example showing how to modify response status, body, and headers in Hono:
import { Hono } from 'hono';
const app = new Hono();
app.get('*', async (c) => {
const headerNameSrc = "foo";
const headerNameDst = "Last-Modified";
const originalResponse = await fetch(c.req.raw);
const originalBody = await originalResponse.json();
const modifiedBody = {
foo: "bar",
...originalBody
};
const response = new Response(JSON.stringify(modifiedBody), {
status: 500,
statusText: "some message",
headers: originalResponse.headers,
});
response.headers.set("foo", "bar");
const src = response.headers.get(headerNameSrc);
if (src != null) {
response.headers.set(headerNameDst, src);
console.log(`Response header "${headerNameDst}" was set to "${response.headers.get(headerNameDst)}"`);
}
return response;
});
export default app;
Modify response - TypeScript example
Example showing how to modify response status, body, and headers in TypeScript:
export default {
async fetch(request): Promise<Response> {
const headerNameSrc = "foo";
const headerNameDst = "Last-Modified";
const originalResponse = await fetch(request);
let response = new Response(originalResponse.body, {
status: 500,
statusText: "some message",
headers: originalResponse.headers,
});
const originalBody = await originalResponse.json();
const body = JSON.stringify({ foo: "bar", ...originalBody });
response = new Response(body, response);
response.headers.set("foo", "bar");
const src = response.headers.get(headerNameSrc);
if (src != null) {
response.headers.set(headerNameDst, src);
console.log(`Response header "${headerNameDst}" was set to "${response.headers.get(headerNameDst)}"`);
}
return response;
},
} satisfies ExportedHandler;
Modify response - JavaScript example
Example showing how to modify response status, body, and headers in JavaScript:
export default {
async fetch(request) {
const headerNameSrc = "foo";
const headerNameDst = "Last-Modified";
const originalResponse = await fetch(request);
let response = new Response(originalResponse.body, {
status: 500,
statusText: "some message",
headers: originalResponse.headers,
});
const originalBody = await originalResponse.json();
const body = JSON.stringify({ foo: "bar", ...originalBody });
response = new Response(body, response);
response.headers.set("foo", "bar");
const src = response.headers.get(headerNameSrc);
if (src != null) {
response.headers.set(headerNameDst, src);
console.log(`Response header "${headerNameDst}" was set to "${response.headers.get(headerNameDst)}"`);
}
return response;
},
};
Modify response body after fetching
To modify a response body, first deserialize the original body (e.g., with .json()), modify the data, serialize it back (e.g., with JSON.stringify), and create a new Response with the modified body: const originalBody = await originalResponse.json(); const body = JSON.stringify({ foo: 'bar', ...originalBody }); response = new Response(body, response).
Add and modify response headers using set method
Response headers can be modified using the headers set method: response.headers.set('foo', 'bar'). Headers can also be retrieved using response.headers.get(headerName) to read values before setting them.
Response properties are immutable; create a copy to modify
Response properties such as status and statusText are immutable and cannot be changed directly. To modify them, construct a new Response object and pass the modified status or statusText in the ResponseInit object. Response headers can be modified through the headers set method.
Modify response status and preserve body and headers
To change a response's status and statusText while preserving the original body and headers, create a new Response passing the original response body, the desired status and statusText, and the original headers: new Response(originalResponse.body, { status: 500, statusText: 'some message', headers: originalResponse.headers }).
Request method property
The request object has a method property that contains the HTTP method as a string (e.g., 'GET', 'POST').
HTTP 405 Method Not Allowed with Allow header
When rejecting an HTTP method, respond with status 405 and include an Allow header listing the permitted methods.
Read request URL in fetch handler
Access the incoming request URL using request.url. Use string methods like includes() to check if the URL contains specific paths.
Serve HTML response from Worker
Return a Response object with HTML content by setting the 'content-type' header to 'text/html;charset=UTF-8' and passing the HTML string as the response body.
Detect request method in fetch handler
Check request.method to determine if the incoming request is a POST, GET, or other HTTP method. Use string comparison like request.method === 'POST' or match against Method enum in Rust.
TypeScript example returning HTML
```ts
export default {
async fetch(request): Promise<Response> {
const html = `<!DOCTYPE html>
<body>
<h1>Hello World</h1>
<p>This markup was generated by a Cloudflare Worker.</p>
</body>`;
return new Response(html, {
headers: {
"content-type": "text/html;charset=UTF-8",
},
});
},
} satisfies ExportedHandler;```
Return HTML from Worker with Hono framework
To return an HTML page from a Cloudflare Worker using the Hono framework, import Hono and the html template tag from 'hono/html'. Create an app instance, define a route with app.get(), and use the html template tag to create the HTML document. Call c.html(doc) to return the response.
Return HTML from Worker Rust
To return an HTML page from a Cloudflare Worker in Rust, use the #[event(fetch)] macro on an async function that receives Request, Env, and Context parameters. Use Response::from_html(html) to return the HTML string directly.
Return HTML from Worker Python
To return an HTML page from a Cloudflare Worker in Python, define a class that extends WorkerEntrypoint and implements an async fetch method that receives the request parameter. Return a Response object with the HTML string and headers parameter set to a dict with 'content-type' key set to 'text/html;charset=UTF-8'.
Return HTML from Worker TypeScript
To return an HTML page from a Cloudflare Worker in TypeScript, create a fetch handler that returns a Response object with the HTML string and a content-type header set to 'text/html;charset=UTF-8'. The handler is async and receives the request parameter, returning a Promise<Response>. The handler must satisfy the ExportedHandler interface.
Python example returning HTML
```py
from workers import WorkerEntrypoint, Response
class Default(WorkerEntrypoint):
async def fetch(self, request):
html = """<!DOCTYPE html>
<body>
<h1>Hello World</h1>
<p>This markup was generated by a Cloudflare Worker.</p>
</body>"""
headers = {"content-type": "text/html;charset=UTF-8"}
return Response(html, headers=headers)```
Rust example returning HTML
```rs
use worker::*;
#[event(fetch)]
async fn fetch(_req: Request, _env: Env, _ctx: Context) -> Result<Response> {
let html = r#"<!DOCTYPE html>
<body>
<h1>Hello World</h1>
<p>This markup was generated by a Cloudflare Worker.</p>
</body>
"#;
Response::from_html(html)
}```