Securing the Website

Securing the website is a crucial step in the deployment process. It ensures that the website is protected from unauthorized access and malicious attacks.

Fundamentals of Securing any NextJS Website

In general we can secure a website in 3 layers:

LayerPurpose
Server authThe source of truth for security and data access
Middleware authRoute-level protection (block unauthenticated requests early)
Client authUX polish only (nice UI, not security)

A good rule: client decides what looks nice, server decides what is true.

We will start with the least secure and work upwards.


Client Auth Layer

Think of this as UX polish (not security). A client-side login state is great for things like:

  • Changing a header button from Login --> Account
  • Showing a different homepage hero for logged-in users
  • Personalised greetings (eg. “Welcome back”)

Important notes

Client auth is a great doormat. It is not a lock however, for SEO it might render as it's loading state or null because of SSR.

You should not hide sensitive data using this method. Also running 10 components all running their own auth checks could cause performance issues.

Example of "Sign in" or "My Account" button

import { useLoginState } from "@/lib/supabase/loginState"
function MyHeader() {
  const { state } = useLoginState()
  return <>
    {state === "loading" ? (
      <Button variant="outline" disabled >
        <FaSpinner className="animate-spin" />
      </Button>
    ) : state === "signedIn" ? (
      <Button variant="outline" href="/account">
        Account
      </Button>
    ) : (
      <Button href="/login">Sign in</Button>
    )}
  </>
}

Server Auth Layer

Server auth is where real security lives. This means:

  • Reading session/user from server-side cookies
  • Redirecting from protected pages on the server
  • Enforcing database access via RLS (Supabase recommended)

In this project, server auth lives here:

  • src/lib/supabase/loginState.server.ts

This is a solid place to read the current auth state on the server and use it to protect pages. It is appropriate for sensitive page protection because it runs server-side and does not rely on browser-only state.

Example: Protect a page server-side (App Router)

// app/my-dashboard/page.tsx
import { redirect } from "next/navigation";
import { getServerLoginState } from "@/lib/supabase/loginState.server";

export default async function MyDashboardPage() {
  const { signedIn } = await getServerLoginState();

  if (!signedIn) {
    redirect("/login?next=/my-dashboard");
  }

  return <div>Private dashboard</div>;
}

Using middleware.ts to secure multiple pages

Adding a /src/middleware.ts file to secure entire routes is a great way to secure multiple pages. The example below would protect anything in the /account/ route and anything inside it. i.e :

/account/
/account/settings
/account/profile
/account/billing
/account/security
./account/... any other pages inside the /account/ route

middleware.ts - secure entire routes

import { createServerClient } from "@supabase/ssr";
import { NextResponse, type NextRequest } from "next/server";
const LOGIN_PATH = "/login";
export async function middleware(req: NextRequest) {
  let res = NextResponse.next({
    request: { headers: req.headers },
  });
  const supabase = createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        getAll() {
          return req.cookies.getAll();
        },
        setAll(cookiesToSet) {
          cookiesToSet.forEach(({ name, value, options }) => {
            res.cookies.set(name, value, options);
          });
        },
      },
    }
  );
  const {
    data: { user },
  } = await supabase.auth.getUser();
  if (!user) {
    const loginUrl = req.nextUrl.clone();
    loginUrl.pathname = LOGIN_PATH;
    loginUrl.searchParams.set("redirectTo", req.nextUrl.pathname);
    return NextResponse.redirect(loginUrl);
  }
  return res;
}
export const config = {
  matcher: [
    "/account/:path*",
    // '/another-secure-area/:path*'
  ],
};

Should I use middleware and server auth for all my pages?

Yes and No.

Yes, if your pages are ultra sensitive and you need to be 100% sure the user is logged in.

Middleware could potentially be misconfigured, so having both is a good idea.

Was this page helpful?