Node.js Secure Cookies over HTTPS

This article explains how Node.js applications ensure secure cookies are transmitted exclusively over encrypted HTTPS connections. It covers the mechanics of the HTTP Secure attribute, how to configure popular Node.js middleware to enforce this behavior, and how to properly handle reverse proxies in production environments.

Under the hood, Node.js enforces secure cookie transmission by appending the Secure attribute to the Set-Cookie HTTP response header. This attribute is an instruction to the user’s web browser, telling it never to send the cookie back to the server over an unencrypted HTTP connection. If an attacker attempts to intercept network traffic, or if the user accidentally navigates to an HTTP version of the site, the browser will withhold the cookie, protecting it from interception.

In the Node.js ecosystem, this behavior is typically configured using session and cookie-parsing middleware like express-session or cookie-parser. When defining the cookie settings, developers must explicitly set the secure property to true.

Here is an example of how to enforce secure cookies in an Express application:

const express = require('express');
const session = require('express-session');
const app = express();

app.use(session({
  secret: 'your-secure-secret',
  resave: false,
  saveUninitialized: true,
  cookie: {
    secure: true,   // Restricts the cookie to HTTPS connections only
    httpOnly: true  // Mitigates XSS attacks by hiding the cookie from client-side JS
  }
}));

When secure is set to true, compliant browsers will block the cookie from being sent over standard http:// requests. During local development, testing this configuration over HTTP can cause cookies to fail to store. To resolve this, developers must either run a local HTTPS server or rely on modern browsers that treat localhost as a secure origin by default.

In production environments, Node.js applications are commonly deployed behind a reverse proxy, such as Nginx, Cloudflare, or a cloud load balancer. These proxies typically terminate the SSL/TLS (HTTPS) connection and forward the request to the internal Node.js process over HTTP.

Under these conditions, Node.js will detect an HTTP connection and refuse to send the cookie if secure is enabled. To fix this, you must configure Node.js to trust the reverse proxy. In Express, this is achieved by enabling the trust proxy setting:

app.set('trust proxy', 1); // Trust the first proxy in front of Node.js

By enabling this setting, Node.js reads the X-Forwarded-Proto header passed by the proxy. If the header indicates the client connected via HTTPS, Node.js recognizes the connection as secure and safely transmits the cookie with the Secure flag.