Next.js Discord

Discord Forum

Middleware cookies weirdly not working

Unanswered
Thrianta posted this in #help-forum
Open in Discord
ThriantaOP
hey guys i have an issue in my middleware but i cant see to get to what it is:

import { NextResponse } from 'next/server'
import { geolocation } from '@vercel/edge';

import routes from "@/i18n/routes"
import translations from "@/i18n/translations"

import themeSettings from "@/themes/settings"
import langSettings from "@/i18n/settings"



const themeCookie = themeSettings.themeCookie;
const langCookie = langSettings.langCookie;

export default function middleware(request) {
    //==================================================
    // Allow server components to access the URL
    //==================================================
    const requestHeaders = new Headers(request.headers);
    requestHeaders.set('x-url', request.url);




    //====================
    // Theme setting
    //====================
    let theme = request.cookies.get(themeCookie);
    if (theme) {
        theme = theme.value.toLowerCase();
    }

    if (!themeSettings.allowed.includes(theme)) {
        request.cookies.set(themeCookie, themeSettings.default);
    }




    //====================
    // Language setting
    //====================
    let lang = request.cookies.get(langCookie);
    if (lang){
        lang = lang.value.toLowerCase();
    }

    if(!translations[lang]) {
        const { country } = geolocation(request);
        if (routes[country]) {
            request.cookies.set(
                langCookie,
                routes[country]
            );
        }
        else {
            console.log('Language new');
            request.cookies.set(
                langCookie,
                routes.default
            );
        }
    }
    request.headers.set(langSettings.langHeader, langSettings.default);
    
    



    //===================================
    // Return response to the renderer
    // It'll handle it from here
    //===================================
    return NextResponse.next({
        request: {
            // Apply new request headers
            headers: request.headers,
            // Apply new request cookies
            cookies: request.cookies,
        },
    });
}

export const config = {
  // matcher solution for public, api, assets and _next exclusion
  matcher: "/((?!api|static|.*\\..*|_next).*)",
};


as you can see from the code, i'm first handling the theme cookies to be used by the server, which successfully manages to set the theme cookie to the default, however when i want to do something similar to the lang cookie it doesn't work.

i don't have the lang cookie initally so it sents it in the block with the console.log() in it (as i've tested in the console), however, it for some reason does not physically set it. i do not get a lang cookie returned in the browser the same way as i do with the theme cookie, what should i do here?

22 Replies

European sprat
i responded in general not seeing you made this
const requestHeaders = new Headers(req.headers)
requestHeaders.set("x-request-url", req.url)

const response = NextResponse.next({
  request: {
    headers: requestHeaders,
  },
})

response.cookies.set("hello", "world", { httpOnly: true })

return response
set cookies on the response
ThriantaOP
hey, it worked only when you set the cookies both in the request and the response so the server can read them apparently
my function now starts with something like this
export default function middleware(request) {
    //==================================================
    // Allow server components to access the URL
    //==================================================
    const response = NextResponse.next({
        request: {
            // Apply old request headers
            headers: request.headers,
            // Apply old request cookies
            cookies: request.cookies,
        }
    })


    function setCookie(name, value){
        request.cookies.set(name, value);
        response.cookies.set(name, value);
    }

    const getCookie = (name) => request.cookies.get(name);

    function setHeader(name, value){
        response.headers.set(name, value);
        request.headers.set(name, value);
    }

    // ...

   }
thanks for your help though!
ThriantaOP
hey again @European sprat,

here's my entire middleware function again:

import { NextResponse } from 'next/server'
import { geolocation } from '@vercel/edge';

import routes from "@/i18n/routes"
import translations from "@/i18n/translations"

import themeSettings from "@/themes/settings"
import langSettings from "@/i18n/settings"



const themeCookie = themeSettings.themeCookie;
const langCookie = langSettings.langCookie;

