Infrastructure & Networking

What is a Cloudflare Worker?

A Cloudflare Worker is a serverless function that executes JavaScript, or other languages compiled to WebAssembly, at Cloudflare’s global edge network, processing HTTP requests and responses at the edge node closest to each user rather than at a centralised origin server. Workers intercept incoming requests to a domain, execute custom logic, reading headers, checking paths, modifying responses, making fetch requests, and return responses directly from the edge without necessarily forwarding requests to an origin server.

Cloudflare Workers are built on the V8 JavaScript engine, the same engine that powers Chrome and Node.js, running in isolated lightweight execution environments called isolates. Unlike traditional serverless functions, AWS Lambda, Google Cloud Functions, which run in containerised virtual machines with cold start times measured in hundreds of milliseconds Workers use isolate-based execution that achieves cold start times under one millisecond. This near-zero cold start time makes Workers practical for latency-sensitive use cases, including redirect management: where every millisecond of processing overhead affects user experience.

Workers are deployed globally to all of Cloudflare’s 200+ edge locations simultaneously, a single Worker deployment is immediately available at every edge node worldwide. A request from a user in Tokyo is handled by the Worker running at Cloudflare’s Tokyo edge. A request from a user in London is handled by the same Worker code running at Cloudflare’s London edge. This automatic global distribution eliminates the geographic performance variation of single-region origin deployments.

How Cloudflare Workers execute

Workers operate through a specific execution model, intercepting requests at the edge and processing them through the Worker’s event handler.

The fetch event handler: the fundamental Worker execution mechanism. A Worker registers a fetch event listener that fires for every HTTP request matching the Worker’s route configuration. The handler receives a Request object containing all request information, URL, method, headers, body, and must return a Response object representing the HTTP response to send to the client.

addEventListener('fetch', event => {
    event.respondWith(handleRequest(event.request))
})

async function handleRequest(request) {
    // Process the request and return a response
    return new Response('Hello from the edge', {
        status: 200,
        headers: { 'Content-Type': 'text/plain' }
    })
}
addEventListener('fetch', event => {
    event.respondWith(handleRequest(event.request))
})

async function handleRequest(request) {
    // Process the request and return a response
    return new Response('Hello from the edge', {
        status: 200,
        headers: { 'Content-Type': 'text/plain' }
    })
}
addEventListener('fetch', event => {
    event.respondWith(handleRequest(event.request))
})

async function handleRequest(request) {
    // Process the request and return a response
    return new Response('Hello from the edge', {
        status: 200,
        headers: { 'Content-Type': 'text/plain' }
    })
}

The modern Workers syntax uses ES module format with an exported default object rather than addEventListener, the module syntax is cleaner and enables better static analysis:

export default {
    async fetch(request, env, ctx) {
        return new Response('Hello from the edge', {
            status: 200,
            headers: { 'Content-Type': 'text/plain' }
        })
    }
}
export default {
    async fetch(request, env, ctx) {
        return new Response('Hello from the edge', {
            status: 200,
            headers: { 'Content-Type': 'text/plain' }
        })
    }
}
export default {
    async fetch(request, env, ctx) {
        return new Response('Hello from the edge', {
            status: 200,
            headers: { 'Content-Type': 'text/plain' }
        })
    }
}

V8 isolates: Workers execute in V8 isolates, lightweight JavaScript execution contexts that share a single V8 engine instance rather than running in separate processes or containers. Traditional serverless functions, Lambda, Cloud Functions, spin up a container or process for each execution context, a heavyweight operation requiring hundreds of milliseconds for cold starts. V8 isolates share the already-running V8 engine, creating a new execution context requires only a few microseconds.

A single Cloudflare edge server runs thousands of Worker isolates simultaneously, serving requests from many different Workers and many different customers on the same hardware. Isolates are sandboxed from each other, one Worker cannot access another Worker’s memory or variables, providing security isolation despite the shared infrastructure.

Request and response Web APIs: Workers use standard Web API interfaces, the same interfaces available in browsers, for working with requests and responses. The Request, Response, Headers, URL, and fetch APIs are all available. This browser API compatibility means Workers code is familiar to web developers and portable, code written for Workers can often run in browsers and vice versa.

What Workers can do

Workers have access to a comprehensive set of capabilities that enable sophisticated edge logic.

Redirect responses: the most directly relevant capability for redirect management. Workers return redirect responses using the Response.redirect() static method or by constructing a Response with a 3xx status code and Location header:

