How to get cookies server-side in a Next.js app
By Flavio Copes
Learn how to read cookies in Next.js 16 App Router with cookies() from next/headers, and the older Pages Router getInitialProps approach.
To read cookies during a server render in Next.js 16 (App Router), call cookies() from next/headers in a Server Component, Server Action, or Route Handler. That is the supported way to see the request cookies on the server.
I had this problem years ago, on the Pages Router. My app depended on cookies for authentication, and my cookies were not set on first page initialization. The page fetched an internal API with Axios during the server render, and Passport.js on that endpoint failed to authenticate the user because the request carried no cookie. The App Router has a cleaner way to solve it, and the old fix still works if you’re on pages/.
App Router: cookies() from next/headers
In a Server Component page or layout:
import { cookies } from 'next/headers'
export default async function BookingsPage() {
const cookieStore = await cookies()
const session = cookieStore.get('session')
// use session?.value when calling your API, or pass it to a helper
}
cookies() is async since Next.js 15. Always await it before calling .get(), .has(), or similar. One side effect to know about: reading cookies depends on the incoming request, so a page that calls cookies() is rendered on every request instead of at build time.
If your page fetches an internal API that needs the browser session, read the cookie (or the whole Cookie header via headers()) and forward it on that server-side request:
import { cookies } from 'next/headers'
export default async function BookingsPage() {
const cookieStore = await cookies()
const cookieHeader = cookieStore
.getAll()
.map(c => `${c.name}=${c.value}`)
.join('; ')
const response = await fetch('http://localhost:3000/api/bookings/list', {
headers: cookieHeader ? { cookie: cookieHeader } : undefined,
cache: 'no-store',
})
const bookings = await response.json()
// ...
}
In a Route Handler you can also read request.headers.get('cookie') from the incoming Request, or use cookies() the same way.
Why does this happen?
When the page renders on the server, your fetch or Axios call is a server-to-server request. The browser is not involved, so it can’t attach its cookies like it does for normal requests. The API endpoint receives a request with no Cookie header, and Passport.js (or whatever handles your sessions) sees an anonymous user.
The cookies are not lost, though. The browser sent them with the initial page request, and cookies() is how you read them. You forward them when the upstream API expects the same Cookie header the browser would have sent.
Pages Router legacy: getInitialProps
If you still use pages/ and getInitialProps, the older pattern is to forward ctx.req.headers.cookie:
Bookings.getInitialProps = async ctx => {
const response = await axios({
method: 'get',
url: 'http://localhost:3000/api/bookings/list',
headers: ctx.req ? { cookie: ctx.req.headers.cookie } : undefined
})
return {
bookings: response.data
}
}
The ternary matters because getInitialProps also runs in the browser on client navigations. There ctx.req is undefined, and accessing ctx.req.headers would crash with a TypeError. The code works on a full page load, then breaks on client-side navigation. In the browser we pass undefined and the browser attaches the cookies itself. For more on that lifecycle, see getInitialProps in Next.js.
One more thing to check: if the user has no session yet, ctx.req.headers.cookie is undefined too. That’s fine, Axios skips the header and your endpoint treats the request as unauthenticated, which is what you want.
Want me to talk about your product? You can sponsor this site.
Related posts about next: