skip to content

A Next.js app gates its routes in middleware by checking that a session cookie is present on the incoming request and redirecting to /login when it is missing. What does that check actually establish about the caller, and what does it leave unproven?

level: middleimportance: must knowfreq 72%

answer

  1. presence is not verification
  2. the gate believes whatever the client sends
  3. curl can send any Cookie header
  4. signature check plus store lookup
  5. optimistic at the edge, authoritative at the data

basics

~20 s

It proves only that the request carried a cookie with that name — any client can send one. It does not show the session is valid, unexpired, unrevoked, or entitled to the data, so verification has to happen where the data is read.

solid answer

~50 s

The check establishes exactly one fact: a cookie by that name arrived on the request. It is an optimistic gate — cheap, runs before the route renders, and spares a signed-out visitor a page they cannot use. What it does not establish is anything about the session behind the cookie. The value could be arbitrary bytes typed into curl, a token whose signature never validated, a session the user signed out of on another device, or a session belonging to a different tenant than the record the page is about to load. Middleware normally does not perform the lookup that would settle those, because it sits on every matched request and the runtime it typically runs in is deliberately constrained. So I treat it as routing and user experience, and put real verification — signature check plus a lookup in the session store — in the code that actually reads the data, in Server Components, Route Handlers and Server Actions.

go deeper

for a junior

Be able to say that the check only asks whether a cookie by that name arrived, and that a real login check happens later on the server. Do not describe the redirect as security.

for a middle

Explain the mechanics: request.cookies reads the incoming Cookie header, nothing is verified, and a plain HTTP client can send any value. Name what verification would require — integrity plus a liveness lookup.

for a senior

Show the reasoning behind the split: middleware sits on every matched request, so an authoritative lookup there costs latency and session-store load. Say where you put the real check and how you stop a new page from skipping it.

for a principal

Own the boundary decision explicitly: declare the gate an optimization in your architecture docs, budget what the edge is allowed to cost per request, and make the verified-session path the only way any team can reach data.

## The check, precisely ```ts if (!request.cookies.has('session')) { return NextResponse.redirect(new URL('/login', request.url)) } return NextResponse.next() ``` On `NextRequest`, `request.cookies` exposes the cookies parsed from the incoming `Cookie` header. `has('session')` answers one question — did a name/value pair called `session` arrive — and `get('session')?.value` gives you the raw string. Nothing in that path validates, decrypts or verifies anything. Next.js does not sign your cookies for you and does not know what a valid session looks like in your system. ## What "present" therefore proves It proves the client chose to send it. That is the whole guarantee, and it is worth stating plainly because it defines the ceiling on what the gate can protect: ```bash curl -i https://app.example.com/dashboard -H 'Cookie: session=anything-at-all' ``` That request sails through the gate. There is no browser involved, no sign-in, no server-side state. Cookie attributes do not help here either: `HttpOnly`, `Secure` and `SameSite` are instructions to a cooperating browser about when to store and send a cookie, and they never even appear on the request — the `Cookie` header carries only names and values. A hostile client is not running your JavaScript and is not bound by any of it. ## What is still unproven when the value is real Even with a genuine, well-formed token, presence leaves four separate questions open. **Integrity.** Was the value produced by you and not edited? For a signed or encrypted token that means a cryptographic verification, not a decode. Reading the payload of a JSON Web Token without checking its signature is the classic version of this mistake: anyone can rewrite `"role":"member"` to `"role":"admin"` and re-encode it, and a decode-only check will believe them. **Liveness.** Has the session been revoked? Sign-out on another device, a password reset, an admin disabling the account, a device being unenrolled — none of those reach out and delete the cookie in the attacker's possession. Only a lookup against where sessions are stored, or a very short token lifetime, closes that window. This is why a stateless claim is a snapshot of the moment it was issued, not of now. **Identity binding.** Which user is this, and does that answer come from a source you control? The gate never asked. **Entitlement.** Even for a live session belonging to a real user, may that user see the specific thing this route is about? The gate ran before the route resolved and has no idea what record is coming. ## Why middleware stops at presence anyway This is not laziness. Middleware runs on every request the matcher covers — pages, Route Handlers, client-side navigation requests, prefetches. Putting a database round trip on that path multiplies your session-store load by your total request volume and adds latency to requests that were going to hit a cache. The runtime middleware typically executes in is also constrained on purpose, so the database driver you use elsewhere may simply not be available there. The design that falls out of both pressures is: cheap negative check at the edge, authoritative check next to the data. The Next.js documentation calls this an *optimistic* check for exactly that reason — it optimistically assumes the cookie means something, in order to give a fast redirect, and defers the pessimistic check to where it is affordable. ## Where the real check goes Verification belongs in the function that touches data: a small session module that verifies the token's integrity, resolves it to a user, and confirms the session is still live, called at the top of every Server Component page, Route Handler and Server Action that returns anything private. Wrapping data access so the query itself cannot run without a verified session is stronger still, because then a new page cannot forget. ## What the gate is genuinely worth Having been honest about the limits, do not throw it away. It saves rendering and data fetching for requests that are obviously signed out, it gives users a single consistent redirect instead of six different empty states, it preserves the destination for after login, and it is a real second layer: an attacker must both defeat it and defeat the check next to the data. It is a good optimization and a poor boundary, and the interview answer is to say which one you are using it as.

  • If the cookie holds a signed token, why is decoding it in middleware to read the user id still not enough?
    Decoding only base64-parses the payload; anyone can edit claims and re-encode. You need a signature verification with the key, and even a verified token only tells you what was true when it was issued. A session revoked two minutes ago still verifies until it expires, so anything that must respect revocation needs a lookup where sessions live.
  • Does making the cookie HttpOnly and Secure improve what the middleware gate can conclude?
    No. Those attributes constrain a cooperating browser — script cannot read the value, and it is only sent over HTTPS — which is worth doing for theft resistance. But the server sees only name and value on the `Cookie` header, and an attacker using a plain HTTP client is not a browser at all. The gate's conclusion is unchanged.
  • Is there a case where checking the session store inside middleware is the right call?
    Sometimes — a low-traffic internal tool, or a session store with an edge-reachable read path and tight latency, where centralising the check is worth the per-request cost. Even then it does not remove the check next to the data: middleware still cannot know which record a route will load, so it buys a better redirect, not authorization.

saying these in an interview costs you the question

  • If the cookie is there, the user is logged in.
  • HttpOnly cookies cannot be forged, so the check is safe.
  • Next.js validates session cookies for you.
  • Decoding the token in middleware counts as verifying it.
  • Once middleware passes the request, the page can trust it.

context