// Using Response.redirect()
return Response.redirect('https://new-destination.com/path', 301)

// Constructing manually
return new Response(null, {
    status: 301,
    headers: { 'Location': 'https://new-destination.com/path' }
})
// Using Response.redirect()
return Response.redirect('https://new-destination.com/path', 301)

// Constructing manually
return new Response(null, {
    status: 301,
    headers: { 'Location': 'https://new-destination.com/path' }
})
// Using Response.redirect()
return Response.redirect('https://new-destination.com/path', 301)

// Constructing manually
return new Response(null, {
    status: 301,
    headers: { 'Location': 'https://new-destination.com/path' }
})

Workers can implement complex redirect logic, matching URL patterns, reading request headers, checking geographic location, and return appropriate redirect responses for each case.

Outbound fetch requests: Workers can make HTTP requests to external services using the standard fetch() API. An outbound fetch from a Worker can query a redirect rule database, verify authentication, call an API, or fetch content to include in the response. Outbound fetches from Workers execute at the edge, fetch requests go to the nearest instance of the target service.

// Fetch redirect rules from an external API
const rulesResponse = await fetch('https://api.redirect-service.com/rules')
const rules = await rulesResponse.json()
// Fetch redirect rules from an external API
const rulesResponse = await fetch('https://api.redirect-service.com/rules')
const rules = await rulesResponse.json()
// Fetch redirect rules from an external API
const rulesResponse = await fetch('https://api.redirect-service.com/rules')
const rules = await rulesResponse.json()

Cloudflare KV, key-value storage: Workers can read from and write to Cloudflare KV, a globally distributed eventually consistent key-value store. KV data is replicated to all edge locations, reads are served from the nearest replica with minimal latency. KV is ideal for storing redirect rules, a Worker handling redirect requests looks up the source URL in KV and returns the stored destination:

// Look up redirect destination in KV
const destination = await env.REDIRECTS.get(url.pathname)
if (destination) {
    return Response.redirect(destination, 301)
}
// Look up redirect destination in KV
const destination = await env.REDIRECTS.get(url.pathname)
if (destination) {
    return Response.redirect(destination, 301)
}
// Look up redirect destination in KV
const destination = await env.REDIRECTS.get(url.pathname)
if (destination) {
    return Response.redirect(destination, 301)
}

Cloudflare Durable Objects: strongly consistent storage objects that maintain state across requests. Where KV is eventually consistent, writes may take time to propagate globally, Durable Objects provide strong consistency guarantees. Appropriate for redirect counters, analytics aggregation, and other stateful operations that require consistency.

Cloudflare R2 object storage: Workers can read from and write to Cloudflare R2, S3-compatible object storage. R2 is accessible from Workers for serving files, storing large redirect databases, and managing content.

Request modification: Workers can modify incoming requests before forwarding them to the origin, adding or removing headers, changing the URL, modifying the request body. A Worker might add authentication headers, normalise URL formats, or strip tracking parameters before the request reaches the origin.

// Add authentication header before forwarding to origin
const modifiedRequest = new Request(request, {
    headers: {
        ...Object.fromEntries(request.headers),
        'X-Internal-Auth': 'secret-token'
    }
})
return fetch(modifiedRequest)
// Add authentication header before forwarding to origin
const modifiedRequest = new Request(request, {
    headers: {
        ...Object.fromEntries(request.headers),
        'X-Internal-Auth': 'secret-token'
    }
})
return fetch(modifiedRequest)
// Add authentication header before forwarding to origin
const modifiedRequest = new Request(request, {
    headers: {
        ...Object.fromEntries(request.headers),
        'X-Internal-Auth': 'secret-token'
    }
})
return fetch(modifiedRequest)

Response modification: Workers can intercept responses from the origin and modify them before returning to the client, adding security headers, modifying content, injecting scripts. A Worker might add HSTS headers, insert analytics scripts, or transform HTML content at the edge.

Geographic and network information: every Worker request includes geographic and network data about the connecting client, country, city, region, ASN, timezone. This data enables geographic redirect logic at the edge without origin server involvement:

const country = request.cf.country
if (country === 'DE') {
    return Response.redirect('https://example.com/de/', 302)
}
const country = request.cf.country
if (country === 'DE') {
    return Response.redirect('https://example.com/de/', 302)
}
const country = request.cf.country
if (country === 'DE') {
    return Response.redirect('https://example.com/de/', 302)
}

