Skip to content
lazy devs
4 min readLazy Devs

Edge vs server rendering: what actually matters

Edge and regional servers solve different problems. The real question is where your data lives and how much compute your request needs, not which one is trendy.

The edge runtime gets sold as a strict upgrade over regional servers, like everyone running Node in a single data center is doing it wrong. That framing is backwards. Edge and server rendering solve different problems, and the right call depends almost entirely on where your data lives and how heavy your request is.

What these words actually mean

When people say "edge rendering" in a Next.js context, they usually mean two distinct things that get blurred together.

The first is the edge runtime: a stripped-down JavaScript environment (V8 isolates, no Node APIs) that runs in many locations close to users. It boots in single-digit milliseconds and has no Node built-ins, so no fs, no raw TCP, and a limited set of npm packages that work.

The second is edge caching, which is just a CDN serving static or cached responses near the user. That part is almost always a win and is mostly orthogonal to the runtime debate.

"Server rendering" in the modern sense means your React Server Components and route handlers run on a Node.js runtime, typically in one region (or a few), close to your database.

The confusion comes from treating "edge" as a synonym for "fast." It is fast for the network hop. It can be slow for everything else.

The thing nobody mentions: your database is in one place

Here is the scenario that breaks the edge-is-always-faster story.

Your function runs in Singapore because that is where the user is. Your Postgres primary runs in Virginia because that is where you provisioned it. Every query now crosses an ocean. A page that does three sequential queries just paid for three round trips at roughly 200ms each. You moved compute to the user and left the data behind, so you made things worse.

// app/dashboard/page.tsx
// Three sequential queries. On the edge in Singapore with a DB
// in Virginia, this can mean ~600ms of pure network latency
// before any rendering happens.
export default async function Dashboard() {
  const user = await getUser();                 // round trip 1
  const team = await getTeam(user.teamId);      // round trip 2
  const projects = await getProjects(team.id);  // round trip 3
 
  return <DashboardView user={user} team={team} projects={projects} />;
}

Same code on a Node server sitting in the same region as the database: each query is a couple of milliseconds. The "slow" regional server beats the "fast" edge function by a wide margin because latency to the database dominates, not latency to the user.

The rule of thumb that actually holds up: put your compute next to whatever it talks to most. For a typical CRUD app, that is the database. Moving rendering to the edge only helps when the request needs little or no origin data, or when the data it needs is already cached at the edge.

Where edge genuinely wins

Edge is the correct tool in a few concrete cases.

Geographically aware logic with no heavy data

A/B test bucketing, geolocation redirects, auth gating, feature flags, rewriting requests based on a cookie. This is request shaping, not data fetching. Middleware is the obvious home for it, and on Vercel middleware runs on the edge by default.

// middleware.ts
import { NextResponse, type NextRequest } from "next/server";
 
export function middleware(req: NextRequest) {
  const country = req.geo?.country ?? "US";
 
  if (country === "DE" && !req.nextUrl.pathname.startsWith("/de")) {
    return NextResponse.redirect(new URL("/de", req.url));
  }
 
  const bucket = req.cookies.get("ab")?.value ?? (Math.random() < 0.5 ? "a" : "b");
  const res = NextResponse.next();
  res.cookies.set("ab", bucket);
  return res;
}

This is cheap, stateless, and benefits directly from running close to the user. No database, no problem.

Reading from data that is already global

If your reads come from an edge-replicated key-value store or a globally distributed database, the data is near the function too. Now edge compute plus edge data actually delivers the low-latency story it promises. The catch is that you have to build for it. A single-region Postgres does not become global because you wrapped it in an edge function.

Streaming the shell fast

For pages that stream, getting the first bytes out quickly matters for perceived performance. An edge function can flush the static shell while slower data resolves. But you can do the same thing from a regional Node server with React's streaming SSR, and if your data is regional anyway, the Node server usually wins overall.

Where server rendering wins

Regional Node rendering is the boring default for good reasons.

You get the full Node runtime, so any npm package works, including ones with native bindings, real database drivers with connection pooling, and SDKs that assume Node. You sit next to your database, so multi-query pages stay fast. And connection pooling actually works, because you are not spinning up a fresh isolate per request that each wants its own database connection.

That last point bites people hard. Edge functions are short-lived and numerous, which is murder on a Postgres connection limit. You end up needing a separate connection pooler (PgBouncer, or a serverless driver over HTTP) just to survive. A regional Node server with a normal pool sidesteps the whole problem.

A decision you can actually apply

Skip the benchmarks-as-marketing and ask three questions about the specific route.

  1. Does this route touch your primary database? If yes, and that database lives in one region, render it on a server in that region. Network physics will not negotiate.
  2. Is this pure request shaping (redirects, headers, geo, auth checks)? Put it in edge middleware. It is what middleware is for.
  3. Is the data this route needs globally replicated or cacheable at the edge? Then edge rendering pays off. Otherwise it does not.

In practice most apps land on a mix: edge middleware for routing and auth, regional servers for the data-heavy pages, and aggressive CDN caching in front of anything static. You almost never want everything on the edge, and you almost never want zero edge.

A concrete split

For a SaaS dashboard, a setup that holds up well:

  • Marketing pages: static, served from the CDN everywhere.
  • Auth and geo middleware: edge.
  • App routes that query Postgres: Node, same region as the database.
  • Public API responses that rarely change: edge-cached with tag-based revalidation.

Each piece runs where its bottleneck is, which is the only principle that survives contact with production.

Takeaway

Edge versus server is not a ranking. It is a question of distance: distance from the user versus distance from the data. Move the compute toward whichever one the request leans on hardest. For data-heavy pages that is your regional server, for request shaping it is the edge, and for most real apps it is both at once.

If you are staring at a slow page and wondering whether the edge will fix it, measure where the time actually goes first. The answer is usually a round trip to a database that lives somewhere else, and no runtime swap will save you from geography.

Related service

Next.js Development Services

Production Next.js apps and sites, tuned for speed and search.

Learn more

Want this built right?

This is the work we do every day. Tell us what you are building and we will show you exactly how we would ship it.

hello@lazydevsagency.com