Netlify Edge Functions aren’t just a way to run code closer to your users; they fundamentally change when your code executes in the request lifecycle, allowing you to mutate responses after static assets have been served.
Let’s see this in action. Imagine you have a static index.html file served from Netlify’s CDN.
<!-- public/index.html -->
<!DOCTYPE html>
<html>
<head>
<title>My Static Site</title>
</head>
<body>
<h1>Hello from the Edge!</h1>
<p>This content will be modified.</p>
</body>
</html>
Now, we deploy an Edge Function that intercepts requests and adds a custom header.
// netlify/edge-functions/modify-response.js
export default function handler(request, context) {
const url = new URL(request.url);
// Only intercept requests for the index page
if (url.pathname === '/') {
const response = context.rewrite('/index.html'); // Serve the static file
response.headers.set('X-Modified-By-Edge', 'true');
response.headers.set('X-Edge-Timestamp', Date.now().toString());
return response;
}
// For other requests, let them pass through normally
return context.next();
}
When you visit your Netlify site, the index.html file is served from the CDN. However, before it’s sent to your browser, the Edge Function runs. It intercepts the request, tells Netlify to serve /index.html (which it already has cached at the edge), and then modifies that response by adding X-Modified-By-Edge: true and a timestamp header. Your browser receives the index.html content, but with these new headers.
The core problem Edge Functions solve is the inability to dynamically alter responses after static assets are served by a CDN. Traditionally, if you wanted to add a header or modify content based on user location or A/B testing, you’d need a serverless function or a backend service that runs after the CDN, adding latency. Edge Functions operate within the CDN’s request pipeline, allowing for near-instantaneous modifications.
Internally, Netlify Edge Functions leverage Deno’s runtime. When a request hits an edge location, Netlify checks if an Edge Function is configured for that path. If so, it executes the JavaScript function in a sandboxed Deno environment. This function receives the request object and a context object. The context is key: context.next() passes the request along to the next handler (which could be another Edge Function or the default asset serving). context.rewrite(path) tells Netlify to serve content from path but still allows the current function to modify the response before it’s sent to the client. This rewrite mechanism is how you can serve static assets while still applying dynamic logic.
The primary levers you control are the request path matching and the manipulation of the Response object returned by your function. You can read request headers, query parameters, and the request URL to make decisions. You can then set or delete response headers, redirect the user using Response.redirect(), or even replace the response body entirely (though this is less common for typical edge use cases).
A common misconception is that Edge Functions are just like serverless functions but "faster." The critical difference is their position in the request lifecycle and their primary purpose: to manipulate the response from the CDN. You’re not typically running complex business logic or database queries here; you’re augmenting or conditionally altering what the CDN is already serving. This makes them ideal for tasks like A/B testing variations of static content, personalizing headers based on geolocation, or implementing simple redirects without hitting a full serverless function.
The next frontier is understanding how to manage state or more complex data across multiple Edge Function invocations within a single request.