A/B testing and experimentation: Workers can implement traffic splitting, randomly routing different percentages of traffic to different destinations for A/B testing. The split logic executes at the edge, no origin involvement required.

Workers for redirect management

Workers are particularly well-suited to redirect management, the combination of edge execution, KV storage, and powerful URL matching creates an optimal platform for fast, scalable redirect infrastructure.

Pattern-based redirect matching: Workers implement sophisticated URL pattern matching using JavaScript’s full string manipulation and regular expression capabilities. Complex patterns, path prefixes, parameter matching, domain-specific rules, are straightforward to implement in JavaScript:

export default {
    async fetch(request, env) {
        const url = new URL(request.url)
        
        // Exact path match
        if (url.pathname === '/old-page') {
            return Response.redirect('https://example.com/new-page', 301)
        }
        
        // Prefix match
        if (url.pathname.startsWith('/old-blog/')) {
            const newPath = url.pathname.replace('/old-blog/', '/blog/')
            return Response.redirect(`https://example.com${newPath}`, 301)
        }
        
        // Regex match
        const match = url.pathname.match(/^\/products\/(\d+)$/)
        if (match) {
            return Response.redirect(
                `https://example.com/products/item-${match[1]}`, 
                301
            )
        }
        
        return fetch(request)
    }
}
export default {
    async fetch(request, env) {
        const url = new URL(request.url)
        
        // Exact path match
        if (url.pathname === '/old-page') {
            return Response.redirect('https://example.com/new-page', 301)
        }
        
        // Prefix match
        if (url.pathname.startsWith('/old-blog/')) {
            const newPath = url.pathname.replace('/old-blog/', '/blog/')
            return Response.redirect(`https://example.com${newPath}`, 301)
        }
        
        // Regex match
        const match = url.pathname.match(/^\/products\/(\d+)$/)
        if (match) {
            return Response.redirect(
                `https://example.com/products/item-${match[1]}`, 
                301
            )
        }
        
        return fetch(request)
    }
}
export default {
    async fetch(request, env) {
        const url = new URL(request.url)
        
        // Exact path match
        if (url.pathname === '/old-page') {
            return Response.redirect('https://example.com/new-page', 301)
        }
        
        // Prefix match
        if (url.pathname.startsWith('/old-blog/')) {
            const newPath = url.pathname.replace('/old-blog/', '/blog/')
            return Response.redirect(`https://example.com${newPath}`, 301)
        }
        
        // Regex match
        const match = url.pathname.match(/^\/products\/(\d+)$/)
        if (match) {
            return Response.redirect(
                `https://example.com/products/item-${match[1]}`, 
                301
            )
        }
        
        return fetch(request)
    }
}

KV-backed redirect databases: storing redirect rules in Cloudflare KV enables dynamic redirect management, rules can be added, updated, and deleted through the KV API without redeploying Worker code. A redirect management platform stores rules as KV entries, source URL as key, destination and status code as value, and the Worker looks up each incoming request path in KV:

export default {
    async fetch(request, env) {
        const url = new URL(request.url)
        const key = url.hostname + url.pathname
        
        const redirectData = await env.REDIRECTS.get(key, { type: 'json' })
        if (redirectData) {
            return Response.redirect(
                redirectData.destination, 
                redirectData.statusCode
            )
        }
        
        return fetch(request)
    }
}
export default {
    async fetch(request, env) {
        const url = new URL(request.url)
        const key = url.hostname + url.pathname
        
        const redirectData = await env.REDIRECTS.get(key, { type: 'json' })
        if (redirectData) {
            return Response.redirect(
                redirectData.destination, 
                redirectData.statusCode
            )
        }
        
        return fetch(request)
    }
}
export default {
    async fetch(request, env) {
        const url = new URL(request.url)
        const key = url.hostname + url.pathname
        
        const redirectData = await env.REDIRECTS.get(key, { type: 'json' })
        if (redirectData) {
            return Response.redirect(
                redirectData.destination, 
                redirectData.statusCode
            )
        }
        
        return fetch(request)
    }
}

