Netlify Edge Functions: Geo‑Based Auth at the Edge
Add JWT authentication and country‑based redirects with Netlify Edge Functions. Learn Deno limits, setup steps, and how to verify the result in 900 words.
01 Sept 2025, 11:38 UTC

Problem: Auth‑Heavy Sites Need Low‑Latency, Edge‑Side Checks
Most web apps still send every request to the origin, where a Node or Go service validates a JWT, looks up a user in a database, and then forwards the request. That round‑trip adds 20–50 ms of latency and taxes the origin with traffic that could be blocked earlier. If your users are spread across continents, you may also want to redirect people to a localized landing page before they hit the origin.
The practical takeaway: run a lightweight auth and geo‑redirect routine directly on Netlify’s CDN edge, before the request ever reaches your origin. It cuts latency, reduces origin load, and keeps the logic in one place.
Why Netlify Edge Functions Are the Right Tool
Netlify Edge Functions are Deno‑based scripts that execute at over 300 PoPs worldwide. They intercept requests before the origin, giving you:
- Immediate access to
context.geo(country, city, latitude/longitude). - Direct header manipulation via
Responseobjects. - Streaming rewrites with
TransformStreamto inject content on‑the‑fly. - Access to environment variables through
Deno.env.get().
They run under a strict 50 ms CPU‑time limit per invocation. Exceeding it returns a 502 with x-nf-edge-function-error: cpu_limit_exceeded. This keeps edge functions lightweight and predictable.
Getting Started: Project Layout & Deployment
my-site/
├─ netlify.toml
├─ netlify/edge-functions/
│ ├─ hello.ts
│ └─ auth.ts
└─ package.json
- Define the edge function folder – Netlify looks for
netlify/edge-functionsby default. If you prefer a custom path, add[[edge_functions]]entries tonetlify.toml. - Write the function – Export a
default asynchandler that receivesrequestandcontext. See the example below. - Deploy –
netlify deploy --prodor push to a preview branch. Edge functions are versioned with the site; reverting a deploy automatically rolls back the functions. - Test locally –
netlify devspins up a dev server that mocks the CDN. Note: the 50 ms CPU limit isn’t enforced locally.
Concrete Example: JWT Auth + Country Redirect
Below is a minimal Edge Function that:
- Validates a JWT from the
Authorizationheader usingjose. - Injects the decoded user ID into a custom header
X-User-Idfor downstream services. - Redirects visitors from
USto/us-homewhile letting others pass through.
// netlify/edge-functions/auth.ts
import { jwtVerify } from "https://deno.land/x/jwt@v2.8.0/mod.ts";
const PUBLIC_KEY = Deno.env.get("PUBLIC_KEY"); // PEM string
export default async (request, context) => {
const authHeader = request.headers.get("Authorization") || "";
const token = authHeader.replace(/^Bearer\s+/i, "");
try {
const payload = await jwtVerify(token, PUBLIC_KEY, { algorithms: ["RS256"] });
// Pass user ID to origin
const newHeaders = new Headers(request.headers);
newHeaders.set("X-User-Id", payload.payload.sub);
// Geo‑based redirect
if (context.geo.country === "US") {
return new Response(null, {
status: 302,
headers: {
Location: "/us-home",
"x-nf-edge-function": "auth",
},
});
}
const newRequest = new Request(request, { headers: newHeaders });
return context.next(newRequest);
} catch (e) {
return new Response("Unauthorized", {
status: 401,
headers: { "x-nf-edge-function": "auth" },
});
}
};
Key points:
- Use
Deno.env.getfor secrets; set them vianetlify env:set PUBLIC_KEY=<pem>before deploy. - The
context.next()call forwards the request to the origin. If you omit it, the function becomes the final handler. - All headers are typed by
@netlify/edge-functionsif you install it locally for IDE support.
Trade‑offs & Practical Limits
- 50 ms CPU budget: Heavy crypto or network calls can hit this limit. Keep JWT verification fast – use a short‑lived key and avoid external lookups.
- No Node APIs:
fs,path, orcrypto.createHashare unavailable. Use Deno stdlib or the Webcrypto.subtleAPI. - Memory: Edge Functions are stateless; any heavy data must be streamed or fetched on‑demand. Streaming with
TransformStreamkeeps memory usage low. - Environment variables: They’re not injected into local dev by default. Use
netlify env:getor the UI. - Pricing: The free tier caps 500 k CPU‑seconds/month. Monitor usage via the Functions tab in the Netlify dashboard.
Verify the Function Works
- Deploy a preview branch:
git push origin my-feature-branch netlify deploy --prod --branch my-feature-branch - Make a request from a browser or
curlwith a valid JWT:curl -H "Authorization: Bearer <jwt>" https://preview.my-site.netlify.com/ - Check headers – you should see
X-User-Idandx-nf-edge-function: auth. If you’re from the US, you’ll get a 302 redirect to/us-home. - In the Netlify dashboard, navigate to Functions > Edge Functions and confirm the invocation count, average CPU time, and error rate.
To test the CPU limit, add a busy loop in the function and hit the endpoint. A 502 with x-nf-edge-function-error: cpu_limit_exceeded should appear.
Actionable Next Steps
- Start with a simple auth flow like the one above. Add more context (e.g.,
context.cookies) as needed. - Use
TransformStreamto inject personalized banners into HTML without buffering the entire response. - Set up a monitoring rule in Netlify to alert when CPU time approaches the 50 ms threshold.
- Document the function’s behavior in your architecture wiki so other teams know the origin will never receive unauthenticated requests.
- Consider moving heavy logic to background functions if you need to hit external APIs or run complex calculations.
By shifting authentication and geo‑redirect logic to Netlify Edge Functions, you reduce origin load, improve user experience, and keep the code in a single, versioned place. Happy edge‑coding!
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.