DEV Community

Cover image for Using HTTP Strict Transport Security (HSTS) headers in Node.js server
Yang Li
Yang Li

Posted on

Using HTTP Strict Transport Security (HSTS) headers in Node.js server

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

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

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>


Enter fullscreen mode Exit fullscreen mode

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

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



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!");
});


Enter fullscreen mode Exit fullscreen mode
  • Option 2: Manually Set the Header

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



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!");
});


Enter fullscreen mode Exit fullscreen mode

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



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");
});


Enter fullscreen mode Exit fullscreen mode

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



$ node index.js
HTTP server started
HTTPS server started


Enter fullscreen mode Exit fullscreen mode

Chrome Dev Console

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! ๐Ÿ™

Top comments (0)