Using Helmet in Node.js to secure your application
Tags: node, webdev
Published: 2023-03-15 17:23:18

**Written by [Antonello Zanini](https://blog.logrocket.com/author/antonello-zanini/)✏️**

Express.js is the most popular web framework for developing web applications in Node.js. It comes with several features to make API development faster and easier. 

However, Express falls short when it comes to security, not ensuring an adequate level of protection from common vulnerabilities. Fortunately, it can be easily extended thanks to middleware, a set of functions executed during the processing of HTTP requests. 

Helmet.js is middleware-based technology that improves security by safeguarding HTTP headers returned by a Node.js app. Here, you will learn what Helmet is, why you need it, and how to integrate it into Node.js to secure HTTP headers in an Express.js application. We will cover:

*   [What is Helmet?](#what-helmet)
*   [Why do you need Helmet in a Node.js app?](#why-need-helmet-node-js-app)
    *   [Setting up a Node.js Express project](#setting-up-node-js-express-project)
    *   [Exploring Express security without Helmet](#exploring-express-security-without-helmet)
*   [Securing Express with Helmet](#securing-express-with-helmet)
    *   [Installing Helmet in your Express project](#installing-helmet-express-project)
    *   [Setting up Helmet in Express](#setting-up-helmet-express)
    *   [Exploring Express security with Helmet](#exploring-express-security-with-helmet)
*   [Configuring security headers in Helmet](#configuring-security-headers-helmet)
    *   [The`Content-Security-Policy`header](#content-security-policy-header)
    *   [The `Referrer-Policy` header](#referrer-policy-header)
    *   [The `Strict-Transport-Security`header](#strict-transport-security-header)
    *   [The `X-Content-Type-Options` header](#x-content-type-options-header)
    *   [The `X-Frame-Options` header](#x-frame-options-header)

Let’s dig into Helmet in Node.js!

## What is Helmet? <a name="what-helmet">

[Helmet.js is an open source JavaScript library](https://helmetjs.github.io/) that helps you secure your Node.js application by setting several HTTP headers. It acts as a middleware for [Express and similar technologies](https://blog.logrocket.com/nestjs-vs-express-js/), automatically [adding or removing HTTP headers](https://blog.logrocket.com/using-next-js-security-headers/) to comply with web security standards. 

Although not a silver bullet, Helmet makes it harder for attackers to exploit known vulnerabilities. It helps to protect Node.js Express apps from common security threats such as [Cross-Site Scripting (XSS)](https://developer.mozilla.org/en-US/docs/Glossary/Cross-site_scripting) and [click-jacking attacks](https://developer.mozilla.org/en-US/docs/Web/Security/Types_of_attacks#click-jacking). 

Helmet is particularly useful because Express applications do not come with security HTTP headers out of the box. This explains why [the `helmet` npm package](https://www.npmjs.com/package/helmet) has more than 2,000,000 weekly downloads, while its [GitHub repo boasts over 9.4k stars](https://github.com/helmetjs/helmet)! 

Let’s now dig deeper into why adopting Helmet in Node.js is so important.

## Why do you need Helmet in a Node.js app? <a name="why-need-helmet-node-js-app">

Without Helmet, default headers returned by Express expose sensitive information and make your Node.js app vulnerable to malicious actors. In contrast, using Helmet in Node.js protects your application from XSS attacks, Content Security Policy vulnerabilities, and other security issues. 

Let’s explore this question further through an example. You are going to set up a Node.js Express app and see what level of security its default HTTP headers offer.

### Setting up a Node.js Express project <a name="setting-up-node-js-express-project">

First, make sure you have Node.js and npm installed on your machine. If you aren’t set up yet, [start by downloading Node.js](https://nodejs.org/en/download/) and following the installation wizard to set it up. 

Now, let’s set up an Express project. Run the command below to create the `express-demo` project folder:

```bash
mkdir express-demo 
```

Then, enter the `express-demo` folder in your terminal by running the following:

```bash
cd express-demo
```

Launch the following command to initialize a default npm project:

```bash
npm init -y
```

[The `npm init` command](https://docs.npmjs.com/cli/v9/commands/npm-init) will configure a blank npm project for you. Note that the `-y` flag will automatically answer "yes" to all questions that npm would otherwise ask you during the process. 

You need to add Express to your project's dependencies. Install the Express module with the below:

```bash
npm install express
```

Then, create an `index.js` file in the `express-demo` folder and initialize it with these lines:

```javascript
// index.js

const express = require("express")

// creating an Express instance
const app = express()
const PORT = process.env.PORT || 3000

// initializing a basic API that
// returns the "Hello, World!" message
app.get("/", (req, res) => {
    res.json("Hello, World!")
})

// running the server
app.listen(PORT, () => {
    console.log(`Starting Express server on http://localhost:${PORT}`)
})
```

This is nothing more than a basic Express server setup with a single endpoint. 

Start the server with this command:

```bash
node index.js
```

The Express server should now be running at `http://localhost:3000`. Launch the command below in your terminal to test the `/` endpoint:

```bash
curl http://localhost:3000/
```

This should print the following output:

```bash
"Hello, World!"
```

Et voilà! You now have a working Express app!

### Exploring Express security without Helmet <a name="exploring-express-security-without-helmet">

Let's now use the demo application we just created to verify the default behavior of Express when it comes to security HTTP headers. 

Repeat the `curl` command above with the `--include` flag to get also the HTTP response headers:

```bash
curl http://localhost:3000/ --include
```

If you do not [have cURL installed](https://blog.logrocket.com/an-intro-to-curl-the-basics-of-the-transfer-tool/) on your computer, use an [HTTP client such as Postman](https://blog.logrocket.com/how-automate-api-tests-postman/) or [Insomnia](https://insomnia.rest/) to inspect the HTTP headers of the response. 

In the response header section, you should be seeing the following:

```json
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 15
ETag: W/"f-pob1Yw/KBE+3vrbZz9GAyq5P2gE"
Date: Fri, 20 Jan 2023 16:29:40 GMT
Connection: keep-alive
Keep-Alive: timeout=5
```

Note the `X-Powered-By` header. As with all headers that begin with `X-`, it is a non-standard header. Specifically, backend technologies generally use `X-Powered-By` to indicate the name and version number of the framework or library used by the server to generate the HTTP response. 

As recommended by [OWASP, a nonprofit foundation](https://owasp.org/) that works to improve web security, [`X-Powered-By` should be omitted](https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-powered-by). This is because you should never give attackers details about your tech stack. Otherwise, they could use that info to exploit known vulnerabilities in that framework or library. 

Now, let’s check what the [Security Headers online project](https://securityheaders.com/) has to say about the security level of your Express app. 

Since Security Headers only allows you to test public addresses, you need to make your local Express server publicly available. You can either deploy your Express app to a server or [take advantage of ngrok](https://ngrok.com/), which allows you to expose a local web server to the Internet. 

[Download ngrok](https://ngrok.com/download), extract it, and open the folder in the terminal. Then, launch the following command to host your local server with ngrok:

```bash
ngrok http 3000
```

You should get a similar result to the below: ![Terminal With Black Background Showing Session Info After Launching Command To Host Local Server With Ngrok](https://blog.logrocket.com/wp-content/uploads/2023/03/img1-Session-info-ngrok-local-server.png) The “Forwarding” field contains the URL to publicly access your local server. Since the endpoint to test is `/`, copy the URL, add a trailing `/` character, and paste it in the Security Headers input as shown below: ![Security Headers Online Service With Blue Background And Text Field With Pasted Url For Public Access To Local Server](https://blog.logrocket.com/wp-content/uploads/2023/03/img2-Security-Headers-online-service.png) 

Click the “Scan” button, and you should get the following result: ![Security Headers Online Service With Red Background And F Grade On Security Report Summary](https://blog.logrocket.com/wp-content/uploads/2023/03/img3-Security-Headers-F-report.png) 

As you can see here, the Security Headers project gives the demo app a worrying “F” grade. The reason is that the API response developed with Express without Helmet is missing all the most relevant HTTP security headers. 

This is also why the official Express ["Production Best Practices: Security"](https://blog.logrocket.com/build-shopify-app-with-node-js/) page mentions adopting Helmet as a best practice for production applications. Let’s now learn how to integrate Helmet in Node.js!

## Securing Express with Helmet <a name="securing-express-with-helmet">

Let’s expand the Express demo project implemented earlier to secure it with Helmet. As you are about to learn, this only takes a couple of lines of code.

### Installing Helmet in your Express project <a name="installing-helmet-express-project">

First, you need to add Helmet.js to your project’s dependencies. Install the `helmet` npm package with the following command:

```bash
npm i helmet
```

Your `package.json` file will now contain `helmet` in [the `dependencies` fields](https://docs.npmjs.com/cli/v9/configuring-npm/package-json#dependencies).

### Setting up Helmet in Express <a name="setting-up-helmet-express">

Integrating Helmet into your Node.js Express app is simple. In case of problems, [follow the official guide](https://github.com/helmetjs/helmet#quick-start). 

In your `index.js` file, import `helmet` with the following command:

```javascript
const helmet = require("helmet")
```

Now, register `helmet` in your Express application with the below:

```bash
app.use(helmet())
```

Remember that `helmet()` is nothing more than [an Express middleware](https://expressjs.com/en/guide/using-middleware.html). Specifically, the top-level `helmet()` function is a wrapper of 15 sub-middlewares. So, by registering `helmet()`, you are adding 15 Express middlewares to your apps. 

Note that each middleware takes care of setting one HTTP security header. Your `index.js` file will now look as follows:

```javascript
// index.js

const express = require("express")
const helmet = require("helmet")

const PORT = process.env.PORT || 3000

const app = express()

// enabling the Helmet middleware
app.use(helmet())

// initializing a basic API that
// returns the "Hello, World!" message
app.get("/", (req, res) => {
    res.json("Hello, World!")
})

// running the server
app.listen(PORT, () => {
    console.log(`Starting Express server on http://localhost:${PORT}`)
})
```

That's it! Adding Helmet to your Node.js app only involve two lines of code!

### Exploring Express security with Helmet <a name="exploring-express-security-with-helmet">

Let’s repeat the security test performed earlier. Stop your Node.js local server and relaunch it with this command:

```bash
node index.js 
```

Repeat the `curl` command or use an HTTP client to see the HTTP headers of the new response:

```bash
curl http://localhost:3000/ --include
```

The response HTTP header section will now include the following:

```json
Content-Security-Policy: default-src 'self';base-uri 'self';font-src 'self' https: data:;form-action 'self';frame-ancestors 'self';img-src 'self' data:;object-src 'none';script-src 'self';script-src-attr 'none';style-src 'self' https: 'unsafe-inline';upgrade-insecure-requests
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Resource-Policy: same-origin
X-DNS-Prefetch-Control: off
X-Frame-Options: SAMEORIGIN
Strict-Transport-Security: max-age=15552000; includeSubDomains
X-Download-Options: noopen
X-Content-Type-Options: nosniff
Origin-Agent-Cluster: ?1
X-Permitted-Cross-Domain-Policies: none
Referrer-Policy: no-referrer
X-XSS-Protection: 0
Content-Type: application/json; charset=utf-8
Content-Length: 15
ETag: W/"f-pob1Yw/KBE+3vrbZz9GAyq5P2gE"
Date: Fri, 20 Jan 2023 18:15:32 GMT
Connection: keep-alive
Keep-Alive: timeout=5
```

As you can see, there are many new HTTP headers in the response. Also, `X-Powered-By` was removed. 

If you shut down ngrok, run it again with the following:

```bash
ngrok http 3000
```

Otherwise, visit the Security Headers website right away. Just like before, copy the URL provided by ngrok and click "Scan.” This time, the outcome will be different: ![Security Headers Online Service With Green Background And A Grade On Security Report Summary](https://blog.logrocket.com/wp-content/uploads/2023/03/img4-Security-Headers-A-report.png) 

Note that the API response now involves all the major HTTP Security headers, except for [`Permissions-Policy`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Permissions_Policy). This allowed the Express app to go from an “F” grade to an “A” grade. What a huge change with just two lines of code! 

Also, as [mentioned on GitHub](https://github.com/helmetjs/helmet/issues/234) by one of the lead developers of Helmet.js, Helmet does not automatically support `Permissions-Policy` only because the header specification is still in a draft state. So, this may change soon.

## Configuring security headers in Helmet <a name="configuring-security-headers-helmet">

As you just learned, Helmet sets a lot of headers by default. Since security policies change over time, it is critical to always keep `helmet` up-to-date. This way, the security headers introduced by Helmet will always comply with security standards. 

All of those headers have an impact on the security of your application, but some are more relevant than others. Let’s now focus on the most important security headers to understand why they are useful, how Helmet treats them by default, and how you can configure them with Helmet.

### The `Content-Security-Policy` header <a name="content-security-policy-header">

[Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP), also known as CSP, is a security measure that helps you mitigate several attacks, such as cross-site scripting (XSS) and data injection attacks. 

Specifically, CSP allows you to specify what sources of content a web page is allowed to load and execute. For example, you can use CSP to block a web page from loading images and iframes from other websites. 

You can configure CSP through the [`Content-Security-Policy` HTTP header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy). By default, Helmet gives the `Content-Security-Policy` header the following value:

```json
Content-Security-Policy: default-src 'self';base-uri 'self';font-src 'self' https: data:;form-action 'self';frame-ancestors 'self';img-src 'self' data:;object-src 'none';script-src 'self';script-src-attr 'none';style-src 'self' https: 'unsafe-inline';upgrade-insecure-requests
```

With this policy, your web pages cannot load remote fonts or styles. This is because `font-src` and `style-src` are set to `self`, respectively. The CSP policy defined by Helmet by default is very restrictive, but you can change it with `contentSecurityPolicy()` as follows:

```json
// overriding "font-src" and "style-src" while
// maintaining the other default values
helmet.contentSecurityPolicy({
  useDefaults: true,
  directives: {
    "font-src": ["'self'", "external-website.com"],
    // allowing styles from any website
    "style-src": null,
  },
})
```

`useDefaults` applies the default values. Then, the following directives override the defaults. Set `useDefaults` to `false` to define a CSP policy from scratch.

### The `Referrer-Policy` header <a name="referrer-policy-header">

The [`Referrer-Policy` HTTP header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy) defines what data should be sent as referrer information in [the `Referer` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referer). By default, `Referer` generally contains the current URL from which an HTTP request is performed. 

For example, if you click on a link to a third-party website, `Referer` will contain the address of the current web page. As a result, the third-party website could use the header to track you or understand what you were visiting. 

If the current address contains private user information, the third-party site will be able to steal it from the `Referer` header. Because of this, even though this header is typically used for caching or analytics, it opens up some privacy concerns because it can leak sensitive information. 

Helmet sets it to `no-referrer` by default. This means that the `Referer` header will always be empty. So, requests performed by web pages served by your Node.js app will not include any referrer information. 

If you want to change this restrictive policy, you can do it with `refererPolicy` as below:

```json
// setting "Referrer-Policy" to "no-referrer"
app.use(
  helmet.referrerPolicy({
    policy: "no-referrer",
  })
)
```

### The `Strict-Transport-Security` header <a name="strict-transport-security-header">

The `Strict-Transport-Security` HTTP header, [also known as HSTS](https://developer.mozilla.org/en-US/docs/Glossary/HSTS), specifies that a site or resource should only be accessed via HTTPS. In detail, the `maxAge` parameter defines the number of seconds browsers should remember to prefer HTTPS over HTTP. 

By default, Helmet sets the `Strict-Transport-Security` header as follows:

```bash
max-age=15552000; includeSubDomains
```

Note that `15552000` seconds corresponds to 180 days, as well as that `includeSubDomains` extends the HSTS policy to all the site's subdomains. 

You can configure the `Strict-Transport-Security` header with the `hsts` Helmet function as follows:

```json
app.use(
  helmet.hsts({
    // 60 days
    maxAge: 86400,
    // removing the "includeSubDomains" option
    includeSubDomains: false,
  })
 )
```

### The `X-Content-Type-Options` header <a name="x-content-type-options-header">

The [`X-Content-Type-Options` HTTP header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options) defines that the [MIME types](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types) used in the [`Content-Type` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type) must be followed. This mitigates [MIME type sniffing](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types#mime_sniffing), which can lead to XSS attacks and cause other vulnerabilities. 

For example, attackers could hide HTML code in a `.png` file. The browser might perform MIME type sniffing to determine the content type of the resource. 

Since the file contains HTML code, the browser will determine that it is an HTML file rather than a JPG image. Thus, the browser will execute the attacker's code accordingly when rendering the page. 

By default, Helmet sets `X-Content-Type-Options` to `nosniff`. This disables and prevents MIME type sniffing. 

Note that the `noSniff()` Helmet function contained in `helmet()` does not accept parameters. If you want to disable this behavior and go against recommended security policies, you can prevent Helmet from importing `nosniff()` with the following:

```json
app.use(
  // not loading the noSniff() middleware
  helmet({
    noSniff: false,
  })
)
```

### The `X-Frame-Options` header <a name="x-frame-options-header">

The [`X-Frame-Options` HTTP response header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options) specifies whether or not a browser should be allowed to render a page in the `<frame>`, `<iframe>`, `<embed>` and `<object>` HTML elements. 

By preventing the content of your site from being embedded in other sites, you can avoid click-jacking attacks. A click-jacking attack involves tricking users into clicking on something different from what they perceive or expect. 

By default, Helmet sets `X-Frame-Options` to `SAMEORIGIN`. This allows a web page to be embedded in a frame on pages with the same origin as the page itself. You can set this header in Helmet with `frameguard()` as follows:

```json
// setting "X-Frame-Options" to "DENY"
app.use(
  helmet.frameguard({
    action: "deny",
  })
);
```

If you want to omit the `X-Frame-Options` header entirely, you can disable the `frameguard()` middleware with the following:

```json
app.use(
  // not including the frameguard() middleware
  helmet({
    frameguard: false,
  })
)
```

Note that this is not recommended for security reasons.

## Conclusion

In this article, you learned what Helmet.js is and why it plays such an important role when it comes to securing a Node.js app. 

As you saw here, APIs developed in Express do not involve security HTTP headers. So, default Express apps come with some security concerns. 

With Helmet, you can add a security layer to Node.js with just a single line of code, safeguarding your application from the most common attacks and vulnerabilities.

---

## 200’s only ✔️ Monitor failed and slow network requests in production

Deploying a Node-based web app or website is the easy part. Making sure your Node instance continues to serve resources to your app is where things get tougher. If you’re interested in ensuring requests to the backend or third party services are successful, [try LogRocket] (https://logrocket.com/signup/). 

[![LogRocket Network Request Monitoring](https://blog.logrocket.com/wp-content/uploads/2019/12/network-request-filter-2-1.png)](https://lp.logrocket.com/blg/signup) 

[LogRocket] (https://lp.logrocket.com/blg/signup) is like a DVR for web apps, recording literally everything that happens on your site. Instead of guessing why problems happen, you can aggregate and report on problematic network requests to quickly understand the root cause.

LogRocket instruments your app to record baseline performance timings such as page load time, time to first byte, slow network requests, and also logs Redux, NgRx, and Vuex actions/state. [Start monitoring for free] (https://lp.logrocket.com/blg/signup).