diff --git a/src/content/docs/workers/examples/103-early-hints.mdx b/src/content/docs/workers/examples/103-early-hints.mdx
index adcae028ab00206..4753b6f032471e7 100644
--- a/src/content/docs/workers/examples/103-early-hints.mdx
+++ b/src/content/docs/workers/examples/103-early-hints.mdx
@@ -136,4 +136,47 @@ def on_fetch(request):
return Response(HTML, headers=headers)
```
+
+
+```ts
+import { Hono } from "hono";
+
+const app = new Hono();
+
+const CSS = "body { color: red; }";
+const HTML = `
+
+
+
+
+ Early Hints test
+
+
+
+ Early Hints test page
+
+
+`;
+
+// Serve CSS file
+app.get("/test.css", (c) => {
+ return c.body(CSS, {
+ headers: {
+ "content-type": "text/css",
+ },
+ });
+});
+
+// Serve HTML with early hints
+app.get("*", (c) => {
+ return c.html(HTML, {
+ headers: {
+ link: "; rel=preload; as=style",
+ },
+ });
+});
+
+export default app;
+```
+
diff --git a/src/content/docs/workers/examples/ab-testing.mdx b/src/content/docs/workers/examples/ab-testing.mdx
index 3b71cca1afaeadf..1a4807ba6b7f7dc 100644
--- a/src/content/docs/workers/examples/ab-testing.mdx
+++ b/src/content/docs/workers/examples/ab-testing.mdx
@@ -137,4 +137,57 @@ async def on_fetch(request):
return fetch(urlunparse(url))
```
+
+
+```ts
+import { Hono } from "hono";
+import { getCookie, setCookie } from "hono/cookie";
+
+const app = new Hono();
+
+const NAME = "myExampleWorkersABTest";
+
+// Enable passthrough to allow direct access to control and test routes
+app.all("/control/*", (c) => fetch(c.req.raw));
+app.all("/test/*", (c) => fetch(c.req.raw));
+
+// Middleware to handle A/B testing logic
+app.use("*", async (c) => {
+ const url = new URL(c.req.url);
+
+ // Determine which group this requester is in
+ const abTestCookie = getCookie(c, NAME);
+
+ if (abTestCookie === "control") {
+ // User is in control group
+ url.pathname = "/control" + c.req.path;
+ } else if (abTestCookie === "test") {
+ // User is in test group
+ url.pathname = "/test" + c.req.path;
+ } else {
+ // If there is no cookie, this is a new client
+ // Choose a group and set the cookie (50/50 split)
+ const group = Math.random() < 0.5 ? "test" : "control";
+
+ // Update URL path based on assigned group
+ if (group === "control") {
+ url.pathname = "/control" + c.req.path;
+ } else {
+ url.pathname = "/test" + c.req.path;
+ }
+
+ // Set cookie to enable persistent A/B sessions
+ setCookie(c, NAME, group, {
+ path: "/",
+ });
+ }
+
+ const res = await fetch(url);
+
+ return c.body(res.body, res);
+});
+
+export default app;
+```
+
diff --git a/src/content/docs/workers/examples/accessing-the-cloudflare-object.mdx b/src/content/docs/workers/examples/accessing-the-cloudflare-object.mdx
index 47039f0e893ec93..2c3d21d8708548f 100644
--- a/src/content/docs/workers/examples/accessing-the-cloudflare-object.mdx
+++ b/src/content/docs/workers/examples/accessing-the-cloudflare-object.mdx
@@ -56,6 +56,30 @@ export default {
} satisfies ExportedHandler;
```
+
+
+```ts
+import { Hono } from "hono";
+
+const app = new Hono();
+
+app.get("*", async (c) => {
+ // Access the raw request to get the cf object
+ const req = c.req.raw;
+
+ // Check if the cf object is available
+ const data =
+ req.cf !== undefined
+ ? req.cf
+ : { error: "The `cf` object is not available inside the preview." };
+
+ // Return the data formatted with 2-space indentation
+ return c.json(data);
+});
+
+export default app;
+```
+
```py
diff --git a/src/content/docs/workers/examples/aggregate-requests.mdx b/src/content/docs/workers/examples/aggregate-requests.mdx
index b3717a412a2fa2d..4ca913c65272d2e 100644
--- a/src/content/docs/workers/examples/aggregate-requests.mdx
+++ b/src/content/docs/workers/examples/aggregate-requests.mdx
@@ -58,6 +58,32 @@ export default {
} satisfies ExportedHandler;
```
+
+
+```ts
+import { Hono } from "hono";
+
+const app = new Hono();
+
+app.get("*", async (c) => {
+ // someHost is set up to return JSON responses
+ const someHost = "https://jsonplaceholder.typicode.com";
+ const url1 = someHost + "/todos/1";
+ const url2 = someHost + "/todos/2";
+
+ // Fetch both URLs concurrently
+ const responses = await Promise.all([fetch(url1), fetch(url2)]);
+
+ // Parse JSON responses concurrently
+ const results = await Promise.all(responses.map((r) => r.json()));
+
+ // Return aggregated results
+ return c.json(results);
+});
+
+export default app;
+```
+
```py
diff --git a/src/content/docs/workers/examples/alter-headers.mdx b/src/content/docs/workers/examples/alter-headers.mdx
index d85457e48c9ea9c..f04da40ccc49445 100644
--- a/src/content/docs/workers/examples/alter-headers.mdx
+++ b/src/content/docs/workers/examples/alter-headers.mdx
@@ -103,6 +103,47 @@ async def on_fetch(request):
return Response(response.body, headers=new_headers)
```
+
+
+```ts
+import { Hono } from 'hono';
+
+const app = new Hono();
+
+app.use('*', async (c, next) => {
+ // Process the request with the next middleware/handler
+ await next();
+
+ // After the response is generated, we can modify its headers
+
+ // Add a custom header with a value
+ c.res.headers.append(
+ "x-workers-hello",
+ "Hello from Cloudflare Workers with Hono"
+ );
+
+ // Delete headers
+ c.res.headers.delete("x-header-to-delete");
+ c.res.headers.delete("x-header2-to-delete");
+
+ // Adjust the value for an existing header
+ c.res.headers.set("x-header-to-change", "NewValue");
+});
+
+app.get('*', async (c) => {
+ // Fetch content from example.com
+ const response = await fetch("https://example.com");
+
+ // Return the response body with original headers
+ // (our middleware will modify the headers before sending)
+ return new Response(response.body, {
+ headers: response.headers
+ });
+});
+
+export default app;
+```
+
You can also use the [`custom-headers-example` template](https://github.com/kristianfreeman/custom-headers-example) to deploy this code to your custom domain.
diff --git a/src/content/docs/workers/examples/auth-with-headers.mdx b/src/content/docs/workers/examples/auth-with-headers.mdx
index 2c0cc9a0299df52..121f1ff57f84681 100644
--- a/src/content/docs/workers/examples/auth-with-headers.mdx
+++ b/src/content/docs/workers/examples/auth-with-headers.mdx
@@ -95,4 +95,39 @@ async def on_fetch(request):
return Response("Sorry, you have supplied an invalid key.", status=403)
```
+
+
+```ts
+import { Hono } from 'hono';
+
+const app = new Hono();
+
+// Add authentication middleware
+app.use('*', async (c, next) => {
+ /**
+ * Define authentication constants
+ */
+ const PRESHARED_AUTH_HEADER_KEY = "X-Custom-PSK";
+ const PRESHARED_AUTH_HEADER_VALUE = "mypresharedkey";
+
+ // Get the pre-shared key from the request header
+ const psk = c.req.header(PRESHARED_AUTH_HEADER_KEY);
+
+ if (psk === PRESHARED_AUTH_HEADER_VALUE) {
+ // Correct preshared header key supplied. Continue to the next handler.
+ await next();
+ } else {
+ // Incorrect key supplied. Reject the request.
+ return c.text("Sorry, you have supplied an invalid key.", 403);
+ }
+});
+
+// Handle all authenticated requests by passing through to origin
+app.all('*', async (c) => {
+ return fetch(c.req.raw);
+});
+
+export default app;
+```
+
diff --git a/src/content/docs/workers/examples/basic-auth.mdx b/src/content/docs/workers/examples/basic-auth.mdx
index d175ce90b08546f..46105a13575d8a7 100644
--- a/src/content/docs/workers/examples/basic-auth.mdx
+++ b/src/content/docs/workers/examples/basic-auth.mdx
@@ -266,15 +266,15 @@ use worker::*;
#[event(fetch)]
async fn fetch(req: Request, env: Env, _ctx: Context) -> Result {
- let basic_user = "admin";
- // You will need an admin password. This should be
- // attached to your Worker as an encrypted secret.
- // Refer to https://developers.cloudflare.com/workers/configuration/secrets/
- let basic_pass = match env.secret("PASSWORD") {
- Ok(s) => s.to_string(),
- Err(_) => "password".to_string(),
- };
- let url = req.url()?;
+let basic_user = "admin";
+// You will need an admin password. This should be
+// attached to your Worker as an encrypted secret.
+// Refer to https://developers.cloudflare.com/workers/configuration/secrets/
+let basic_pass = match env.secret("PASSWORD") {
+Ok(s) => s.to_string(),
+Err(_) => "password".to_string(),
+};
+let url = req.url()?;
match url.path() {
"/" => Response::ok("Anyone can access the homepage."),
@@ -328,6 +328,56 @@ async fn fetch(req: Request, env: Env, _ctx: Context) -> Result {
}
_ => Response::error("Not Found.", 404),
}
+
}
-```
-
\ No newline at end of file
+
+````
+
+
+```ts
+/**
+ * Shows how to restrict access using the HTTP Basic schema with Hono.
+ * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication
+ * @see https://tools.ietf.org/html/rfc7617
+ */
+
+import { Hono } from "hono";
+import { basicAuth } from "hono/basic-auth";
+
+// Define environment interface
+interface Env {
+ Bindings: {
+ USERNAME: string;
+ PASSWORD: string;
+ };
+}
+
+const app = new Hono();
+
+// Public homepage - accessible to everyone
+app.get("/", (c) => {
+ return c.text("Anyone can access the homepage.");
+});
+
+// Admin route - protected with Basic Auth
+app.get(
+ "/admin",
+ async (c, next) => {
+ const auth = basicAuth({
+ username: c.env.USERNAME,
+ password: c.env.PASSWORD
+ })
+
+ return await auth(c, next);
+ },
+ (c) => {
+ return c.text("🎉 You have private access!", 200, {
+ "Cache-Control": "no-store",
+ });
+ }
+);
+
+export default app;
+````
+
+
diff --git a/src/content/docs/workers/examples/block-on-tls.mdx b/src/content/docs/workers/examples/block-on-tls.mdx
index 05741afacb2fb6c..b564e92c3c8e406 100644
--- a/src/content/docs/workers/examples/block-on-tls.mdx
+++ b/src/content/docs/workers/examples/block-on-tls.mdx
@@ -69,6 +69,42 @@ export default {
} satisfies ExportedHandler;
```
+
+
+```ts
+import { Hono } from "hono";
+
+const app = new Hono();
+
+// Middleware to check TLS version
+app.use("*", async (c, next) => {
+ // Access the raw request to get the cf object with TLS info
+ const request = c.req.raw;
+ const tlsVersion = request.cf?.tlsVersion;
+
+ // Allow only TLS versions 1.2 and 1.3
+ if (tlsVersion !== "TLSv1.2" && tlsVersion !== "TLSv1.3") {
+ return c.text("Please use TLS version 1.2 or higher.", 403);
+ }
+
+ await next();
+
+});
+
+app.onError((err, c) => {
+ console.error(
+ "request.cf does not exist in the previewer, only in production",
+ );
+ return c.text(`Error in workers script: ${err.message}`, 500);
+});
+
+app.get("/", async (c) => {
+ return c.text(`TLS Version: ${c.req.raw.cf.tlsVersion}`);
+});
+
+export default app;
+```
+
```py
diff --git a/src/content/docs/workers/examples/bulk-origin-proxy.mdx b/src/content/docs/workers/examples/bulk-origin-proxy.mdx
index 28b7febfa193cdd..d8b03e760233245 100644
--- a/src/content/docs/workers/examples/bulk-origin-proxy.mdx
+++ b/src/content/docs/workers/examples/bulk-origin-proxy.mdx
@@ -74,6 +74,39 @@ export default {
} satisfies ExportedHandler;
```
+
+
+```ts
+import { Hono } from "hono";
+import { proxy } from "hono/proxy";
+
+// An object with different URLs to fetch
+const ORIGINS: Record = {
+ "starwarsapi.yourdomain.com": "swapi.dev",
+ "google.yourdomain.com": "www.google.com",
+};
+
+const app = new Hono();
+
+app.all("*", async (c) => {
+ const url = new URL(c.req.url);
+
+ // Check if incoming hostname is a key in the ORIGINS object
+ if (url.hostname in ORIGINS) {
+ const target = ORIGINS[url.hostname];
+ url.hostname = target;
+
+ // If it is, proxy request to that third party origin
+ return proxy(url, c.req.raw);
+ }
+
+ // Otherwise, process request as normal
+ return proxy(c.req.raw);
+});
+
+export default app;
+```
+
```py
diff --git a/src/content/docs/workers/examples/bulk-redirects.mdx b/src/content/docs/workers/examples/bulk-redirects.mdx
index e29389c3597dd73..8f5e60ed41ce114 100644
--- a/src/content/docs/workers/examples/bulk-redirects.mdx
+++ b/src/content/docs/workers/examples/bulk-redirects.mdx
@@ -99,4 +99,44 @@ async def on_fetch(request):
return fetch(request)
```
+
+
+```ts
+import { Hono } from "hono";
+
+const app = new Hono();
+
+// Configure your redirects
+const externalHostname = "examples.cloudflareworkers.com";
+
+const redirectMap = new Map([
+ ["/bulk1", `https://${externalHostname}/redirect2`],
+ ["/bulk2", `https://${externalHostname}/redirect3`],
+ ["/bulk3", `https://${externalHostname}/redirect4`],
+ ["/bulk4", "https://google.com"],
+]);
+
+// Middleware to handle redirects
+app.use("*", async (c, next) => {
+ const path = c.req.path;
+ const location = redirectMap.get(path);
+
+ if (location) {
+ // If path is in our redirect map, perform the redirect
+ return c.redirect(location, 301);
+ }
+
+ // Otherwise, continue to the next handler
+ await next();
+});
+
+// Default handler for requests that don't match any redirects
+app.all("*", async (c) => {
+ // Pass through to origin
+ return fetch(c.req.raw);
+});
+
+export default app;
+```
+
diff --git a/src/content/docs/workers/examples/cache-api.mdx b/src/content/docs/workers/examples/cache-api.mdx
index 4a80a1debf9bfe8..3082c2749c05cad 100644
--- a/src/content/docs/workers/examples/cache-api.mdx
+++ b/src/content/docs/workers/examples/cache-api.mdx
@@ -132,4 +132,29 @@ async def on_fetch(request, _env, ctx):
return response
```
+
+
+```ts
+import { Hono } from "hono";
+import { cache } from "hono/cache";
+
+const app = new Hono();
+
+// We leverage hono built-in cache helper here
+app.get(
+ "*",
+ cache({
+ cacheName: "my-cache",
+ cacheControl: "max-age=3600", // 1 hour
+ }),
+);
+
+// Add a route to handle the request if it's not in cache
+app.get("*", (c) => {
+ return c.text("Hello from Hono!");
+});
+
+export default app;
+```
+
diff --git a/src/content/docs/workers/examples/cache-post-request.mdx b/src/content/docs/workers/examples/cache-post-request.mdx
index 717cd0a73fec0ba..725722ea28d0763 100644
--- a/src/content/docs/workers/examples/cache-post-request.mdx
+++ b/src/content/docs/workers/examples/cache-post-request.mdx
@@ -147,4 +147,58 @@ async def on_fetch(request, _, ctx):
return fetch(request)
```
+
+
+```ts
+import { Hono } from "hono";
+import { sha256 } from "hono/utils/crypto";
+
+const app = new Hono();
+
+// Middleware for caching POST requests
+app.post("*", async (c) => {
+ try {
+ // Get the request body
+ const body = await c.req.raw.clone().text();
+
+ // Hash the request body to use it as part of the cache key
+ const hash = await sha256(body);
+
+ // Create the cache URL
+ const cacheUrl = new URL(c.req.url);
+
+ // Store the URL in cache by prepending the body's hash
+ cacheUrl.pathname = "/posts" + cacheUrl.pathname + hash;
+
+ // Convert to a GET to be able to cache
+ const cacheKey = new Request(cacheUrl.toString(), {
+ headers: c.req.raw.headers,
+ method: "GET",
+ });
+
+ const cache = caches.default;
+
+ // Find the cache key in the cache
+ let response = await cache.match(cacheKey);
+
+ // If not in cache, fetch response to POST request from origin
+ if (!response) {
+ response = await fetch(c.req.raw);
+ c.executionCtx.waitUntil(cache.put(cacheKey, response.clone()));
+ }
+
+ return response;
+ } catch (e) {
+ return c.text("Error thrown " + e.message, 500);
+ }
+});
+
+// Handle all other HTTP methods
+app.all("*", (c) => {
+ return fetch(c.req.raw);
+});
+
+export default app;
+```
+
diff --git a/src/content/docs/workers/examples/cache-tags.mdx b/src/content/docs/workers/examples/cache-tags.mdx
index 64de2b5e5900d26..0b0993baf4458c3 100644
--- a/src/content/docs/workers/examples/cache-tags.mdx
+++ b/src/content/docs/workers/examples/cache-tags.mdx
@@ -25,8 +25,7 @@ export default {
const params = requestUrl.searchParams;
const tags =
params && params.has("tags") ? params.get("tags").split(",") : [];
- const url =
- params && params.has("uri") ? JSON.parse(params.get("uri")) : "";
+ const url = params && params.has("uri") ? params.get("uri") : "";
if (!url) {
const errorObject = {
error: "URL cannot be empty",
@@ -69,8 +68,7 @@ export default {
const params = requestUrl.searchParams;
const tags =
params && params.has("tags") ? params.get("tags").split(",") : [];
- const url =
- params && params.has("uri") ? JSON.parse(params.get("uri")) : "";
+ const url = params && params.has("uri") ? params.get("uri") : "";
if (!url) {
const errorObject = {
error: "URL cannot be empty",
@@ -104,6 +102,46 @@ export default {
} satisfies ExportedHandler;
```
+
+
+```ts
+import { Hono } from "hono";
+
+const app = new Hono();
+
+app.all("*", async (c) => {
+ const tags = c.req.query("tags") ? c.req.query("tags").split(",") : [];
+ const uri = c.req.query("uri") ? c.req.query("uri") : "";
+
+ if (!uri) {
+ return c.json({ error: "URL cannot be empty" }, 400);
+ }
+
+ const init = {
+ cf: {
+ cacheTags: tags,
+ },
+ };
+
+ const result = await fetch(uri, init);
+ const cacheStatus = result.headers.get("cf-cache-status");
+ const lastModified = result.headers.get("last-modified");
+
+ const response = {
+ cache: cacheStatus,
+ lastModified: lastModified,
+ };
+
+ return c.json(response, result.status);
+});
+
+app.onError((err, c) => {
+ return c.json({ error: err.message }, 500);
+});
+
+export default app;
+```
+
```py
diff --git a/src/content/docs/workers/examples/cache-using-fetch.mdx b/src/content/docs/workers/examples/cache-using-fetch.mdx
index 7cc76df5d6bf6b5..e42e86d9106b6d6 100644
--- a/src/content/docs/workers/examples/cache-using-fetch.mdx
+++ b/src/content/docs/workers/examples/cache-using-fetch.mdx
@@ -76,6 +76,46 @@ export default {
} satisfies ExportedHandler;
```
+
+
+```ts
+import { Hono } from 'hono';
+
+type Bindings = {};
+
+const app = new Hono<{ Bindings: Bindings }>();
+
+app.all('*', async (c) => {
+ const url = new URL(c.req.url);
+
+ // Only use the path for the cache key, removing query strings
+ // and always store using HTTPS, for example, https://www.example.com/file-uri-here
+ const someCustomKey = `https://${url.hostname}${url.pathname}`;
+
+ // Fetch the request with custom cache settings
+ let response = await fetch(c.req.raw, {
+ cf: {
+ // Always cache this fetch regardless of content type
+ // for a max of 5 seconds before revalidating the resource
+ cacheTtl: 5,
+ cacheEverything: true,
+ // Enterprise only feature, see Cache API for other plans
+ cacheKey: someCustomKey,
+ },
+ });
+
+ // Reconstruct the Response object to make its headers mutable
+ response = new Response(response.body, response);
+
+ // Set cache control headers to cache on browser for 25 minutes
+ response.headers.set("Cache-Control", "max-age=1500");
+
+ return response;
+});
+
+export default app;
+```
+
```py
@@ -226,6 +266,38 @@ export default {
} satisfies ExportedHandler;
```
+
+
+```ts
+import { Hono } from 'hono';
+
+type Bindings = {};
+
+const app = new Hono<{ Bindings: Bindings }>();
+
+app.all('*', async (c) => {
+ const originalUrl = c.req.url;
+ const url = new URL(originalUrl);
+
+ // Randomly select a storage backend
+ if (Math.random() < 0.5) {
+ url.hostname = "example.s3.amazonaws.com";
+ } else {
+ url.hostname = "example.storage.googleapis.com";
+ }
+
+ // Create a new request to the selected backend
+ const newRequest = new Request(url, c.req.raw);
+
+ // Fetch using the original URL as the cache key
+ return fetch(newRequest, {
+ cf: { cacheKey: originalUrl },
+ });
+});
+
+export default app;
+```
+
Workers operating on behalf of different zones cannot affect each other's cache. You can only override cache keys when making requests within your own zone (in the above example `event.request.url` was the key stored), or requests to hosts that are not on Cloudflare. When making a request to another Cloudflare zone (for example, belonging to a different Cloudflare customer), that zone fully controls how its own content is cached within Cloudflare; you cannot override it.
diff --git a/src/content/docs/workers/examples/conditional-response.mdx b/src/content/docs/workers/examples/conditional-response.mdx
index 8ad70c4a9a371a5..1295423b4e000c9 100644
--- a/src/content/docs/workers/examples/conditional-response.mdx
+++ b/src/content/docs/workers/examples/conditional-response.mdx
@@ -163,4 +163,76 @@ async def on_fetch(request):
return fetch(request)
```
+
+
+```ts
+import { Hono } from "hono";
+import { HTTPException } from "hono/http-exception";
+
+const app = new Hono();
+
+// Middleware to handle all conditions before reaching the main handler
+app.use("*", async (c, next) => {
+ const request = c.req.raw;
+ const BLOCKED_HOSTNAMES = ["nope.mywebsite.com", "bye.website.com"];
+ const hostname = new URL(c.req.url)?.hostname;
+
+ // Return a new Response based on a URL's hostname
+ if (BLOCKED_HOSTNAMES.includes(hostname)) {
+ return c.text("Blocked Host", 403);
+ }
+
+ // Block paths ending in .doc or .xml based on the URL's file extension
+ const forbiddenExtRegExp = new RegExp(/\.(doc|xml)$/);
+ if (forbiddenExtRegExp.test(c.req.pathname)) {
+ return c.text("Blocked Extension", 403);
+ }
+
+ // On User Agent
+ const userAgent = c.req.header("User-Agent") || "";
+ if (userAgent.includes("bot")) {
+ return c.text("Block User Agent containing bot", 403);
+ }
+
+ // On Client's IP address
+ const clientIP = c.req.header("CF-Connecting-IP");
+ if (clientIP === "1.2.3.4") {
+ return c.text("Block the IP 1.2.3.4", 403);
+ }
+
+ // On ASN
+ if (request.cf && request.cf.asn === 64512) {
+ return c.text("Block the ASN 64512 response");
+ }
+
+ // On Device Type
+ // Requires Enterprise "CF-Device-Type Header" zone setting or
+ // Page Rule with "Cache By Device Type" setting applied.
+ const device = c.req.header("CF-Device-Type");
+ if (device === "mobile") {
+ return c.redirect("https://mobile.example.com");
+ }
+
+ // Continue to the next handler
+ await next();
+});
+
+// Handle POST requests differently
+app.post("*", (c) => {
+ return c.text("Response for POST");
+});
+
+// Default handler for other methods
+app.get("*", async (c) => {
+ console.error(
+ "Getting Client's IP address, device type, and ASN are not supported in playground. Must test on a live worker",
+ );
+
+ // Fetch the original request
+ return fetch(c.req.raw);
+});
+
+export default app;
+```
+
diff --git a/src/content/docs/workers/examples/cors-header-proxy.mdx b/src/content/docs/workers/examples/cors-header-proxy.mdx
index f0db16e550324af..e25baa21e3c8f57 100644
--- a/src/content/docs/workers/examples/cors-header-proxy.mdx
+++ b/src/content/docs/workers/examples/cors-header-proxy.mdx
@@ -328,6 +328,145 @@ export default {
} satisfies ExportedHandler;
```
+
+
+```ts
+import { Hono } from "hono";
+import { cors } from "hono/cors";
+
+// The URL for the remote third party API you want to fetch from
+// but does not implement CORS
+const API_URL = "https://examples.cloudflareworkers.com/demos/demoapi";
+
+// The endpoint you want the CORS reverse proxy to be on
+const PROXY_ENDPOINT = "/corsproxy/";
+
+const app = new Hono();
+
+// Demo page handler
+app.get("*", async (c) => {
+ // Only handle non-proxy requests with this handler
+ if (c.req.path.startsWith(PROXY_ENDPOINT)) {
+ return next();
+ }
+
+ // Create the demo page HTML
+ const DEMO_PAGE = `
+
+
+
+ API GET without CORS Proxy
+ Shows TypeError: Failed to fetch since CORS is misconfigured
+
+ Waiting
+ API GET with CORS Proxy
+
+ Waiting
+ API POST with CORS Proxy + Preflight
+
+ Waiting
+
+
+
+ `;
+
+ return c.html(DEMO_PAGE);
+});
+
+// CORS proxy routes
+app.on(["GET", "HEAD", "POST", "OPTIONS"], PROXY_ENDPOINT + "*", async (c) => {
+ const url = new URL(c.req.url);
+
+ // Handle OPTIONS preflight requests
+ if (c.req.method === "OPTIONS") {
+ const origin = c.req.header("Origin");
+ const requestMethod = c.req.header("Access-Control-Request-Method");
+ const requestHeaders = c.req.header("Access-Control-Request-Headers");
+
+ if (origin && requestMethod && requestHeaders) {
+ // Handle CORS preflight requests
+ return new Response(null, {
+ headers: {
+ "Access-Control-Allow-Origin": "*",
+ "Access-Control-Allow-Methods": "GET,HEAD,POST,OPTIONS",
+ "Access-Control-Max-Age": "86400",
+ "Access-Control-Allow-Headers": requestHeaders,
+ },
+ });
+ } else {
+ // Handle standard OPTIONS request
+ return new Response(null, {
+ headers: {
+ Allow: "GET, HEAD, POST, OPTIONS",
+ },
+ });
+ }
+ }
+
+ // Handle actual requests
+ let apiUrl = url.searchParams.get("apiurl") || API_URL;
+
+ // Rewrite request to point to API URL
+ const modifiedRequest = new Request(apiUrl, c.req.raw);
+ modifiedRequest.headers.set("Origin", new URL(apiUrl).origin);
+
+ let response = await fetch(modifiedRequest);
+
+ // Recreate the response so we can modify the headers
+ response = new Response(response.body, response);
+
+ // Set CORS headers
+ response.headers.set("Access-Control-Allow-Origin", url.origin);
+
+ // Append to/Add Vary header so browser will cache response correctly
+ response.headers.append("Vary", "Origin");
+
+ return response;
+});
+
+// Handle method not allowed for proxy endpoint
+app.all(PROXY_ENDPOINT + "*", (c) => {
+ return new Response(null, {
+ status: 405,
+ statusText: "Method Not Allowed",
+ });
+});
+
+export default app;
+```
+
```py
@@ -442,100 +581,102 @@ async def on_fetch(request):
use std::{borrow::Cow, collections::HashMap};
use worker::*;
-fn raw_html_response(html: &str) -> Result {
- Response::from_html(html)
+fn raw*html_response(html: &str) -> Result {
+Response::from_html(html)
}
async fn handle_request(req: Request, api_url: &str) -> Result {
- let url = req.url().unwrap();
- let mut api_url2 = url
- .query_pairs()
- .find(|x| x.0 == Cow::Borrowed("apiurl"))
- .unwrap()
- .1
- .to_string();
- if api_url2 == String::from("") {
- api_url2 = api_url.to_string();
- }
- let mut request = req.clone_mut()?;
- *request.path_mut()? = api_url2.clone();
- if let url::Origin::Tuple(origin, _, _) = Url::parse(&api_url2)?.origin() {
- (*request.headers_mut()?).set("Origin", &origin)?;
- }
- let mut response = Fetch::Request(request).send().await?.cloned()?;
- let headers = response.headers_mut();
- if let url::Origin::Tuple(origin, _, _) = url.origin() {
- headers.set("Access-Control-Allow-Origin", &origin)?;
- headers.set("Vary", "Origin")?;
- }
+let url = req.url().unwrap();
+let mut api_url2 = url
+.query_pairs()
+.find(|x| x.0 == Cow::Borrowed("apiurl"))
+.unwrap()
+.1
+.to_string();
+if api_url2 == String::from("") {
+api_url2 = api_url.to_string();
+}
+let mut request = req.clone_mut()?;
+\*request.path_mut()? = api_url2.clone();
+if let url::Origin::Tuple(origin, *, _) = Url::parse(&api_url2)?.origin() {
+(\*request.headers_mut()?).set("Origin", &origin)?;
+}
+let mut response = Fetch::Request(request).send().await?.cloned()?;
+let headers = response.headers_mut();
+if let url::Origin::Tuple(origin, _, \_) = url.origin() {
+headers.set("Access-Control-Allow-Origin", &origin)?;
+headers.set("Vary", "Origin")?;
+}
Ok(response)
+
}
-fn handle_options(req: Request, cors_headers: &HashMap<&str, &str>) -> Result {
- let headers: Vec<_> = req.headers().keys().collect();
- if [
- "access-control-request-method",
- "access-control-request-headers",
- "origin",
- ]
- .iter()
- .all(|i| headers.contains(&i.to_string()))
- {
- let mut headers = Headers::new();
- for (k, v) in cors_headers.iter() {
- headers.set(k, v)?;
- }
- return Ok(Response::empty()?.with_headers(headers));
- }
- Response::empty()
+fn handle*options(req: Request, cors_headers: &HashMap<&str, &str>) -> Result {
+let headers: Vec<*> = req.headers().keys().collect();
+if [
+"access-control-request-method",
+"access-control-request-headers",
+"origin",
+]
+.iter()
+.all(|i| headers.contains(&i.to_string()))
+{
+let mut headers = Headers::new();
+for (k, v) in cors_headers.iter() {
+headers.set(k, v)?;
}
-#[event(fetch)]
-async fn fetch(req: Request, _env: Env, _ctx: Context) -> Result {
- let cors_headers = HashMap::from([
- ("Access-Control-Allow-Origin", "*"),
- ("Access-Control-Allow-Methods", "GET,HEAD,POST,OPTIONS"),
- ("Access-Control-Max-Age", "86400"),
- ]);
- let api_url = "https://examples.cloudflareworkers.com/demos/demoapi";
- let proxy_endpoint = "/corsproxy/";
- let demo_page = format!(
- r#"
-
-
-
- API GET without CORS Proxy
- Shows TypeError: Failed to fetch since CORS is misconfigured
-
- Waiting
- API GET with CORS Proxy
-
- Waiting
- API POST with CORS Proxy + Preflight
-
- Waiting
-
-
-
- "#
- );
+}}
+}})()
+
+