Wildcard redirect handling: Workers implement wildcard redirect patterns by checking multiple KV keys or applying pattern matching logic. A wildcard rule for /old-section/*/new-section/* can be stored as a prefix rule and matched against incoming request paths:

// Check for exact match first, then prefix matches
const exactMatch = await env.REDIRECTS.get(url.pathname)
if (exactMatch) return Response.redirect(exactMatch, 301)

// Check prefix rules stored separately
const prefixRules = await env.PREFIX_RULES.get('rules', { type: 'json' })
for (const rule of prefixRules) {
    if (url.pathname.startsWith(rule.prefix)) {
        const destination = url.pathname.replace(rule.prefix, rule.target)
        return Response.redirect(destination, 301)
    }
}
// Check for exact match first, then prefix matches
const exactMatch = await env.REDIRECTS.get(url.pathname)
if (exactMatch) return Response.redirect(exactMatch, 301)

// Check prefix rules stored separately
const prefixRules = await env.PREFIX_RULES.get('rules', { type: 'json' })
for (const rule of prefixRules) {
    if (url.pathname.startsWith(rule.prefix)) {
        const destination = url.pathname.replace(rule.prefix, rule.target)
        return Response.redirect(destination, 301)
    }
}
// Check for exact match first, then prefix matches
const exactMatch = await env.REDIRECTS.get(url.pathname)
if (exactMatch) return Response.redirect(exactMatch, 301)

// Check prefix rules stored separately
const prefixRules = await env.PREFIX_RULES.get('rules', { type: 'json' })
for (const rule of prefixRules) {
    if (url.pathname.startsWith(rule.prefix)) {
        const destination = url.pathname.replace(rule.prefix, rule.target)
        return Response.redirect(destination, 301)
    }
}

Multi-domain redirect management: a single Worker can handle redirects for multiple domains, checking the request’s Host header to apply domain-specific rules. This multi-tenancy enables redirect management platforms to serve many customer domains from a single Worker deployment:

const domain = new URL(request.url).hostname
const domainRules = await env.REDIRECTS.get(domain, { type: 'json' })

if (domainRules) {
    const destination = domainRules[url.pathname]
    if (destination) {
        return Response.redirect(destination, 301)
    }
}
const domain = new URL(request.url).hostname
const domainRules = await env.REDIRECTS.get(domain, { type: 'json' })

if (domainRules) {
    const destination = domainRules[url.pathname]
    if (destination) {
        return Response.redirect(destination, 301)
    }
}
const domain = new URL(request.url).hostname
const domainRules = await env.REDIRECTS.get(domain, { type: 'json' })

if (domainRules) {
    const destination = domainRules[url.pathname]
    if (destination) {
        return Response.redirect(destination, 301)
    }
}

Workers pricing and limits

Understanding Workers pricing and limits is important for redirect management at scale.

Free tier: Cloudflare’s free Workers tier includes 100,000 requests per day with CPU time limited to 10 milliseconds per request. The free tier is suitable for development and low-traffic redirect implementations.

Workers Paid, $5 per month: includes 10 million requests per month with additional requests billed per million. CPU time limit increases to 30 seconds per request. The paid tier is appropriate for production redirect management with moderate to high traffic.

CPU time vs wall clock time: Workers billing and limits are based on CPU time, the time the Worker’s JavaScript code is actively executing, not wall clock time, the total time from request receipt to response delivery including network requests and KV lookups. A Worker that makes a KV lookup, which might take 10 milliseconds wall clock time, consumes only a fraction of that in CPU time while waiting for the KV response. This distinction makes Workers efficient for I/O-bound operations like redirect rule lookups.

Memory limit: 128 MB per isolate. Sufficient for redirect rule matching but requires careful management for Workers that cache large rule sets in memory.

Script size limit: 1 MB for Worker scripts. Redirect management Workers are typically small, well within this limit.

Workers vs other redirect implementation approaches

Comparing Workers to alternative redirect implementation approaches clarifies when Workers is the appropriate choice.

Workers vs web server redirects: web server redirect rules, Nginx, Apache, execute on the origin server. Workers execute at the nearest edge node, 10-30ms from any user globally vs 100-300ms round trip to a regional origin. Workers win on latency for geographically distributed traffic.

Workers vs Cloudflare Page Rules / Redirect Rules: Cloudflare’s built-in redirect rules are simpler to configure but less flexible. Redirect Rules handle common patterns without code. Workers handle complex logic, database-driven redirects, and custom matching that Redirect Rules cannot express.

Workers vs dedicated redirect platforms: dedicated redirect management platforms, including those built on Workers, provide management interfaces, analytics, monitoring, and team collaboration features that a custom Worker implementation requires building from scratch. For most organisations a dedicated platform provides better value than custom Worker development.

Related terms

Related terms

Ready to keep every link alive?

Ready to keep every link alive?

Ready to keep every link alive?