Template Gallery

These templates are simple building blocks for developing Workers scripts.

Boilerplates

Hello World

Simple Hello World in JS

Paste this into your terminal:

Hello World Rust

Simple Hello World in Rust

Paste this into your terminal:

Router

Selects the logic based on the request method and URL. Use with REST APIs or apps that require routing logic.

Paste this into your terminal:

Hello World TypeScript

Simple Hello World in TypeScript

Paste this into your terminal:

Snippets

A/B Testing

Set up an A/B test by controlling what response is served based on cookies

Copy into a Worker script:
async function handleRequest(request) { const NAME = 'experiment-0' // Responses below are place holders, you could set up // a custom path for each test (e.g. /control/somepath ) const TEST_RESPONSE = new Response('Test group') // fetch('/test/sompath', request) const CONTROL_RESPONSE = new Response('Control group') // fetch('/control/sompath', request) // Determine which group this requester is in. const cookie = request.headers.get('cookie') if (cookie && cookie.includes(`${NAME}=control`)) { return CONTROL_RESPONSE } else if (cookie && cookie.includes(`${NAME}=test`)) { return TEST_RESPONSE } else { // if no cookie then this is a new client, decide a group and set the cookie let group = Math.random() < 0.5 ? 'test' : 'control' // 50/50 split let response = group === 'control' ? CONTROL_RESPONSE : TEST_RESPONSE response.headers.append('Set-Cookie', `${NAME}=${group}; path=/`) return response } } addEventListener('fetch', event => { event.respondWith(handleRequest(event.request)) })

Aggregate Requests

Sends two GET request to two urls and aggregates the responses into one response.

Copy into a Worker script:
async function handleRequest(request) { const init = { headers: { 'content-type': type, }, } const responses = await Promise.all([fetch(url1, init), fetch(url2, init)]) const results = await Promise.all([gatherResponse(responses[0]), gatherResponse(responses[1])]) return new Response(results, init) } addEventListener('fetch', event => { return event.respondWith(handleRequest(event.request)) }) /** * gatherResponse awaits and returns a response body as a string. * Use await gatherResponse(..) in an async function to get the response body * @param {Response} response */ async function gatherResponse(response) { const { headers } = response const contentType = headers.get('content-type') if (contentType.includes('application/json')) { return await response.json() } else if (contentType.includes('application/text')) { return await response.text() } else if (contentType.includes('text/html')) { return await response.text() } else { return await response.text() } } /** * Example someHost is set up to return JSON responses * Replace url1 and url2 with the hosts you wish to * send requests to * @param {string} url the URL to send the request to */ const someHost = 'https://workers-tooling.cf/demos' const url1 = someHost + '/requests/json' const url2 = someHost + '/requests/json' const type = 'application/json;charset=UTF-8'

Alter Headers

Change the headers sent in a request or returned in a response

Copy into a Worker script:
async function handleRequest(request) { // Make the headers mutable by re-constructing the Request. request = new Request(request) request.headers.set('x-my-header', 'custom value') const URL = 'https://workers-tooling.cf/demos/static/html' // URL is set up to respond with dummy HTML, remove to send requests to your own origin let response = await fetch(URL, request) // Make the headers mutable by re-constructing the Response. response = new Response(response.body, response) response.headers.set('x-my-header', 'custom value') return response } addEventListener('fetch', event => { event.respondWith(handleRequest(event.request)) })

Auth with headers

Allow or deny a request based on a known pre-shared key in a header. Note while this simple implementation is helpful, it is not meant to replace more secure scripts such as signed requests using the WebCrypto API.

Copy into a Worker script:
async function handleRequest(request) { let psk = request.headers.get(PRESHARED_AUTH_HEADER_KEY) if (psk === PRESHARED_AUTH_HEADER_VALUE) { // Correct preshared header key supplied. Fetching request // from origin return fetch(request) } // Incorrect key rejecting request return new Response('Sorry, you have supplied an invalid key.', { status: 403, }) } addEventListener('fetch', event => { event.respondWith(handleRequest(event.request)) }) /** * @param {string} PRESHARED_AUTH_HEADER_KEY custom header to check for key * @param {string} PRESHARED_AUTH_HEADER_VALUE hard coded key value */ const PRESHARED_AUTH_HEADER_KEY = 'X-Custom-PSK' const PRESHARED_AUTH_HEADER_VALUE = 'mypresharedkey'

Bulk Origin Proxies

Resolve requests to your domain to a set of proxy third-party origins

Copy into a Worker script:
async function handleRequest(request) { let url = new URL(request.url) // Check if incoming hostname is // a key in the ORIGINS object let target = ORIGINS[url.hostname] // If it is, proxy request to that third party origin if (target) { url.hostname = target return fetch(url, request) } // Otherwise, process request as normal return fetch(request) } addEventListener('fetch', event => { event.respondWith(handleRequest(event.request)) }) /** * An object with different url's to fetch * @param {Object} ORIGINS */ const ORIGINS = { 'starwarsapi.yourdomain.com': 'swapi.co', 'google.yourdomain.com': 'google.com', }

Bulk Redirects

Redirects requests to certain URLs based a mapped object to the request’s URL.

Copy into a Worker script:
async function handleRequest(request) { let requestURL = new URL(request.url) let path = requestURL.pathname.split('/redirect')[1] let location = redirectMap.get(path) if (location) { return Response.redirect(location, 301) } // If in map, return the original request return fetch(request) } addEventListener('fetch', async event => { event.respondWith(handleRequest(event.request)) }) const externalHostname = 'workers-tooling.cf' const redirectMap = new Map([ ['/bulk1', 'https://' + externalHostname + '/redirect2'], ['/bulk2', 'https://' + externalHostname + '/redirect3'], ['/bulk3', 'https://' + externalHostname + '/redirect4'], ['/bulk4', 'https://google.com'], ])

Cache API

Cache using Cloudflare’s Cache API. This example can cache POST requests as well as change what hostname to store a response in cache. Note the previewer is not avalible for using Cache API.

Copy into a Worker script:
async function handleRequest(event) { let request = event.request let cacheUrl = new URL(request.url) // hostname for a different zone cacheUrl.hostname = someOtherHostname let cacheKey = new Request(cacheUrl, request) let cache = caches.default // Get this request from this zone's cache let response = await cache.match(cacheKey) if (!response) { //if not in cache, grab it from the origin response = await fetch(request) // must use Response constructor to inherit all of response's fields response = new Response(response.body, response) // Cache API respects Cache-Control headers, so by setting max-age to 10 // the response will only live in cache for max of 10 seconds response.headers.append('Cache-Control', 'max-age=10') // store the fetched response as cacheKey // use waitUntil so computational expensive tasks don't delay the response event.waitUntil(cache.put(cacheKey, response.clone())) } return response } async function handlePostRequest(event) { let request = event.request let body = await request.clone().text() let hash = await sha256(body) let cacheUrl = new URL(request.url) // get/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 let cacheKey = new Request(cacheUrl, { headers: request.headers, method: 'GET', }) let cache = caches.default //try to find the cache key in the cache let response = await cache.match(cacheKey) // otherwise, fetch response to POST request from origin if (!response) { response = await fetch(request) event.waitUntil(cache.put(cacheKey, response)) } return response } addEventListener('fetch', event => { try { let request = event.request if (request.method.toUpperCase() === 'POST') return event.respondWith(handlePostRequest(event)) return event.respondWith(handleRequest(event)) } catch (e) { return event.respondWith(new Response('Error thrown ' + e.message)) } }) async function sha256(message) { // encode as UTF-8 const msgBuffer = new TextEncoder().encode(message) // hash the message const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer) // convert ArrayBuffer to Array const hashArray = Array.from(new Uint8Array(hashBuffer)) // convert bytes to hex string const hashHex = hashArray.map(b => ('00' + b.toString(16)).slice(-2)).join('') return hashHex } const someOtherHostname = 'my.herokuapp.com'

Cache TTL and Custom Keys

Determine how/if Cloudflare’s Classic CDN and Browsers will cache a resource by setting TTLs, cache keys, and cache settings.

Copy into a Worker script:
async function handleRequest(request) { let url = new URL(request.url) // Only use the path for the cache key, removing query strings // and always storing HTTPS e.g. https://www.example.com/file-uri-here let someCustomKey = `https://${url.hostname}${url.pathname}` let response = await fetch(request, { cf: { // Tell Cloudflare's Global CDN to always cache this fetch regardless of content type // for a max of 5 seconds before revalidating the resource cacheTtl: 5, // sets TTL to 5 and cacheEverything setting //Enterprise only feature, see Cache API for other plans cacheKey: someCustomKey, }, }) // must use Response constructor to inherit all of response's fields 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 } addEventListener('fetch', event => { return event.respondWith(handleRequest(event.request)) })

Conditional Response

Return a response based on the incoming request’s URL, HTTP method, User Agent, IP address, ASN or device type (e.g. mobile)

Copy into a Worker script:
const BLOCKED_HOSTNAMES = ['nope.mywebsite.com', 'bye.website.com'] async function handleRequest(request) { // Return a new Response based on.. // On URL's hostname let url = new URL(request.url) if (BLOCKED_HOSTNAMES.includes(url.hostname)) { return new Response('Blocked Host', { status: 403 }) } // On URL's file extenstion (e.g. block paths ending in .doc or .xml) let forbiddenExtRegExp = new RegExp(/\.(doc|xml)$/) if (forbiddenExtRegExp.test(url.pathname)) { return new Response('Blocked Extension', { status: 403 }) } // On HTTP method if (request.method === 'POST') { return new Response('Response for POST') } // On User Agent let userAgent = request.headers.get('User-Agent') || '' if (userAgent.includes('bot')) { return new Response('Block User Agent containing bot', { status: 403 }) } // On Client's IP address let clientIP = request.headers.get('CF-Connecting-IP') if (clientIP === '1.2.3.4') { return new Response('Block the IP 1.2.3.4', { status: 403 }) } // On ASN if ((request.cf || {}).asn == 64512) { return new Response('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. let device = request.headers.get('CF-Device-Type') if (device === 'mobile') { return Response.redirect('https://mobile.example.com') } console.error( "Getting Client's IP address, device type, and ASN are not supported in playground. Must test on a live worker", ) return fetch(request) } addEventListener('fetch', event => { event.respondWith(handleRequest(event.request)) })

CORS Header Proxy

Add necessary CORS headers to a third party API response

Copy into a Worker script:
async function handleRequest(request) { const url = new URL(request.url) const apiurl = url.searchParams.get('apiurl') // Rewrite request to point to API url. This also makes the request mutable // so we can add the correct Origin header to make the API server think // that this request isn't cross-site. request = new Request(apiurl, request) request.headers.set('Origin', new URL(apiurl).origin) let response = await fetch(request) // 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 } function handleOptions(request) { // Make sure the necesssary headers are present // for this to be a valid pre-flight request if ( request.headers.get('Origin') !== null && request.headers.get('Access-Control-Request-Method') !== null && request.headers.get('Access-Control-Request-Headers') !== null ) { // Handle CORS pre-flight request. // If you want to check the requested method + headers // you can do that here. return new Response(null, { headers: corsHeaders, }) } else { // Handle standard OPTIONS request. // If you want to allow other HTTP Methods, you can do that here. return new Response(null, { headers: { Allow: 'GET, HEAD, POST, OPTIONS', }, }) } } addEventListener('fetch', event => { const request = event.request const url = new URL(request.url) if (url.pathname.startsWith(proxyEndpoint)) { if (request.method === 'OPTIONS') { // Handle CORS preflight requests event.respondWith(handleOptions(request)) } else if ( request.method === 'GET' || request.method === 'HEAD' || request.method === 'POST' ) { // Handle requests to the API server event.respondWith(handleRequest(request)) } else { event.respondWith(async () => { return new Response(null, { status: 405, statusText: 'Method Not Allowed', }) }) } } else { // Serve demo page event.respondWith(rawHtmlResponse(demoPage)) } }) // We support the GET, POST, HEAD, and OPTIONS methods from any origin, // and accept the Content-Type header on requests. These headers must be // present on all responses to all CORS requests. In practice, this means // all responses to OPTIONS requests. const corsHeaders = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, HEAD, POST, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type', } // The URL for the remote third party API you want to fetch from // but does not implement CORS const apiurl = 'https://workers-tooling.cf/demos/demoapi' // The endpoint you want the CORS reverse proxy to be on const proxyEndpoint = '/corsproxy/' // The rest of this snippet for the demo page async function rawHtmlResponse(html) { return new Response(html, { headers: { 'content-type': 'text/html;charset=UTF-8', }, }) } const demoPage = ` <!DOCTYPE html> <html> <body> <h1>API GET without CORS Proxy</h1> <a target='_blank' href='https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Checking_that_the_fetch_was_successful'>Shows TypeError: Failed to fetch since CORS is misconfigured</a> <p id='noproxy-status'/> <code id='noproxy'>Waiting</code> <h1>API GET with CORS Proxy</h1> <p id='proxy-status'/> <code id='proxy'>Waiting</code> <h1>API POST with CORS Proxy + Preflight</h1> <p id='proxypreflight-status'/> <code id='proxypreflight'>Waiting</code> <script> let reqs = {}; reqs.noproxy = async () => { let response = await fetch('${apiurl}') return await response.json() } reqs.proxy = async () => { let response = await fetch(window.location.origin + '${proxyEndpoint}?apiurl=${apiurl}') return await response.json() } reqs.proxypreflight = async () => { const reqBody = { msg: "Hello world!" } let response = await fetch(window.location.origin + '${proxyEndpoint}?apiurl=${apiurl}', { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(reqBody), }) return await response.json() } (async () => { for (const [reqName, req] of Object.entries(reqs)) { try { let data = await req() document.getElementById(reqName).innerHTML = JSON.stringify(data) } catch (e) { document.getElementById(reqName).innerHTML = e } } })() </script> </body> </html>`

Country Code

Redirect a response based on the country code of the visitor

Copy into a Worker script:
async function handleRequest(request) { return redirect(request, 'subdomain') } addEventListener('fetch', event => { event.respondWith(handleRequest(event.request)) }) /** * Returns a redirect determined by the country code * @param {Request} request */ function redirect(request) { // The `cf-ipcountry` header is not supported in the previewer const country = request.headers.get('cf-ipcountry') const url = countryMap[country] return Response.redirect(url) } /** * A map of the url's to redirect to * @param {Object} countryMap */ const countryMap = { "US" : "https://example.com/us", "EU": "https://eu.example.com/" }

Debugging Tips

Send debug information in an errored response and to a logging service.

Copy into a Worker script:
const LOG_URL = 'https://log-service.example.com/' // Service setup up to receive logs async function handleRequest(event) { let response try { response = await fetch(event.request) if (!response.ok) { let body = await response.text() throw new Error( 'Bad response at origin. Status: ' + response.status + ' Body: ' + body.trim().substring(0, 10), //ensures is string that can be a header ) } } catch (err) { // Without event.waitUntil(), our fetch() to our logging service may // or may not complete. event.waitUntil(postLog(err)) const stack = JSON.stringify(err.stack) || err // Copy the response and initialize body to the stack trace response = new Response(stack, response) // Shove our rewritten URL into a header to find out what it was. response.headers.set('X-Debug-stack', stack) response.headers.set('X-Debug-err', err) } return response } addEventListener('fetch', event => { //Have any uncaught errors thrown go directly to origin event.passThroughOnException() event.respondWith(handleRequest(event)) }) function postLog(data) { return fetch(LOG_URL, { method: 'POST', body: data, }) }

Fetch HTML

Sends a request to a remote server, reads HTML from the response, then serves that HTML.

Copy into a Worker script:
async function handleRequest(request) { const init = { headers: { 'content-type': 'text/html;charset=UTF-8', }, } const response = await fetch(url, init) const results = await gatherResponse(response) return new Response(results, init) } addEventListener('fetch', event => { return event.respondWith(handleRequest(event.request)) }) /** * gatherResponse awaits and returns a response body as a string. * Use await gatherResponse(..) in an async function to get the response body * @param {Response} response */ async function gatherResponse(response) { const { headers } = response const contentType = headers.get('content-type') if (contentType.includes('application/json')) { return await response.json() } else if (contentType.includes('application/text')) { return await response.text() } else if (contentType.includes('text/html')) { return await response.text() } else { return await response.text() } } /** * Example someHost at url is set up to respond with HTML * Replace url with the host you wish to send requests to * */ const someHost = 'https://workers-tooling.cf/demos' const url = someHost + '/static/html'

Fetch JSON

Sends a GET request and reads in JSON from the response.

Copy into a Worker script:
async function handleRequest(request) { const init = { headers: { 'content-type': type, }, } const responses = await Promise.all([fetch(url1, init), fetch(url2, init)]) const results = await Promise.all([gatherResponse(responses[0]), gatherResponse(responses[1])]) return new Response(results, init) } addEventListener('fetch', event => { return event.respondWith(handleRequest(event.request)) }) /** * gatherResponse awaits and returns a response body as a string. * Use await gatherResponse(..) in an async function to get the response body * @param {Response} response */ async function gatherResponse(response) { const { headers } = response const contentType = headers.get('content-type') if (contentType.includes('application/json')) { return await response.json() } else if (contentType.includes('application/text')) { return await response.text() } else if (contentType.includes('text/html')) { return await response.text() } else { return await response.text() } } /** * Example someHost is set up to return JSON responses * Replace url1 and url2 with the hosts you wish to * send requests to * @param {string} url the URL to send the request to */ const someHost = 'https://workers-tooling.cf/demos' const url1 = someHost + '/requests/json' const url2 = someHost + '/requests/json' const type = 'application/json;charset=UTF-8'

HTTP/2 Server Push

Push static assests to a client’s browser without waiting for HTML to render

Copy into a Worker script:
async function handleRequest(request) { // If request is for test.css just serve the raw CSS if (/test.css$/.test(request.url)) { return new Response(CSS, { headers: { 'content-type': 'text/css', }, }) } else { // serve raw HTML using HTTP/2 for the CSS file return new Response(HTML, { headers: { 'content-type': 'text/html', Link: '</http2_push/h2p/test.css>; rel=preload;', }, }) } } addEventListener('fetch', event => { event.respondWith(handleRequest(event.request)) }) const CSS = `body { color: red; }` const HTML = ` <!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>Server push test</title> <link rel="stylesheet" href="http2_push/h2p/test.css"> </head> <body> <h1>Server push test page</h1> </body> </html> `

Modify Request Property

Recommended practice for forming a request based off the incoming request. First, takes in the incoming request then modifies specific properties like POST body, redirect, and the Cloudflare specific property cf and runs the fetch.

Copy into a Worker script:
async function handleRequest(request) { /** * Best practice is to only assign new properties on the request * object (i.e. RequestInit props) through either a method or the constructor */ let newRequestInit = { // Change method method: 'POST', // Change body body: JSON.stringify({ bar: 'foo' }), // Change the redirect mode. redirect: 'follow', //Change headers, note this method will erase existing headers headers: { 'Content-Type': 'application/json', }, // Change a Cloudflare feature on the outbound response cf: { apps: false }, } // Change URL let url = someUrl // Change just the host url = new URL(url) url.hostname = someHost // Best practice is to always use the original request to construct the new request // thereby cloning all the attributes, applying the URL also requires a constructor // since once a Request has been constructed, its URL is immutable. const newRequest = new Request(url, new Request(request, newRequestInit)) // Set headers using method newRequest.headers.set('X-Example', 'bar') newRequest.headers.set('Content-Type', 'application/json') try { return await fetch(newRequest) } catch (e) { return new Response(JSON.stringify({ error: e.message }), { status: 500 }) } } addEventListener('fetch', event => { event.respondWith(handleRequest(event.request)) }) /** * Example someHost is set up to return raw JSON * @param {string} someUrl the URL to send the request to, since we are setting hostname too only path is applied * @param {string} someHost the host the request will resolve too */ const someHost = 'example.com' const someUrl = 'https://foo.example.com/api.js'

Modify Response

Recommended practice for mutating a fetched response. First, fetches a request then modifies specific properties which are immutable: status, statusText, headers and body.

Copy into a Worker script:
async function handleRequest(request) { /** * Best practice is to only assign properties on the response * object (i.e. ResponseInit props) through either a method or the constructor * since properties are immutable */ let originalResponse = await fetch(request) let originalBody = await originalResponse.json() // Change status and statusText // Make sure to pass in originalResponse to preserving all parts // of the original response except the part we want to update. let response = new Response(originalResponse, { status: 500, statusText: 'some message' }) // Change response body by adding the foo prop let body = JSON.stringify({ foo: 'bar', ...originalBody }) response = new Response(body, response) // Add a header using set method response.headers.set('foo', 'bar') // Set destination header to the value of the source header if (response.headers.has(headerNameSrc)) { response.headers.set(headerNameDst, response.headers.get(headerNameSrc)) console.log( `Response header "${headerNameDst}" was set to "${response.headers.get(headerNameDst)}"`, ) } return response } addEventListener('fetch', event => { event.respondWith(handleRequest(event.request)) }) /** * @param {string} headerNameSrc the header to get the new value from * @param {string} headerNameDst the header to set based off of value in src */ const headerNameSrc = 'foo'//'Orig-Header' const headerNameDst = 'Last-Modified'

Read POST Data

Serves an HTML form, then reads POSTs from that form data. Can also be used to read JSON or other POST data from an incoming request.

Copy into a Worker script:
async function handlePostRequest(request) { let reqBody = await readRequestBody(request) let retBody = `The request body sent in was ${reqBody}` return new Response(retBody) } async function handleRequest(request) { let retBody = `The request was a GET ` return new Response(retBody) } addEventListener('fetch', event => { const { request } = event const { url } = request if (url.includes('form')) { return event.respondWith(rawHtmlResponse(someForm)) } if (request.method === 'POST') { return event.respondWith(handlePostRequest(request)) } else if (request.method === 'GET') { return event.respondWith(handleRequest(request)) } }) /** * rawHtmlResponse delievers a response with HTML inputted directly * into the worker script * @param {string} html */ async function rawHtmlResponse(html) { const init = { headers: { 'content-type': 'text/html;charset=UTF-8', }, } return new Response(html, init) } /** * readRequestBody reads in the incoming request body * Use await readRequestBody(..) in an async function to get the string * @param {Request} request the incoming request to read from */ async function readRequestBody(request) { const { headers } = request const contentType = headers.get('content-type') if (contentType.includes('application/json')) { const body = await request.json() return JSON.stringify(body) } else if (contentType.includes('application/text')) { const body = await request.text() return body } else if (contentType.includes('text/html')) { const body = await request.text() return body } else if (contentType.includes('form')) { const formData = await request.formData() let body = {} for (let entry of formData.entries()) { body[entry[0]] = entry[1] } return JSON.stringify(body) } else { let myBlob = await request.blob() var objectURL = URL.createObjectURL(myBlob) return objectURL } } const someForm = ` <!DOCTYPE html> <html> <body> <h1>Hello World</h1> <p>This is all generated using a Worker</p> <form action="/demos/requests" method="post"> <div> <label for="say">What do you want to say?</label> <input name="say" id="say" value="Hi"> </div> <div> <label for="to">To who?</label> <input name="to" id="to" value="Mom"> </div> <div> <button>Send my greetings</button> </div> </form> </body> </html>

Post JSON

Sends a POST request with JSON data from the Workers script.

Copy into a Worker script:
async function handleRequest(request) { const init = { body: JSON.stringify(body), method: 'POST', headers: { 'content-type': 'application/json;charset=UTF-8', }, } const response = await fetch(url, init) const results = await gatherResponse(response) return new Response(results, init) } addEventListener('fetch', event => { return event.respondWith(handleRequest(event.request)) }) /** * gatherResponse awaits and returns a response body as a string. * Use await gatherResponse(..) in an async function to get the response body * @param {Response} response */ async function gatherResponse(response) { const { headers } = response const contentType = headers.get('content-type') if (contentType.includes('application/json')) { return await response.json() } else if (contentType.includes('application/text')) { return await response.text() } else if (contentType.includes('text/html')) { return await response.text() } else { return await response.text() } } /** * Example someHost is set up to take in a JSON request * Replace url with the host you wish to send requests to * @param {string} url the URL to send the request to * @param {BodyInit} body the JSON data to send in the request */ const someHost = 'https://workers-tooling.cf/demos' const url = someHost + '/requests/json' const body = { results: ['default data to send'], errors: null, msg: 'I sent this to the fetch', }

Data Loss Prevention

Prevent access to personal and sensitive data by inspecting response data from an origin server. In this example, sensitive data is defined by regexes for email, UK mobile number, or credit card number. If a match is detected, trigger a data breach alert and respond with either a block or the data stripped from the response.

Copy into a Worker script:
const SOME_HOOK_SERVER = 'https://webhook.flow-wolf.io/hook' const DEBUG = true /** * Define personal data with regular expressions * Respond with block if credit card data, and strip * emails and phone numbers from the response * Execution will be limited to MIME type "text/*" */ async function handleRequest(request) { let response = await fetch(request) // Return origin responst, if response wasn't text let contentType = response.headers.get('content-type') if (!contentType.toLowerCase().includes('text/')) { return response } let text = await response.text() text = DEBUG ? // for testing only - replace the response from the origin with an email text.replace('You may use this', 'me@example.com may use this') : text const sensitiveRegexsMap = { email: String.raw`\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b`, phone: String.raw`\b07\d{9}\b`, creditCard: String.raw`\b(?:4[0-9]{12}(?:[0-9]{3})?|(?:5[1-5][0-9]{2}|222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|6(?:011|5[0-9]{2})[0-9]{12}|(?:2131|1800|35\d{3})\d{11})\b`, } for (const kind in sensitiveRegexsMap) { let sensitiveRegex = new RegExp(sensitiveRegexsMap[kind], 'ig') let match = await sensitiveRegex.test(text) if (match) { // alert a data breach by posting to a webhook server await postDataBreach(request) // respond with a block if credit card, else replace // senstive text with *'s return kind === 'creditCard' ? new Response(kind + ' found\nForbidden\n', { status: 403, statusText: 'Forbidden', }) : new Response(text.replace(sensitiveRegex, '**********'), response) } } return new Response(text, response) } addEventListener('fetch', event => { event.respondWith(handleRequest(event.request)) }) async function postDataBreach(request) { let trueClientIp = request.headers.get('cf-connecting-ip') let epoch = new Date().getTime() const body = { ip: trueClientIp, time: epoch, request: request, } const init = { body: JSON.stringify(body), method: 'POST', headers: { 'content-type': 'application/json;charset=UTF-8', }, } return await fetch(SOME_HOOK_SERVER, init) }

Redirect

Redirect a request by sending a 301 or 302 HTTP response

Copy into a Worker script:
async function handleRequest(request) { return Response.redirect(someURLToRedirectTo, code) } addEventListener('fetch', async event => { event.respondWith(handleRequest(event.request)) }) /** * Example Input * @param {Request} url where to redirect the response * @param {number?=301|302} type permanent or temporary redirect */ const someURLToRedirectTo = 'https://www.google.com' const code = 301

Rewrite URLs

Rewrite URLs using HTML Rewriter

Copy into a Worker script:
async function handleRequest(req) { const res = await fetch(req) return rewriter.transform(res) } const rewriter = new HTMLRewriter() .on('a', new AttributeRewriter('href')) .on('img', new AttributeRewriter('src')) class AttributeRewriter { constructor(attributeName) { this.attributeName = attributeName } element(element) { const attribute = element.getAttribute(this.attributeName) if (attribute) { element.setAttribute( this.attributeName, attribute.replace('myolddomain.com', 'mynewdomain.com') ) } } } addEventListener('fetch', event => { event.respondWith(handleRequest(event.request)) })

Send Raw HTML

Delivers an HTML page from HTML directly in the Worker script.

Copy into a Worker script:
async function handleRequest(request) { const init = { headers: { 'content-type': 'text/html;charset=UTF-8', }, } return new Response(someHTML, init) } addEventListener('fetch', event => { return event.respondWith(handleRequest(event.request)) }) const someHTML = `<!DOCTYPE html> <html> <body> <h1>Hello World</h1> <p>This is all generated using a Worker</p> <iframe width="560" height="315" src="https://www.youtube.com/embed/dQw4w9WgXcQ" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen ></iframe> </body> </html> `

Send Raw JSON

Renders a response of type application/json to the client

Copy into a Worker script:
async function handleRequest(request) { const init = { headers: { 'content-type': 'application/json;charset=UTF-8', }, } return new Response(JSON.stringify(someJSON), init) } addEventListener('fetch', event => { return event.respondWith(handleRequest(event.request)) }) const someJSON = { result: ['some', 'results'], errors: null, msg: 'this is some random json', }

Signed Request/Response

Check signatures of requests and sign responses with a private key

Copy into a Worker script:
// NOTE Requires ESM through webpack project type const crypto = require('crypto') const SECRET = 'SECRET_KEY' async function handleRequest(request) { let signed = await checkSignature(request) if (signed) { let responseBody = 'Hello worker!' return await signResponse(responseBody, new Response(responseBody)) } else { return new Response('Request not signed', { status: 400 }) } } addEventListener('fetch', event => { console.log(createHexSignature('asd')) event.respondWith(handleRequest(event.request)) }) async function createHexSignature(requestBody) { let hmac = crypto.createHmac('sha256', SECRET) hmac.update(requestBody) return hmac.digest('hex') } async function checkSignature(request) { // hash request with secret key let expectedSignature = await createHexSignature(await request.text()) let actualSignature = await request.headers.get('signature') // check that hash matches signature return expectedSignature === actualSignature } async function signResponse(responseBody, response) { // create signature const signature = await createHexSignature(responseBody) response.headers.set('signature', signature) //add header with signature return response }

Block on TLS Version

Inspects the incoming request’s TLS version and blocks if under TLSv1.2.

Copy into a Worker script:
async function handleRequest(request) { try { let tlsVersion = request.cf.tlsVersion // Allow only TLS versions 1.2 and 1.3 if (tlsVersion != 'TLSv1.2' && tlsVersion != 'TLSv1.3') { return new Response('Please use TLS version 1.2 or higher.', { status: 403, }) } return fetch(request) } catch (err) { console.error( 'request.cf does not exist in the previewer, only in production', ) return new Response('Error in workers script' + err.message, { status: 500, }) } } addEventListener('fetch', event => { event.respondWith(handleRequest(event.request)) })

Featured Projects

The gallery is actively growing. The template creator allows you to share templates. Host a public repo, and then run wrangler generate https://github.com/. For archived recipes, see the old docs.