Next.js Discord

Discord Forum

Headers cannot be modified, error on deployment, working locally

Unanswered
Valentine ant posted this in #help-forum
Open in Discord
Valentine antOP
Hey! Trying to deploy my next js project onto Vercel. Everything runs fine locally, but I get this error with an unhelpful digest property.
[Error]: Headers cannot be modified. Read more: https://nextjs.org/docs/app/api-reference/functions/headers
    at Proxy.callable (/var/task/node_modules/.pnpm/next@13.5.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/compiled/next-server/app-page-experimental.runtime.prod.js:75:33099)

My app/page.tsx is a home page that calls a route handler to check if there is a current valid cookie if not redirect to sign-in. My app/sign-in/page.tsx does something similar, where it checks for a valid cookie then redirects to home if there is.

import { request } from '@/lib/utils'
import { Chat } from '@/components/chat'
import { headers } from 'next/headers'
import { redirect } from 'next/navigation'

async function getSession() {
  "use client"
  const options = {
    method: "GET",
    headers: headers(),
  };
  console.log("check 1");
  const session = await fetch(`${process.env.FRONTEND_BASE_URL}/api/user`, options)
  console.log("check 2);
  if (session.status === 401) {
    console.log("redirecting");
    redirect(`/sign-in`)
  } else if (session.status === 402) { // invalid cookie code
    const options = {
      method: "GET",
    };
    await fetch(`${process.env.FRONTEND_BASE_URL}/api/session`, options)
    redirect(`/sign-in`)
  }
  return await session.json()
}

export default async function IndexPage() {
  const session = await getSession()
  const user = session.user

  return <Chat />
}

I get the headers cannot be modified error between check 1 and check 2. Problem is I need to pass headers() to the route handler or it won't be able to pickup the cookies.

api/user/route.tsx:
import { sealData, unsealData } from 'iron-session/edge';
import { NextRequest, NextResponse } from 'next/server'
import { request as serverRequest } from '@/lib/utils'
import { cookies } from 'next/headers'

export async function GET(request: NextRequest) {
    "use server";
    const badResponse = new Response(null, {
        status: 401,
    })
    let goodHeaders = {}
    const cookie = request.cookies.get('voyager-session')?.value
    const session = cookie
        ? await unsealData(cookie, {
            password: process.env.SESSION_PASSWORD as string,
        })
        : { accessToken: null, refreshToken: null };

    if (!cookie || Object.keys(session).length === 0) {
        return badResponse
    }
    const { accessToken, refreshToken } = JSON.parse(String(session))
    if (!accessToken || !refreshToken) {
        return new Response(JSON.stringify({ msg: 'Invalid cookie, need to delete' }), {
            status: 402,
        })
    }
    const options = {
        method: 'GET',
        url: `/me?token=${accessToken}&refresh=${refreshToken}`,
    }
    const newSession = await serverRequest(options)
    if (!newSession) {
        return new Response(JSON.stringify({ msg: 'Expired cookie, need to delete' }), {
            status: 402,
        })
    }
    let result = {
        ...newSession,
        token: accessToken
    }
    return new Response(JSON.stringify(result), {
        status: 200,
        headers: {
            ...goodHeaders,
            'Content-Type': 'application/json'
        }
    });
};

async function process_auth(session: any) {
    const sessionString = JSON.stringify(session);
    const encryptedSession = await sealData(sessionString, {
        password: process.env.SESSION_PASSWORD as string,
    });
    return encryptedSession;
}


If I don't pass headers() in my fetch to api/user in app/page, then const cookie = request.cookies.get('voyager-session')?.value comes as null and the error routes my page back to sign-in even though there is a cookie I can manually see. So how do I get the headers with cookies in my route handler?

Any help would be greatly appreciated ❤️

2 Replies

@Valentine ant Hey! Trying to deploy my next js project onto Vercel. Everything runs fine locally, but I get this error with an unhelpful digest property. shell [Error]: Headers cannot be modified. Read more: https://nextjs.org/docs/app/api-reference/functions/headers at Proxy.callable (/var/task/node_modules/.pnpm/next@13.5.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/compiled/next-server/app-page-experimental.runtime.prod.js:75:33099) My app/page.tsx is a home page that calls a route handler to check if there is a current valid cookie if not redirect to sign-in. My app/sign-in/page.tsx does something similar, where it checks for a valid cookie then redirects to home if there is. typescript import { request } from '@/lib/utils' import { Chat } from '@/components/chat' import { headers } from 'next/headers' import { redirect } from 'next/navigation' async function getSession() { "use client" const options = { method: "GET", headers: headers(), }; console.log("check 1"); const session = await fetch(`${process.env.FRONTEND_BASE_URL}/api/user`, options) console.log("check 2); if (session.status === 401) { console.log("redirecting"); redirect(`/sign-in`) } else if (session.status === 402) { // invalid cookie code const options = { method: "GET", }; await fetch(`${process.env.FRONTEND_BASE_URL}/api/session`, options) redirect(`/sign-in`) } return await session.json() } export default async function IndexPage() { const session = await getSession() const user = session.user return <Chat /> } I get the headers cannot be modified error between check 1 and check 2. Problem is I need to pass headers() to the route handler or it won't be able to pickup the cookies. api/user/route.tsx: typescript import { sealData, unsealData } from 'iron-session/edge'; import { NextRequest, NextResponse } from 'next/server' import { request as serverRequest } from '@/lib/utils' import { cookies } from 'next/headers' export async function GET(request: NextRequest) { "use server"; const badResponse = new Response(null, { status: 401, }) let goodHeaders = {} const cookie = request.cookies.get('voyager-session')?.value const session = cookie ? await unsealData(cookie, { password: process.env.SESSION_PASSWORD as string, }) : { accessToken: null, refreshToken: null }; if (!cookie || Object.keys(session).length === 0) { return badResponse } const { accessToken, refreshToken } = JSON.parse(String(session)) if (!accessToken || !refreshToken) { return new Response(JSON.stringify({ msg: 'Invalid cookie, need to delete' }), { status: 402, }) } const options = { method: 'GET', url: `/me?token=${accessToken}&refresh=${refreshToken}`, } const newSession = await serverRequest(options) if (!newSession) { return new Response(JSON.stringify({ msg: 'Expired cookie, need to delete' }), { status: 402, }) } let result = { ...newSession, token: accessToken } return new Response(JSON.stringify(result), { status: 200, headers: { ...goodHeaders, 'Content-Type': 'application/json' } }); }; async function process_auth(session: any) { const sessionString = JSON.stringify(session); const encryptedSession = await sealData(sessionString, { password: process.env.SESSION_PASSWORD as string, }); return encryptedSession; } If I don't pass headers() in my fetch to api/user in app/page, then `const cookie = request.cookies.get('voyager-session')?.value` comes as null and the error routes my page back to sign-in even though there is a cookie I can manually see. So how do I get the headers with cookies in my route handler? Any help would be greatly appreciated ❤️
Landseer
any answer for this?
Landseer
okay so you need to pass the cookie manually like:
headers: {
'Cookie': 'CookieName=CookieValueGetFromCookiesFromNext/Headers'
},

This way, you dont need to touch the headers function directly which causes modify headers error.
Make sure that the cookie name is correct because in production mode, the cookie name will have some prefix like __Secure-
but in dev mode it has none

headers: {
'Cookie': ${process.env.NEXT_PUBLIC_COOKIE_PREFIX}next-auth.session-token=${cookieStore.get(${process.env.NEXT_PUBLIC_COOKIE_PREFIX}next-auth.session-token)?.value},
},