Http-only cookie not being set in production with NextJS + Django

I have deployed my frontend using NextJS and my backend is Django.

FE: https://domain-fe.fly.dev
BE: https://domain.fly.dev

The issue I am experiencing is that the http-only cookie is being sent over the network but it isn’t being set in the browser. When running locally everything works as expected.

The http-only cookie settings are sameSite: 'None' and Secure: True.

I am using Axios and sending withCrendentials: true on the login request. Since the cookie isn’t being set in the browser I am getting redirected back to my login page which is expected. What I need to happen is have the http-only cookie set in the browser and then I can continue with my application.

Please help me sort this issue out.

Hi,

Some guesses for things I’d look at to debug this:

  1. If you are using something like node.js, secure cookies won’t be set when the connection is not encrypted (or if it thinks it is not). Since your app is running behind Fly’s proxy, that is different to running locally. So make sure your app is indeed sure it is ok to set the cookie. For more on that e.g Using secure sessions behind an HTTP proxy - GoSquared Blog

  2. I assume your backend domain is acting like an API, with the front-end being an SPA. So the backend would be setting the cookie for subsequent requests to be sent to it. In which case you would have the backend code set the cookie’s domain as domain.fly.dev. You can’t set it to e.g fly.dev for some kind of shared cookie … because then you could set a cookie for every Fly app (bad). Check the domain of the cookie in the headers. If you do need a cookie that is shared by them both, you would need to use your own domain. Then you can set a cookie for e.g .your-domain.com, as that only applies to your own site. You basically just update your DNS A/AAAA records to be the Fly app IP, and request a SSL certificate using the appropriate flyctl command.

  3. Make sure the path isn’t set as that would also cause it to not match (maybe). And it has a valid expiry time. The simple stuff but doesn’t hurt to check.

Hey Greg, thanks for responding with some suggestions.

I tried step 1 + 2 (minus the custom domain) and didn’t get much success. We also tried updating some Django settings the below block, but that didn’t give any success when trying to login with the deployed fly frontend.

SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SESSION_COOKIE_DOMAIN = '.fly.dev'

I just ran the frontend locally so its running off localhost:3000 and I left all the endpoints pointing to the deployed fly backend and I was able successfully login. Is it possible the fly http proxy is blocking the cookies?

Hey,

No problem.

So … I’m not sure what domain you are using for the cookie when run locally. If it’s set for localhost, yep that will work. But I can’t imagine you are able to set a cookie for .fly.dev. That would be … bad. Only there are public suffixes which browsers know not to set cookies for. Why?

  • Avoid privacy-damaging “supercookies” being set for high-level domain name suffixes

The full list is here https://github.com/publicsuffix/list/blob/master/public_suffix_list.dat It’s huge. But you don’t need to read it. Since within that is …

fly.dev

Just like e.g run.app is in there, added by Google, for their app platform. And so on.

Since if you could set a cookie for fly.dev, it would be used by every Fly app. Oops.

I don’t know the innards of your app and so whether the cookie needs to be read by both domains but it seems (to me) that your choices are:

  1. set the cookie’s domain as name.fly.dev
  2. set the cookie’s domain as your-domain.com or .your-domain.com for a shared cookie. I’d go for that, personally

You can’t set it as .fly.dev.

You are still left with the “secure” question. But it sounds like you have that part solved using the ssl header, behind the Fly proxy. So my guess is that your issue is the domain.

Hey Greg,

After many attempts at a variety of different methods I finally got it to work. The solution that I came up with was using the rewrites feature in NextJS. With this feature I wrote a reverse proxy for all our routes. It works because the browser thinks the backend endpoint is coming from the same domain in our case the frontend domain.

In the NextJS Config

async rewrites() {
    return [
      {
        source: '/api/{existing-backend-route},
        destination: 'hosted-backend-url/{existing-route}',
      },
    ]
  },

Then you change your frontend routes to point to the source and prefix your deployed backend route e.g https:deployed-backend-domain.com/api/{existing-route}.

This has provided many advantages for us.

  1. Can use sameSite: 'Strict' again since it is coming from the same domain
  2. CORS issues shouldn’t cause anymore problems
  3. Now folks won’t know what our backend API endpoints will look like because they are behind the custom proxy routes.

I appreciate all your help and suggestions and I am glad to figure this out!

1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.