Cross-Domain Session Cookie (Express API on Heroku + React App on Netlify) Cross-Domain Session Cookie (Express API on Heroku + React App on Netlify) heroku heroku

Cross-Domain Session Cookie (Express API on Heroku + React App on Netlify)


Short answer:

It wasn't working as expected because I was testing on Chrome Incognito and, by default, Chrome blocks third party cookies in Incognito mode (more details).

Below is a list with some things to check if you're having a similar issue ;)



Checklist

In case it helps, here's a checklist with different things that you main need ;)

  • (Backend) Add "trust proxy" option

If you're deploying on Heroku, add the following line (you can add it before your session settings).

app.set("trust proxy", 1);
  • (Backend) Check your session settings

In particular, check the option sameSite and secure (more details here).

The code below will set sameSite: 'none' and secure: true in production:

app.use(  session({    secret: process.env.SESSION_SECRET || 'Super Secret (change it)',    resave: true,    saveUninitialized: false,    cookie: {      sameSite: process.env.NODE_ENV === "production" ? 'none' : 'lax', // must be 'none' to enable cross-site delivery      secure: process.env.NODE_ENV === "production", // must be true if sameSite='none'    }  }));
  • (Backend) CORS config
app.use(  cors({    credentials: true,    origin: [process.env.FRONTEND_APP_URL]  }));
  • (Backend) Environment Variables

Setup the environment variables in Heroku. For example:

FRONTEND_APP_URL = https://my-project.netlify.app

IMPORTANT: For the CORS URL, avoid a trailing slash at the end. The following may not work:

FRONTEND_APP_URL = https://my-project.netlify.app/ --> avoid this trailing slash!
  • (Frontend) Send credentials

Make sure you're sending credentials in your API calls (you need to do that for all calls you make to the API, including the call for user login).

If you're using axios, you can do use withCredentials option. For example:

    axios.post(`${process.env.REACT_APP_BACKEND_API_URL}/items`, {        title: this.state.title,        description: this.state.description,    }, {withCredentials:true})    .then( (res) => {        // ...    });
  • (Browser) Check the configuration for third-party cookies

For testing, you probably want to make sure you're using the default configuration provided by each browser.

For example, as of 2021, Chrome blocks third-party cookies in Incognito mode (but not in "normal" mode), so you probably want to have something like this:

enter image description here

  • ...and deal with browser restrictions...:

Finally, keep in mind that each browser has a different policy for third party cookies and, in general, those restrictions are expected to increase in the coming years.

For example, Chrome is expected to block third-party cookies at some point in 2023 (source).

If your App needs to bypass those restrictions, here are some options:

  • Implement Backend & Frontend under the same domain

  • Implement Backend & Frontend under subdomains of the same domain (example, example.com & api.example.com)

  • Have your Backend API under a proxy (if you're using Netlify, you can easily setup a proxy using a _redirects file)