Using HTTP Strict Transport Security (HSTS) headers in Node.js server
Tags: node, security, web, express
Published: 2023-06-05 03:32:35

For most websites and apps, employing security-related HTTP headers has become standard practice. Websites use headers as part of HTTP requests and replies to convey information about a page or data sent via the HTTP protocol.
Among the many available security headers that modern web browsers use to protect users, one crucial type is the HTTP Strict Transport Security (HSTS) header. **The HSTS header that a website provides tells the browser to use the HTTPS protocol on each subsequent visit.**

https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security

![HTTP Strict Transport Security](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jglfw0zpvep2nhx7ckzp.png)

However, despite their utility, ease of implementation, and support from virtually every browser, only about 25% of mobile and 28% of desktop HTTP responses include HSTS headers.

![HSTS directives usage](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yainiyjho8ak1qepft5f.jpg)

Let us take an in-depth look at HSTS headers to discover how they affect web security and why we should use them on Node.js.
Then, I’d like to describe how to enable HSTS inside a Node.js server.

## Why enable HSTS?

HTTPS connections are secure because they encrypt and decrypt transmitted data packets using SSL/TLS certificates, which well-known certificate authorities (CAs) can verify. As a result, **only the user’s computer and the server can read the transmitted data packets, regardless of who intercepts the network traffic.**

Typical HTTP connections are unencrypted, meaning that anyone who accesses the data can read it. These unintended recipients might include servers routing and forwarding the user’s data to its destination server or a hacker who accesses the network traffic on a public Wi-Fi router.

## HOW DOES HSTS WORK?

When the HTTP response header contains an HSTS, web browsers know to always use an HTTPS connection with the server and automatically redirect users who first connected via HTTP.
From then on, connections to the web application or site remain encrypted and secure, enabling the app to use secure cookies and helping prevent MITM (man-in-the-middle) security attacks.

There are three different parameters available in the HSTS header.

- MAX-AGE

```
MAX-AGE=<EXPIRE-TIME>
```

This required parameter specifies the amount of time in seconds that the browser should remember to connect to the site via HTTPS. Once this time expires, the browser will load the site normally on the next visit. The expiration time is updated in the user’s browser every time it sees the HSTS header. As a result, the parameter can remain active indefinitely.
Alternatively, certain sites may immediately disable HSTS by setting this value to 0.

- INCLUDESUBDOMAINS

The `includeSubDomains` parameter is an optional flag that tells the browser to enable HSTS for the website and all its subdomains. Because this setting applies broadly, we should ensure that all of our subdomains can support HTTPS before implementing the parameter.

- PRELOAD

An initial connection could potentially use a compromised HTTP connection. In response, Google has maintained the unofficial preload parameter.
Most major browsers support this security header, which significantly mitigates the risk. A website can add the preload parameter to the HSTS header and then register the domain to the preload service. Browsers can then check this reference list to determine whether their initial connections can use HTTPS.

## How to implement HSTS in Express (Node.js)

There are 2 options to implement HSTS in Express (Node.js) server.

- Option 1: Use `Helmet` Middleware

https://www.npmjs.com/package/helmet

![NPM Helmet](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hj3zt5wfe7x85zruptd0.png)

The first way we can set HSTS is via Helmet middleware with the app initialization:

```javascript
const app = express();
app.use(helmet());
app.use(helmet.hsts({
  maxAge: 300,
  includeSubDomains: true,
  preload: true
}));

app.use((req, res) => {
  res.send("Hello secure web!");
});
```

- Option 2: Manually Set the Header

Alternatively, we can manually set the HSTS header through a middleware that applies it to every reply:

```javascript
const app = express();

app.use((req, res) => {
  if (req.secure) {
    res.setHeader("Strict-Transport-Security", "max-age=300; includeSubDomains; preload");
  }
  res.send("Hello secure web!");
});
```

We can finish the code by creating the HTTP and HTTPS servers with the following code:

```javascript
const httpServer = http.createServer(app);
httpServer.listen(80, () => {
  console.log("HTTP server started.");
});

const httpsServer = https.createServer(credentials, app);
httpsServer.listen(443, () => {
  console.log("HTTPS server started");
});
```

Finally, run the server code and open a web browser.

```shell
$ node index.js
HTTP server started
HTTPS server started
```

![Chrome Dev Console](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r2yk2fivihigqzl88d5b.png)

For a full source code, please refer to this GitHub repo:
https://github.com/liyang51827/express-hsts-demo

This is my first post on DEV Community 👋.
Thanks for your kind attention! 🙏