Using Middleware for Simplified Web App Authentication

• Reading time: 2 minutes

Authentication in modern web apps can be complex. Render your app on the server like in the good old days and you’ll save yourself some hassle. But what if you want a cheap statically generated site with protected routes?

Server-side rendering (SSR) allows you to intercept requests on the server, read cookie headers and provide a fully populated page. React Server Components (RSC) are a more novel and elegant way to achieve the same goal. Both SSR and RSC come with a tradeoff though; the server has to perform rendering work for every single request, which may not be desirable.

In a static site generator (SSG) setup, the Next.js documentation recommends pre-rendering a loader, followed by fetching the user on the client-side and displaying the full page or redirecting to the login page. Even if you add lazy loading and code splitting optimizations, this approach uses unnecessary network roundtrips.

Instead, server-side middleware can determine the authentication status using cookies before the first response byte is sent:

// middleware.js
async function getUser(req) {
  const response = await fetch("/api/user", {
    headers: req.headers,
  })
  if (!response.ok) return
  return await response.json()
}

export async function middleware(req) {
  const user = await getUser(req)
  if (!user && req.nextUrl.pathname !== "/login") {
    return NextResponse.redirect("/login")
  } else {
    return NextResponse.next()
  }
}

This approach can be combined with SSG and has nearly zero runtime cost. The middleware makes a request to some user endpoint using the original request’s cookie. If this fails, it redirects to the authentication page. If it succeeds, it proceeds with the actual response. Clients can assume that an authenticated user exists and only need to handle the rare case of a session being invalidated by the server after the initial load.

It’s important to note that this technique is not exclusive to Next.js but can be used with any setup that supports middleware. For example, Cloudflare Pages supports middleware as an edge function via Cloudflare Workers.