# How to Implement Protected Routes with Middleware in Next.js on Cloudflare

> Implement protected routes in Next.js on Cloudflare using middleware. Validate Better-Auth sessions at the edge and redirect unauthenticated users efficiently before page loads.

- Repository: [Muhammad Arifin/fullstack-next-cloudflare](https://github.com/ifindev/fullstack-next-cloudflare)
- Tags: tutorial
- Published: 2026-03-04

---

**You can implement protected routes in Next.js on Cloudflare by creating a [`middleware.ts`](https://github.com/ifindev/fullstack-next-cloudflare/blob/main/middleware.ts) file that validates Better-Auth sessions at the edge and redirects unauthenticated users before they reach protected pages.**

The `ifindev/fullstack-next-cloudflare` repository demonstrates a production-ready pattern for implementing protected routes with middleware in Next.js on Cloudflare. By combining Next.js Middleware with Better-Auth running on Cloudflare Workers, authentication checks execute at the edge—ensuring low latency and preventing protected resources from ever reaching unauthenticated clients.

## Setting Up the Middleware Entry Point

Every protected route implementation starts with the middleware entry point. The [`middleware.ts`](https://github.com/ifindev/fullstack-next-cloudflare/blob/main/middleware.ts) file at the project root intercepts incoming requests before they reach your page components.

The middleware initializes by importing `NextRequest` and `NextResponse` from `next/server`, along with the `getAuthInstance` utility from `@/modules/auth/utils/auth-utils`:

```typescript
import { type NextRequest, NextResponse } from "next/server";
import { getAuthInstance as getAuth } from "@/modules/auth/utils/auth-utils";

```

This file serves as the gatekeeper for your application, running on Cloudflare's edge runtime to evaluate authentication status before any rendering occurs.

## Configuring the Better-Auth Singleton

The authentication logic relies on a singleton pattern to maintain a single Better-Auth instance across requests. In [`src/modules/auth/utils/auth-utils.ts`](https://github.com/ifindev/fullstack-next-cloudflare/blob/main/src/modules/auth/utils/auth-utils.ts), the `getAuthInstance` function creates or reuses a cached Better-Auth configuration that knows how to read session cookies stored by the client.

This singleton approach prevents unnecessary re-initialization of the auth provider on every request, which is critical for performance in a serverless edge environment like Cloudflare Workers. The utility exports several helper functions including `getCurrentUser`, `requireAuth`, and `isAuthenticated` for use across server actions and API routes.

## Implementing Session Validation Logic

Inside [`middleware.ts`](https://github.com/ifindev/fullstack-next-cloudflare/blob/main/middleware.ts), the core protection logic validates sessions by calling `auth.api.getSession()` with the incoming request headers. If the session is valid, the request proceeds; if not, the user redirects to the login page.

The `config.matcher` array restricts this protection to specific routes, such as the dashboard area:

```typescript
export async function middleware(request: NextRequest) {
  try {
    const auth = await getAuth();
    const session = await auth.api.getSession({ headers: request.headers });

    if (!session) {
      return NextResponse.redirect(new URL("/login", request.url));
    }

    return NextResponse.next();
  } catch {
    return NextResponse.redirect(new URL("/login", request.url));
  }
}

export const config = {
  matcher: ["/dashboard/:path*"],
};

```

Because this executes on Cloudflare's edge network, the redirect happens before any page JavaScript downloads or executes, providing immediate protection for sensitive routes.

## Reusing Authentication Logic in Server Actions

Beyond middleware, the same authentication utilities protect server actions and API routes. The [`src/modules/auth/utils/auth-utils.ts`](https://github.com/ifindev/fullstack-next-cloudflare/blob/main/src/modules/auth/utils/auth-utils.ts) file exports `getCurrentUser` for optional session checks and `requireAuth` for mandatory authentication.

To conditionally access user data in a server action:

```typescript
import { getCurrentUser } from "@/modules/auth/utils/auth-utils";

export async function someServerAction() {
  const user = await getCurrentUser();
  if (!user) {
    throw new Error("User not authenticated");
  }
  // Access user.id, user.email, etc.
}

```

For actions that always require authentication, use `requireAuth` to enforce login:

```typescript
import { requireAuth } from "@/modules/auth/utils/auth-utils";

export async function createTodoAction(data) {
  const user = await requireAuth(); // Throws if not logged in
  // Create todo belonging to user.id
}

```

This pattern ensures consistent authentication logic across your entire Next.js application, whether running at the edge in middleware or in serverless functions.

## Summary

- **Create [`middleware.ts`](https://github.com/ifindev/fullstack-next-cloudflare/blob/main/middleware.ts)** at your project root to intercept requests before they reach protected pages.
- **Use a singleton pattern** via `getAuthInstance` in [`src/modules/auth/utils/auth-utils.ts`](https://github.com/ifindev/fullstack-next-cloudflare/blob/main/src/modules/auth/utils/auth-utils.ts) to maintain a cached Better-Auth configuration.
- **Validate sessions** by calling `auth.api.getSession({ headers: request.headers })` and redirect to `/login` when validation fails.
- **Configure `config.matcher`** to specify which routes require protection, such as `/dashboard/:path*`.
- **Reuse auth utilities** (`getCurrentUser`, `requireAuth`) in server actions and API routes for consistent security across your Cloudflare-hosted Next.js application.

## Frequently Asked Questions

### How does the middleware know which routes to protect?

The `config.matcher` export in [`middleware.ts`](https://github.com/ifindev/fullstack-next-cloudflare/blob/main/middleware.ts) defines URL patterns that trigger the authentication check. In the `ifindev/fullstack-next-cloudflare` repository, the matcher array `["/dashboard/:path*"]` ensures only dashboard routes and their subpaths undergo session validation, while public pages like `/login` remain accessible without authentication.

### What happens if the Better-Auth instance fails to initialize in the middleware?

The middleware wraps authentication logic in a try-catch block. If `getAuth()` throws an error due to initialization failure, the catch block immediately redirects the user to `/login`. This fail-safe prevents unauthorized access when the auth system is unavailable or misconfigured.

### Can I use the same authentication check in API routes and server actions?

Yes. The [`src/modules/auth/utils/auth-utils.ts`](https://github.com/ifindev/fullstack-next-cloudflare/blob/main/src/modules/auth/utils/auth-utils.ts) file exports `getCurrentUser` and `requireAuth` functions that reuse the same singleton Better-Auth instance used by the middleware. This ensures consistent session validation whether you're protecting edge middleware, server actions, or API routes running on Cloudflare Workers.

### Why run authentication checks at the edge with Cloudflare Workers?

Running checks at the edge via Cloudflare Workers eliminates latency by validating sessions geographically close to the user before any page rendering occurs. This approach prevents protected HTML, JavaScript, or data from ever reaching an unauthenticated client, offering superior security and performance compared to client-side authentication checks.