export default function middleware(request) {

    const response = NextResponse.next({
        request: {
            // Apply old request headers
            headers: request.headers,
            // Apply old request cookies
            cookies: request.cookies,
        }
    })


    function setCookie(name, value){
        request.cookies.set(name, value);
        response.cookies.set(name, value);
    }

    const getCookie = (name) => request.cookies.get(name);

    function setHeader(name, value){
        response.headers.set(name, value);
        request.headers.set(name, value);
    }

    //==================================================
    // Allow server components to access the URL
    //==================================================
    setHeader("x-url", request.url);




    //====================
    // Theme setting
    //====================
    let theme = getCookie(themeCookie);
    if (theme) {
        theme = theme.value.toLowerCase();
    }

    if (!themeSettings.allowed.includes(theme)) {
        setCookie(themeCookie, themeSettings.default);
    }




    //====================
    // Language setting
    //====================
    let lang = getCookie(langCookie);
    if (lang){
        lang = lang.value.toLowerCase();
    }

    if(!translations[lang]) {
        const { country } = geolocation(response);
        if (routes[country]) {
            setCookie(
                langCookie,
                routes[country]
            );
        }
        else {
            setCookie(
                langCookie,
                routes["default"]
            );
        }
    }
    
    setHeader(langSettings.langHeader, langSettings.default);
    
    

    //===================================
    // Return response to the renderer
    // It'll handle it from here
    //===================================
    return response;
}

export const config = {
    // matcher solution for public, api, assets and _next exclusion
    matcher: "/((?!api|static|.*\\..*|_next).*)",
  };


even what is done here unfortunately cant change up the cookies so that the server can instantly access them after middleware:
from the layout:
export default function RootLayout({ children }) {
    console.log("from layout", cookies())
    //more code follows
}
European sprat
i don't think it'll work reading them from cookies after it has passed middleware
1) request comes in without cookies
2) you set the cookies on the client
3) request still doesn't have cookies
4) cookies() reads the request which doesn't have cookies
maybe you can add the values as custom headers that you can read?
ThriantaOP
:shrug: that is quite a tiring and expensive workaround but it works:

import { NextResponse } from 'next/server'
import { geolocation } from '@vercel/edge';

import routes from "@/i18n/routes"
import translations from "@/i18n/translations"

import themeSettings from "@/themes/settings"
import langSettings from "@/i18n/settings"



const themeCookie = themeSettings.themeCookie;
const lastThemeCookie = themeSettings.lastThemeCookie;
const langCookie = langSettings.langCookie;

export default function middleware(request) {

    const response = NextResponse.next({
        request: {
            // Apply old headers
            headers: request.headers,
            // Apply old cookies
            cookies: request.cookies
        }
    })

    function setCookie(name, value){
        request.cookies.set(name, value);
        response.cookies.set(name, value);
    }

    const getCookie = (name) => request.cookies.get(name);

    function setHeader(name, value){
        response.headers.set(name, value);
        request.headers.set(name, value);
    }

    //==================================================
    // Allow server components to access the URL
    //==================================================
    setHeader("x-url", request.url);



    //====================
    // Theme setting
    //====================
    let theme = getCookie(themeCookie);
    if (theme) {
        theme = theme.value.toLowerCase();
    }

    if (!themeSettings.allowed.includes(theme)) {
        theme = themeSettings.default;
        setCookie(themeCookie, themeSettings.default);
    }

    if (getCookie(lastThemeCookie)){
        setHeader(themeSettings.lastThemeHeader, getCookie(lastThemeCookie).value);
    }

    setHeader(themeSettings.themeHeader, theme);




    //====================
    // Language setting
    //====================
    let lang = getCookie(langCookie);
    if (lang){
        lang = lang.value.toLowerCase();
    }

    if(!translations[lang]) {
        const { country } = geolocation(response);
        if (routes[country]) {
            lang = routes[country];
        }
        else {
            lang = routes.default;
        }

        setCookie(langCookie, lang);
    }
    setHeader(langSettings.langHeader, lang);
    
    
    
    

    //===================================
    // Return response to the renderer
    // It'll handle it from here
    //===================================
    return response;
}

export const config = {
  // matcher solution for public, api, assets and _next exclusion
  matcher: "/((?!api|static|.*\\..*|_next).*)",
};
crazy that vercel haven't fixed this massive bug in so long
European sprat
which bug?
ThriantaOP
you cant pass new cookies to the server
only headers
European sprat
yeah but cookies are read from the client
they're sent with the request
ThriantaOP
yeah but their whole purpose is to be read on the server
if you want it to only be read on the client just use localhost or something
anyways thanks for your help though! 😄