When Static Isn't Enough: Adding Edge Functions Without Losing the Benefits
You built a static site. It’s fast. It’s cheap to host. It deploys in seconds. Lighthouse loves it. Your ops burden is effectively zero.
Then the requirements change.
A client needs a contact form that sends email and writes to a CRM. The marketing team wants geo-based pricing on the landing page. A partner integration requires validating a webhook signature server-side. None of these are complex features, but they all need code that runs on a server — and you don’t have one.
The instinct is to reach for a full backend. Spin up an Express server. Add a database. Deploy it somewhere. Suddenly you’re back to managing infrastructure, and the simplicity that made the static approach attractive is gone.
There’s a middle path. Edge functions let you add targeted server-side logic without abandoning the static architecture. The trick is knowing where the line is.
What Edge Functions Actually Are
An edge function is a small piece of server-side code that runs on a CDN node close to the user, rather than on a central origin server. When a request hits your function, it executes on whichever node is geographically nearest — the same way your static HTML is served from the nearest CDN node.
The key properties:
Cold starts are minimal. Edge runtimes like Cloudflare Workers, Vercel Edge Functions, and Netlify Edge Functions use V8 isolates, not containers. Startup time is typically under 10ms, compared to 200–500ms for traditional serverless functions (AWS Lambda, Google Cloud Functions).
They run close to the user. A function deployed to Cloudflare’s network runs in 300+ locations globally. A traditional serverless function runs in one or two regions. For latency-sensitive operations, this matters.
They have constraints. Most edge runtimes limit execution time (typically 10–50ms for Cloudflare Workers on the free plan, longer on paid tiers), don’t support all Node.js APIs, and have limited access to persistent storage. You can’t run a full ORM or connect to a Postgres database directly from most edge runtimes — at least not without adapter libraries.
These constraints aren’t bugs. They’re guardrails that keep you in the territory where edge functions make sense.
The Right Use Cases
Edge functions earn their place when you need server-side logic that is small, fast, and stateless. Here’s what fits naturally:
Form handling. Accept a POST request, validate the input, forward it to an email API or CRM webhook, return a response. No database. No session. Just a relay with validation.
// Cloudflare Worker — contact form handler
export default {
async fetch(request: Request): Promise<Response> {
if (request.method !== "POST") {
return new Response("Method not allowed", { status: 405 });
}
const body = await request.json();
const { name, email, message } = body;
if (!name || !email || !message) {
return new Response(JSON.stringify({ error: "All fields required" }), {
status: 400,
headers: { "Content-Type": "application/json" },
});
}
await fetch("https://api.resend.com/emails", {
method: "POST",
headers: {
Authorization: `Bearer ${env.RESEND_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
from: "site@yourdomain.com",
to: "hello@yourdomain.com",
subject: `Contact: ${name}`,
text: `From: ${name} (${email})\n\n${message}`,
}),
});
return new Response(JSON.stringify({ success: true }), {
headers: { "Content-Type": "application/json" },
});
},
};
This replaces a third-party form service. You own the code. No monthly fee. No vendor lock-in. The function runs in under 50ms and costs fractions of a cent per invocation.
Webhook validation. A partner sends a webhook to your site. You need to verify the signature before processing the payload. This requires a secret key and crypto operations that can’t happen in the browser.
async function verifyWebhookSignature(request: Request, secret: string): Promise<boolean> {
const signature = request.headers.get("x-signature-256") || "";
const body = await request.text();
const key = await crypto.subtle.importKey(
"raw",
new TextEncoder().encode(secret),
{ name: "HMAC", hash: "SHA-256" },
false,
["sign"]
);
const expected = await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(body));
const expectedHex = [...new Uint8Array(expected)]
.map((b) => b.toString(16).padStart(2, "0"))
.join("");
return signature === expectedHex;
}
Geo-based content. The edge runtime knows where the request is coming from. Showing different pricing, redirecting to a regional subdomain, or localizing content based on geography are all natural fits.
API proxying with auth. Your static site needs to call a third-party API that requires a secret key. Instead of exposing the key in client-side JavaScript, proxy the request through an edge function that injects the key server-side.
The Wrong Use Cases
Knowing when not to use edge functions matters more than knowing when to use them.
Don’t use them as a full backend. If you’re writing CRUD operations, managing sessions, running database migrations, or building an authentication system — you don’t need edge functions. You need a backend. That’s fine. But recognize that you’ve crossed the line from “static site with a few server-side touches” to “application with a backend.”
Don’t use them for heavy computation. Image processing, PDF generation, data aggregation over large datasets — these operations exceed the execution time and memory limits of most edge runtimes. Use a traditional serverless function or a dedicated service instead.
Don’t use them to work around static limitations you should accept. If you’re considering edge functions to add real-time personalization to every page, you’re not building a static site with edge functions — you’re building a dynamic site that happens to use a CDN. Be honest about the architecture you actually need.
Where They Fit in Your Stack
The mental model that works: your static site handles 95% of requests by serving pre-built HTML from the CDN. Edge functions handle the remaining 5% — the form submissions, the webhook callbacks, the API proxies — at the same edge locations, with the same low latency.
On Cloudflare Pages, edge functions live in a functions/ directory. Each file maps to a route. A file at functions/api/contact.ts handles requests to /api/contact.
On Netlify, edge functions live in netlify/edge-functions/ and are configured in netlify.toml.
On Vercel, you can use Edge Runtime in API routes or middleware by exporting export const runtime = "edge" in a route handler.
On Render, edge functions aren’t native to the static site product, but you can deploy a separate Cloudflare Worker or use Render’s serverless functions alongside your static site.
The deployment model matters less than the principle: keep the edge functions small, stateless, and focused on the operations that genuinely require server-side execution. Everything else stays static.
The Discipline Required
The hardest part of this architecture isn’t the code. It’s the discipline to keep the edge layer thin.
Every edge function you add is a piece of server-side code you now maintain. It has environment variables. It needs monitoring. It can fail. The whole point of going static was reducing that operational surface. If you add 20 edge functions with complex routing, shared state, and database connections through adapter libraries — you’ve built a distributed backend and you’re pretending it’s a static site.
The rule I follow: if an edge function takes more than 50 lines of code, it probably shouldn’t be an edge function. Either it belongs in a proper backend service, or you’re over-engineering something that a third-party service handles better.
Three to five edge functions covering forms, webhooks, and API proxying is the sweet spot for most static sites. Beyond that, you’re past the inflection point where the static-first architecture is still earning its keep.
The Line
Static-first is a spectrum, not a binary. At one end: pure HTML files, no server logic, everything handled by third-party services or client-side code. At the other end: a full server-rendered application with a CDN in front of it.
Edge functions sit right at the boundary — just enough server-side capability to handle the operations that can’t happen in the browser, without pulling you into the territory where you need a backend team, a database, and an ops rotation.
The right question isn’t “should I use edge functions?” It’s “can I stay static for this?” If the answer is yes, stay static. If the answer is no, add the thinnest possible server-side layer at the edge. And if the answer is “I need a lot of server-side logic,” stop pretending it’s a static site and build the backend properly.
The clarity is the feature. Know where you are on the spectrum and build accordingly.
Static Signal is published by Neuron Web Development.