One place for hosting & domains

      Content

      How To Secure Node.js Applications with a Content Security Policy


      The author selected the Free Software Foundation to receive a donation as part of the Write for DOnations program.

      Introduction

      When the browser loads a page, it executes a lot of code to render the content. The code could be from the same origin as the root document, or a different origin. By default, the browser does not distinguish between the two and executes any code requested by a page regardless of the source. Attackers use this exploit to maliciously inject scripts to the page, which are then executed because the browser has no way of determining if the content is harmful. These situations are where a Content Security Policy (CSP) can provide protection.

      A CSP is an HTTP header that provides an extra layer of security against code-injection attacks, such as cross-site scripting (XSS), clickjacking, and other similar exploits. It facilitates the creation of an “allowlist” of trusted content and blocks the execution of code from sources not present in the allowlist. It also reports any policy violations to a URL of your choice, so that you can keep abreast of potential security attacks.

      With the CSP header, you can specify approved sources for content on your site that the browser can load. Any code that is not from the approved sources, will be blocked from executing, which makes it considerably more difficult for an attacker to inject content and siphon data.

      In this tutorial, you’ll review the different protections the CSP header offers by implementing one in an example Node.js application. You’ll also collect JSON reports of CSP violations to catch problems and fix exploits quickly.

      Prerequisites

      To follow this tutorial, you will need the following:

      • A recent version of Node.js installed on your machine. Follow the steps in the relevant How To Install Node.js tutorial for your operating system to set up a Node.js development environment.

      You should also use a recent browser version, preferably Chrome, as it has the best support for CSP level 3 directives at the time of writing this article (November 2020). Also, make sure to disable any third-party extensions while testing the CSP implementation so that they don’t interfere with the violation reports rendered in the console.

      Step 1 — Setting Up the Demo Project

      To demonstrate the process of creating a Content Security Policy, we’ll work through the entire process of implementing one for this demo project. It’s a one-page website with a variety of content that approximates a typical website or application. It includes a small Vue.js application, YouTube embeds, and some images sourced from Unsplash. It also uses Google fonts and the Bootstrap framework, which is loaded over a content delivery network (CDN).

      In this step, you’ll set up the demo project on your test server or local machine and view it in your browser.

      First, clone the project to your filesystem using the following command:

      • git clone https://github.com/do-community/csp-demo

      Once your project directory is set up, change into it with the following command:

      Next, install the dependencies specified in the package.json file with the next command. You use the express package to set up the web server, while nodemon helps to automatically restart the node application when it detects file changes in the directory:

      Once the dependencies you’ve installed the dependencies, enter the following command to start the web server on port 5500:

      You can now visit your_server_ip:5500 or localhost:5500 in your browser to view the demo page. You will find the text Hello World!, a YouTube embed, and some images on the page.

      CSP Demo

      In the next section, we’ll implement a CSP policy that covers only the most basic protections. We’ll then build on that in the subsequent sections as we uncover all the legitimate resources that we need to allow on the page.

      Step 2 — Implementing a Basic CSP

      Let’s go ahead and write a CSP policy that restricts fonts, images, scripts, styles, and embeds to those originating from the current host only. The following is the response header that achieves this:

      Content-Security-Policy: default-src 'self'; font-src 'self'; img-src 'self'; script-src 'self'; style-src 'self'; frame-src 'self';
      

      Here’s an explanation of the policy directives in this header:

      • font-src defines the sources from where fonts can be loaded from.
      • img-src defines the sources from which image loading is permitted.
      • script-src controls any script-loading privileges on a web page.
      • style-src is the directive for allowing stylesheet sources.
      • frame-src defines allowed sources for frame embeds. (It was deprecated in CSP level 2, but reinstated in level 3.)
      • default-src defines a fallback policy for certain directives if they are not explicitly specified in the header. Here is a complete list of the directives that fall back to default-src.

      In this example, all the specified directives are assigned the 'self' keyword in their source list. This indicates that only resources from the current host (including the URL scheme and port number) should be allowed to execute. For example, script-src 'self' allows the execution of scripts from the current host, but it blocks all other script sources.

      Let’s go ahead and add the header to our Node.js project.

      Leave your app running and open a new terminal window to work with your server.js file:

      Next, add the CSP header from the example in an Express middleware layer. This ensures that you’re including the header in every response from the server:

      server.js

      const express = require('express');
      const bodyParser = require('body-parser');
      const path = require('path');
      const app = express();
      
      app.use(function (req, res, next) {
        res.setHeader(
          'Content-Security-Policy',
          "default-src 'self'; font-src 'self'; img-src 'self'; script-src 'self'; style-src 'self'; frame-src 'self'"
        );
        next();
      });
      
      app.use(bodyParser.json());
      app.use(express.static(path.join(__dirname)));
      
      app.get('/', (req, res) => {
        res.sendFile(path.join(__dirname + '/index.html'));
      });
      
      const server = app.listen(process.env.PORT || 5500, () => {
        const { port } = server.address();
        console.log(`Server running on PORT ${port}`);
      });
      

      Save the file and reload the project in your browser. You’ll notice that the page is completely broken.

      Our CSP header is working as expected and all the external sources that we included on the page have been blocked from loading because they violate the defined policy. However, this is not an ideal way to test a brand-new policy since it can break a website when violations occur.

      Broken page after adding basic CSP

      This is why the Content-Security-Policy-Report-Only header exists. You can use it instead of Content-Security-Policy to prevent the browser from enforcing the policy, while still reporting the violations that occur—this means that you can refine the policy without putting your site at risk. Once you’re happy with your policy, you can switch back to the enforcing header so that the protections are activated.

      Go ahead and replace the Content-Security-Policy header with Content-Security-Policy-Report-Only in your server.js file:

      Add the following highlighted code:

      server.js

      . . .
      app.use(function (req, res, next) {
        res.setHeader(
          'Content-Security-Policy-Report-Only',
          "default-src 'self'; font-src 'self'; img-src 'self'; script-src 'self'; style-src 'self'; frame-src 'self'"
        );
        next();
      });
      . . .
      

      Save the file and reload the page in your browser. The page returns to a working state, but the browser console still reports the CSP violations. Each violation is prefixed with [Report Only] to indicate that the policy is not enforced.

      Report only mode is now active

      In this section, we created the initial implementation of our CSP and set it to report-only mode so that we can refine it without causing the site to break. In the next section, we’ll fix the violations triggered through our initial CSP.

      Step 3 — Fixing Policy Violations

      The policy we implemented in the previous section triggered several violations because we restricted all resources to the origin only—however, we have several third-party assets on the page.

      The two ways to fix CSP violations are: approving the sources in the policy, or removing the code that triggers the violations. Since legitimate resources are triggering all the violations, we’ll concentrate mainly on the former option in this section.

      Open up your browser console. It will display all the current violations of the CSP. Let’s fix each of these issues.

      Allowing the Stylesheets

      The first two violations in the console are from the Google fonts and Bootstrap stylesheets, which you’re loading from https://fonts.googleapis.com and https://cdn.jsdelivr.net respectively. You can allow them both on the page through the style-src directive:

      style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net;
      

      This specifies that CSS files from the origin host, https://fonts.googleapis.com, and https://cdn.jsdelivr.net, should be executed on the page. This policy is quite broad, because it allows any stylesheet from the allowlist domains (not just the ones you’re currently using).

      We can be more specific by using the exact file or directory we’d like to allow instead:

      style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css;
      

      Now, it will only allow the exact, specified stylesheet to execute. It will block all other stylesheets—even if they originate from https://cdn.jsdelivr.net.

      You can update the CSP header like the following with the updated style-src directive. By the time you reload the page, both violations will be resolved:

      server.js

      . . .
      app.use(function (req, res, next) {
        res.setHeader(
          'Content-Security-Policy-Report-Only',
          "default-src 'self'; font-src 'self'; img-src 'self'; script-src 'self'; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css; frame-src 'self';"
        );
        next();
      });
      . . .
      

      Allowing the Image Sources

      The images you’re using on the page are from a single source: https://images.unsplash.com. Let’s allow it through the img-src directive like the following:

      server.js

      . . .
      app.use(function (req, res, next) {
        res.setHeader(
          'Content-Security-Policy-Report-Only',
          "default-src 'self'; font-src 'self'; img-src 'self' https://images.unsplash.com; script-src 'self'; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css; frame-src 'self'"
        );
        next();
      });
      . . .
      

      Allowing the Youtube Embed

      You can allow valid sources for nested browsing contexts, which use elements such as <iframe>, through the frame-src directive. If this directive is absent, the browser will look for the child-src directive, which subsequently falls back to the default-src directive.

      Our current policy limits frame embeds to the origin host. Let’s add https://www.youtube.com to the allowlist so that the CSP doesn’t block Youtube embeds from loading once we enforce the policy:

      frame-src 'self' https://www.youtube.com;
      

      Note that the www subdomain is significant here. If you have an embed from https://youtube.com, it will be blocked according to this policy unless you also add https://youtube.com to the allowlist:

      frame-src 'self' https://www.youtube.com https://youtube.com;
      

      Here’s the updated CSP header. Change the frame-src directive as following:

      server.js

      . . .
      app.use(function (req, res, next) {
        res.setHeader(
          'Content-Security-Policy-Report-Only',
          "default-src 'self'; font-src 'self'; img-src 'self' https://images.unsplash.com; script-src 'self'; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css; frame-src 'self' https://www.youtube.com https://youtube.com;"
        );
        next();
      });
      . . .
      

      Allowing the Font Files

      Google fonts violations

      The Google fonts stylesheet contain references to several font files from https://fonts.gstatic.com. You will find that these files currently violate the defined font-src policy (currently 'self'), so you need to account for them in your revised policy:

      font-src 'self' https://fonts.gstatic.com;
      

      Update the font-src directive in the CSP header as following:

      server.js

      . . .
      app.use(function (req, res, next) {
        res.setHeader(
          'Content-Security-Policy-Report-Only',
          "default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self' https://images.unsplash.com; script-src 'self'; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css; frame-src 'self' https://www.youtube.com https://youtube.com;"
        );
        next();
      });
      . . .
      

      Once you reload the page, the console will no longer report violations for the Google fonts files.

      Allowing the Vue.js Script

      A Vue.js script loaded over a CDN is rendering the Hello world! text at the top of the page. We’ll allow its execution on the page through the script-src directive. As mentioned earlier, it’s important to be specific when allowing CDN sources so we don’t open up our site to other possible malicious scripts that are hosted on that domain.

      script-src 'self' https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js;
      

      In this example, you’re only allowing the exact script at that URL to execute on the page. If you want to switch between development and production builds, you’ll need to add the other script URL to the allowlist as well, or you can allow all the resources present in the /dist location, to cover both cases at once:

      script-src 'self' https://cdn.jsdelivr.net/npm/[email protected]/dist/;
      

      Here’s the updated CSP header with the relevant changes:

      server.js

      . . .
      app.use(function (req, res, next) {
        res.setHeader(
          'Content-Security-Policy-Report-Only',
          "default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self' https://images.unsplash.com; script-src 'self' https://cdn.jsdelivr.net/npm/[email protected]/dist/; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css; frame-src 'self' https://www.youtube.com https://youtube.com;"
        );
        next();
      });
      . . .
      

      At this point, we’ve successfully allowed all the external files and scripts that our page relies on. But we still have one more CSP violation to resolve due to the presence of an inline script on the page. We’ll explore a few solutions to this problem in the next section.

      Step 4 — Handling Inline Sources

      Although you can approve inline code (such as JavaScript code in a <script> tag) within a CSP using the 'unsafe-inline' keyword, it is not recommended because it greatly increases the risk of a code-injection attack.

      This example policy allows the execution of any inline script on the page, but this is not safe for the aforementioned reasons.

      script-src 'self' 'unsafe-inline' https://unpkg.com/[email protected]/dist/;
      

      The best way to avoid using unsafe-inline is to move the inline code to an external file and reference it that way. This is a better approach for caching, minification, and maintainability, and it also makes the CSP easier to modify in the future.

      However, if you absolutely must use inline code, there are two major ways to add them to your allowlist safely.

      Option 1 — Using a Hash

      This method requires that you calculate a SHA hash that is based on the script itself and then add it to the script-src directive. In recent versions of Chrome, you don’t even need to generate the hash yourself as it’s already included in the CSP violation error in the console:

      [Report Only] Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self' https://unpkg.com/[email protected]/dist/". Either the 'unsafe-inline' keyword, a hash ('sha256-INJfZVfoUd61ITRFLf63g+S/NJAfswGDl15oK0iXgYM='), or a nonce ('nonce-...') is required to enable inline execution.
      

      The highlighted section here is the exact SHA256 hash that you would need to add to the script-src directive to allow the execution of the specific inline script that triggered the violation.

      Copy the hash and add it to your CSP as follows:

      script-src 'self' https://unpkg.com/[email protected]/dist/ 'sha256-INJfZVfoUd61ITRFLf63g+S/NJAfswGDl15oK0iXgYM=';
      

      The disadvantage to this approach is that if the contents of the script changes, the generated hash will be different, which will trigger a violation.

      Option 2 — Using a Nonce

      The second way to allow the execution of inline code is by using a nonce. These are random strings that you can use to allow a complete block of code regardless of its content.

      Here’s an example of a nonce value in use:

      script-src 'self' https://unpkg.com/[email protected]/dist/ 'nonce-EDNnf03nceIOfn39fn3e9h3sdfa'
      

      The value of the nonce in the CSP must match the nonce attribute on the script:

      <script nonce="EDNnf03nceIOfn39fn3e9h3sdfa">
        // Some inline code
      </script>
      

      Nonces must be unguessable and dynamically generated each time the page is loaded so that an attacker is unable to use them for the execution of a malicious script. If you decide to implement this option, you can use the crypto package to generate a nonce as following:

      const crypto = require('crypto');
      let nonce = crypto.randomBytes(16).toString('base64');
      

      We will opt for the hash method in this tutorial since it’s more practical for our use case.

      Update the script-src directive in your CSP header to include the SHA256 hash of the sole inline script as shown following:

      server.js

      . . .
      app.use(function (req, res, next) {
        res.setHeader(
          'Content-Security-Policy-Report-Only',
          "default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self' https://images.unsplash.com; script-src 'self' https://unpkg.com/[email protected]/dist/ 'sha256-INJfZVfoUd61ITRFLf63g+S/NJAfswGDl15oK0iXgYM='; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css; frame-src 'self' https://www.youtube.com https://youtube.com;"
        );
        next();
      });
      . . .
      

      This removes the final CSP violation error that the inline script triggers from the console.

      In the next section, we will monitor the effects of our CSP in a production environment.

      Step 5 — Monitoring Violations

      Once you have your CSP in place, it’s necessary to keep an eye on its effect once in use. For example, if you forget to allow a legitimate source in production or when an attacker is trying to exploit an XSS attack vector (which you need to identify and stop immediately).

      Without some form of active reporting in place, there’s no way to know of these events. This is why the report-to directive exists. It specifies a location that the browser should POST a JSON-formatted violation report to, in the event it has to take action based on the CSP.

      To use this directive, you need to add an additional header to specify an endpoint for the Reporting API:

      Report-To: {"group":"csp-endpoint","max_age":10886400,"endpoints":[{"url":"http://your_server_ip:5500/__cspreport__"}],"include_subdomains":true}
      

      Once that is set, specify the group name in the report-to directive as following:

      report-to csp-endpoint;
      

      Here’s the updated portion of the server.js file with the changes. Be sure to replace the <your_server_ip> placeholder in the Report-To header with your actual server IP address:

      server.js

      . . .
      app.use(function (req, res, next) {
        res.setHeader(
          'Report-To',
          '{"group":"csp-endpoint","max_age":10886400,"endpoints":[{"url":"https://<your_server_ip>:5500/__cspreport__"}],"include_subdomains":true}'
        );
        res.setHeader(
          'Content-Security-Policy-Report-Only',
          "default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self' https://images.unsplash.com; script-src 'self' https://cdn.jsdelivr.net/npm/[email protected]/dist/ 'sha256-INJfZVfoUd61ITRFLf63g+S/NJAfswGDl15oK0iXgYM='; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css; frame-src 'self' https://www.youtube.com https://youtube.com; report-to csp-endpoint;"
        );
        next();
      });
      . . .
      

      The report-to directive is intended to replace the now deprecated report-uri directive, but most browsers don’t support it yet (as of November 2020). So, for compatibility with current browsers while also ensuring compatibility with future browser releases support, you should specify both report-uri and report-to in your CSP. If the latter is supported, it will ignore the former:

      server.js

      . . .
      app.use(function (req, res, next) {
        res.setHeader(
          'Report-To',
          '{"group":"csp-endpoint","max_age":10886400,"endpoints":[{"url":"https://your_server_ip:5500/__cspreport__"}],"include_subdomains":true}'
        );
        res.setHeader(
          'Content-Security-Policy-Report-Only',
          "default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self' https://images.unsplash.com; script-src 'self' https://cdn.jsdelivr.net/npm/[email protected]/dist/ 'sha256-INJfZVfoUd61ITRFLf63g+S/NJAfswGDl15oK0iXgYM='; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css; frame-src 'self' https://www.youtube.com https://youtube.com; report-to csp-endpoint; report-uri /__cspreport__;"
        );
        next();
      });
      . . .
      

      The /__cspreport__ route needs to exist on the server as well; add this to your file like the following:

      server.js

      . . .
      app.get('/', (req, res) => {
        res.sendFile(path.join(__dirname + '/index.html'));
      });
      
      app.post('/__cspreport__', (req, res) => {
        console.log(req.body);
      });
      . . .
      

      Some browsers send the Content-Type of the report payload as application/csp-report, while others use application/json. If the report-to directive is supported, the Content-Type should be application/reports+json.

      To account for all the possible Content-Type values, you have to set up some configurations on your express server:

      server.js

      . . .
      app.use(
        bodyParser.json({
          type: ['application/json', 'application/csp-report', 'application/reports+json'],
        })
      );
      . . .
      

      At this point, any CSP violations will be sent to the /__cspreport__ route and subsequently logged to the terminal.

      You can try it out by adding a resource from a source that is not compliant with the current CSP, or modifying the inline script in the index.html file as shown following:

      index.html

      . . .
      <script>
        new Vue({
          el: '#vue',
          render(createElement) {
            return createElement('h1', 'Hello World!');
          },
        });
        console.log("Hello")
      </script>
      . . .
      

      This will trigger a violation because the hash of the script is now different from what you included in the CSP header.

      Here’s a typical violation report from a browser using the report-uri:

      {
        'csp-report': {
          'document-uri': 'http://localhost:5500/',
          referrer: '',
          'violated-directive': 'script-src-elem',
          'effective-directive': 'script-src-elem',
          'original-policy': "default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self' https://images.unsplash.com; script-src 'self' https://cdn.jsdelivr.net/npm/[email protected]/dist/ 'sha256-INJfZVfoUd61ITRFLf63g+S/NJAfswGDl15oK0iXgYM='; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css; frame-src 'self' https://www.youtube.com https://youtube.com; report-uri /__cspreport__;",
          disposition: 'report',
          'blocked-uri': 'inline',
          'line-number': 58,
          'source-file': 'http://localhost:5500/',
          'status-code': 200,
          'script-sample': ''
        }
      }
      

      The parts to this report are:

      • document-uri: The page the violation occurred on.
      • referrer: The page’s referrer.
      • blocked-uri: The resource that violated the page’s policy (an inline script in this case).
      • line-number: The line number where the inline code begins.
      • violated-directive: The specific directive that was violated. (script-src-elem in this case, which falls back to script-src.)
      • original-policy: The complete policy of the page.

      If the browser supports the report-to directive, the payload should have a similar structure to what is following. Notice how it’s different from the report-uri payload while still carrying the same information:

      [{
          "age": 16796,
          "body": {
              "blocked-uri": "https://vimeo.com",
              "disposition": "enforce",
              "document-uri": "https://localhost:5500/",
              "effective-directive": "frame-src",
              "line-number": 58,
          'original-policy': "default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self' https://images.unsplash.com; script-src 'self' https://cdn.jsdelivr.net/npm/[email protected]/dist/ 'sha256-INJfZVfoUd61ITRFLf63g+S/NJAfswGDl15oK0iXgYM='; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css; frame-src 'self' https://www.youtube.com https://youtube.com; report-uri /__cspreport__;",
              "referrer": "",
              "script-sample": "",
              "sourceFile": "https://localhost:5500/",
              "violated-directive": "frame-src"
          },
          "type": "csp",
          "url": "https://localhost:5500/",
          "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36"
      }]
      

      Note: The report-to directive is supported only in secure contexts, which means that you need to set up your Express server with a valid HTTPS certificate, otherwise you won’t be able to test or use it.

      In this section, we successfully set up CSP monitoring on our server so that we can detect and fix problems quickly. Let’s go ahead and finish this tutorial by enforcing the final policy in the next step.

      Step 6 — Publishing the Final Policy

      Once you’re confident your CSP is set up correctly (ideally after leaving it in production for a few days or weeks in report-only mode), you can enforce it by changing the CSP header from Content-Security-Policy-Report-Only to Content-Security-Policy.

      In addition to reporting the violations, this will stop unauthorized resources from being executed on the page, leading to a safer experience for your visitors.

      Here is the final version of the server.js file:

      server.js

      const express = require('express');
      const bodyParser = require('body-parser');
      const path = require('path');
      const app = express();
      
      app.use(function (req, res, next) {
        res.setHeader(
          'Report-To',
          '{"group":"csp-endpoint","max_age":10886400,"endpoints":[{"url":"http://your_server_ip:5500/__cspreport__"}],"include_subdomains":true}'
        );
        res.setHeader(
          'Content-Security-Policy',
          "default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self' https://images.unsplash.com; script-src 'self' https://cdn.jsdelivr.net/npm/[email protected]/dist/ 'sha256-INJfZVfoUd61ITRFLf63g+S/NJAfswGDl15oK0iXgYM='; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css; frame-src 'self' https://www.youtube.com https://youtube.com; report-to csp-endpoint; report-uri /__cspreport__;"
        );
        next();
      });
      
      app.use(
        bodyParser.json({
          type: [
            'application/json',
            'application/csp-report',
            'application/reports+json',
          ],
        })
      );
      app.use(express.static(path.join(__dirname)));
      
      app.get('/', (req, res) => {
        res.sendFile(path.join(__dirname + '/index.html'));
      });
      
      app.post('/__cspreport__', (req, res) => {
        console.log(req.body);
      });
      
      const server = app.listen(process.env.PORT || 5500, () => {
        const { port } = server.address();
        console.log(`Server running on PORT ${port}`);
      });
      

      Browser support
      The CSP header is supported in all browsers with the exception of Internet Explorer, which uses the non-standard X-Content-Security-Policy header instead. If you need to support IE, you have to issue the CSP twice in the response headers.

      The latest version of the CSP spec (level 3) also introduced some newer directives that are not well supported at the moment. Examples include the script-src-elem and prefetch-src directives. Make sure to use the appropriate fallbacks when setting up those directives to ensure the protections remain active in browsers that have not caught up yet.

      Conclusion

      In this article, you’ve set up an effective Content Security Policy for a Node.js application and monitored the violations. If you have an older or more complex website, it will require a wider policy setup that covers all the bases. However, setting up a thorough policy is worth the effort as it makes it a lot harder for an attacker to exploit your website to steal user data.

      You can find the final code from this tutorial in this GitHub repository.

      For more security-related articles, check out our Security topic page. If you would like to learn more about working with Node.js, you can read our How To Code in Node.js series.



      Source link

      20 Fail-Proof Ways to Come Up With New Content Ideas


      Running out of ideas might be one of the worst situations when you depend on content to drive your business. You probably never imagined this would happen when first starting your online business. However, realizing you have nothing to say when facing a blank blog post is frustrating and panic-inducing.

      But don’t worry. You have plenty to say!

      Everyone gets hit with a case of “blogger’s block” once in a while. Fortunately, there are lots of tried and true methods for getting back in sync with your editorial calendar.

      In this post, we’ll talk a little about why having fresh content matters. Then, we’ll share 20 fail-proof ways to come up with new content ideas. Let’s get started!

      The Best Content Marketing Idea?

      Partner with DreamHost! Our automatic updates and strong security defenses take server management off your hands so you can focus on managing your blog, not downtime.

      Why Creating New Content Matters

      Content creation is a key aspect of inbound marketing, but it’s also how your audience gets to know you. Your next potential customer will only know you as an online persona. As such, you’ll want to take advantage of every opportunity you have to establish yourself as an expert in your niche. Providing new, insightful content to your audience is an efficient way to do this.

      Additionally, companies that post more content get more traffic. Publishing 16 or more blog posts a month can help you pull in nearly four times the traffic of your less prolific competition.

      Of course, content creation isn’t just about what’s on your website.

      You’ll also want to have steady social media content to help promote posts, articles, and videos. Social media is also an effective way to let your current followers know you have something new to share and bring fresh eyes to your content.

      Finally, keep in mind the trust factor when it comes to digital consumerism. Without the benefit of a physical storefront, if you’ve stopped producing content, it can be hard to tell if you’re still around. An e-commerce website with a blog and social media that hasn’t been updated in six months is unlikely to inspire confidence in potential customers.

      20 Fail-Proof Ways to Come Up With New Content Ideas

      Now we’ve covered why developing new content is so important, let’s get to work with 20 fail-proof ways to create new content ideas. There are plenty to get through, so let’s begin!

      1. Create Topics in Bunches

      Coming up with a topic under pressure can be stressful, and the relief you feel when you come up with something might be enough to make you stop for the day. However, you may want to try extending that brainstorming session and coming up with a few ideas at once.

      Batching similar tasks together is a tried and tested productivity technique. It can keep you focused and cut down on multitasking. What’s more, you can better develop themes in your content, which will likely come in handy to keep readers engaged.

      So pour some coffee, set a timer, and away you go!

      If you can, try to come up with enough topics to last a month or two. You can plug your new topics into your content calendar and make a plan for your next brainstorming session while you’re at it.

      2. Scour Social Media

      Social media could be a continuous source of topic ideas. Since Americans spend about two hours a day on social media, chances are your followers will be online discussing what they’re currently interested in. By doing a bit of virtual eavesdropping, you can kickstart your content idea generation.

      To start, look at what’s trending among your current followers. Take note of questions they’re asking and topics they’re interested in — but don’t stop there. Instead, let yourself go down a rabbit hole. Who are the other people and brands that your fans follow, and what hashtags are they using? All of this information can be used as fodder for future content.

      3. Review Your Blog Comments

      Engaging with your readers in your comments section is an effective way of building a relationship with them. However, you can also read through comments to develop new ideas.

      Your readers are likely from different parts of the world and have very different life experiences from you. So they may be leaving unique insights into and observations about your post’s subject matter. They may even be asking questions that you can answer in future blog posts.

      Interacting regularly with your readers can make it more likely that they’ll leave thoughtful comments. After all, if you take the time to respond, it’s more rewarding for them to share their thoughts. You might also give them some encouragement with a strong Call To Action (CTA) at the end of your posts.

      4. Conduct Interviews

      Interviews with an expert in your niche are a versatile way to help develop content for your blog or social media channels. The material you get from a single interview can bolster your content calendar for a week or two.

      First, the material lends itself to multitasking. Of course, you can publish the interview as a blog post. However, you might also shoot some video and share short clips of the highlights.

      Also, as there’s likely some overlap in your audiences, you may gain new followers from your guest. You might try creating a few easily-shared social media graphics for your interviewee to post on their channels.

      Try reaching out to a personality in your industry to see if they’re open to being interviewed. You might want to try asking some of the people your audience follows on social media. If you’re concerned about coming up with questions, this could be a good time to poll your readers to see what they would ask.

      5. Check Out Competitor Sites

      Your competitors’ sites are a potential goldmine of content ideas. After all, they’re targeting the same population as you!

      Of course, you can start with their blog to see if they’ve covered any topics you haven’t thought of. If you find ideas this way, try to outdo them by creating longer and better content (otherwise known as the Skyscraper Technique). However, take care not to plagiarize. There’s nothing wrong with using their content as inspiration, but be sure your words are your own.

      Next, head for the comments sections. Just like your own audience, your competition’s readers are likely asking valuable questions and sharing thoughts that you can use as a jumping-off point.

      While you’re on their site, you might also want to sign up for their newsletter. They may offer additional content to subscribers that you can’t access on the website.

      6. Google Search Suggestions

      Google may have some great ideas for your next topic. This tactic can be incredibly valuable, as you don’t need to provide much information. Also, the suggestions you get back will be relevant and mostly optimized as they’re based on actual searches.

      Start by typing in a general idea or even just your niche. You’ll get a list of potential topics. You might want to log out of your Google account first or use a private browsing tab, so your past search history doesn’t influence the results.

      Google suggestions based on a search for baking

      Also, check out the People Also Ask and Searches Related To… sections. These are two more places to find topic and keyword inspiration.

      Google searches related to baking

      You may want to incorporate this strategy into your topic brainstorming sessions. If you come up with one good idea, Google might help by giving you five more.

      7. Connect Your Brand to Current Events

      Be on the lookout for any current events you may be able to relate to your brand. While your audience may not be especially interested in the event itself, a clever and relevant tie-in could take advantage of trending searches and hashtags.

      This can be an excellent strategy for social media posts, as they’re shorter and more in-the-moment. Keep in mind, although this tactic can be a lot of fun, you’ll want to exercise some caution to avoid tying your brand to anything overly controversial.

      You might also stick to your niche when discussing current events. You can spotlight any new developments on social media or dig into the latest happenings with a blog post.

      8. Create Product Reviews

      Even if you don’t sell a product, you can still review items your followers may find helpful. Product reviews are another versatile bit of content. You can quickly tweet out some praise, being sure to tag the relevant company when you do. Reviews also lend themselves well to video, as you can demonstrate the product and “humanize” your business.

      Start by thinking of things you use every day and if they might be useful to your audience. For example, if you’re a food blogger, you might not believe a scented candle relates to your niche. However, if you swear by it to get rid of lingering food smells in your apartment, your readers will likely appreciate the recommendation.

      Furthermore, you can ask your audience about the items they can’t live without. A monthly product review could quickly become one of your more popular features.

      9. Use a Topic Generator Platform

      If you’re still stumped, you can get an assist from a topic generator platform. You might try HubSpot’s Blog Ideas Generator.

      HubSpot’s Blog Ideas Generator

      This free tool can provide you with up to a year’s worth of weekly blog post topics. All you have to do is type in up to five different nouns and click on the Give Me Blog Ideas button. It may not offer cast-iron and fully-formed ideas; however, it can be a great starting point for developing more relevant content.

      10. Tell Personal Stories

      It’s highly unlikely that you’re the only person creating content in your niche. Your audience follows you because they like you. Chances are, they’ll enjoy learning more about your successes and failures.

      These stories may inspire your readers or make you more relatable to them. Best of all, since you’re pulling from your experience, there’s no research required.

      How you share your story is as personal as the story itself. You may want to write a narrative or simply list lessons you’ve learned over the years. You could even let your audience ask you questions on a Twitter chat or YouTube live stream. This is also an excellent opportunity to tell your brand’s story.

      11. Get Ideas From Industry Newsletters

      No matter your niche, it’s a pretty safe bet that there are a few respected newsletters dedicated to it. These periodicals are likely full of up to the minute details about topics your readers will be interested in.

      Try subscribing to some industry newsletters and use what they cover to help cultivate fresh content for your blog. You might also note who is writing the content and who is being interviewed for these publications. Following these people could lead to even more inspiration, or even an interview or two.

      Want to Learn How to Create Great Content?

      Whether you need help finding a target audience, crafting the ideal digital marketing strategy, or launching a podcast, we can help! Subscribe to our monthly digest so you never miss an article.

      12. Watch YouTube Videos

      Having your own YouTube channel is a great idea, but you can also use the platform to develop content ideas. You may start by watching videos in your niche and reading through comments for inspiration, similar to what you might do on a competitor’s blog.

      However, you can also try searching for some of your old topic ideas and then using the suggested videos to come up with new ones.

      YouTube’s suggested videos section

      You could also try looking at the most popular videos in your niche. This should give you a good idea of what your audience might be interested in.

      13. Stay Up to Date With New Products and Tech

      Regardless of the industry you’re in, new developments are likely happening all the time. Staying on top of new products and technology related to your niche can regularly help you generate new content ideas. If you subscribe to industry newsletters, you’ll be well-positioned to discuss these advancements.

      Try sharing this information with your audience on social media and get their thoughts on it. This is potentially an effective way to get a conversation started on your social platforms, which could spark even more ideas for you.

      14. Talk About Recent Studies

      Conducting original research is a great way to generate content, but it’s not always practical. Instead, let your readers know about new studies or survey results related to your niche. This strategy provides value to your audience and helps cement your reputation as an expert.

      While you should probably share these findings on social media quickly to capitalize on recency, you might build other content as well. For example, you could craft an in-depth blog post or shoot a video exploring the study’s potential impact.

      15. Refresh or Expand Your Old Content

      Speaking of studies, if you’ve included any in older content, it could be time for a refresh. Taking time to look through old content can help update facts and statistics for the sake of accuracy. Also, suppose the content is especially dated. In that case, you may have been trying to rank for different keywords or using poor Search Engine Optimization (SEO) practices.

      You might also look through your older blog posts to see if you can expand upon ideas. A small paragraph in an old article could be fodder for a brand new piece of content.

      Finally, you could browse your metrics to identify lower-performing posts that could benefit from some attention.

      16. Visit Online Forums

      If you’ve already mined your own comments section for reader questions, you might want to try some online forums next, such as Quora. This website is devoted to the asking and answering of questions and covers just about any topic you can think of.

      Alt-text: Questions about baking on Quora

      Type in your area of interest, and you can find a wealth of content inspiration. Look for questions you haven’t covered, or curate a few related questions and try writing an ultimate guide.

      You’re Cordially Invited

      Join DreamHost’s Facebook group to connect with like-minded website owners and get advice from peers and experts alike!

      17. Give Your Users a Survey

      To find out more about what your readers would like to see, try asking them directly. Your audience will be familiar with the types of content you produce and the topics you cover, so they’re in a great position to provide advice.

      This can be as formal or relaxed as you’d like. You can select individuals and ask them directly what they’re interested in, or just hold a conversation on Facebook to find out more about your audience. You could also try sending out a survey to your newsletter subscribers.

      18. Read Conference Agendas

      If you’ve had success checking on industry newsletters, you might also explore conference agendas. These can be full of interesting topics to research and knowledgeable people to follow or connect with.

      You can try reading through agendas for upcoming or even past industry events. If it’s being talked about at a conference, you should probably be talking about it as well. And once it’s safe to attend events again (thanks, COVID-19), you might consider writing a roundup for your followers.

      19. Help a Reporter Out (HARO)

      Help a Reporter Out (HARO) is a website that connects journalists with sources. While you can use this tool in the hopes of being included in a story, it’s also helpful for generating content ideas.

      The Help a Reporter Out homepage

      Signing up for HARO as a source will get you three emails a day full of potential content topics. You’ll receive pitch requests every weekday, which you can mine for potential content ideas.

      20. Browse Amazon’s Best Sellers in Your Industry

      Amazon lists the top-selling books by industry. You might find your next read there, but it can also give you a sense of what people want to learn about. The list is based on sales and is updated hourly.

      Amazon’s best selling books in the cookbooks, food, and wine category

      You can pull up a list of relevant titles and start judging books by their covers (and table of contents). Try scanning summaries and chapter titles to help generate new content ideas.

      Blog Post Ideas Made Easy

      Consistently coming up with new content ideas can be one of the more stressful aspects of running an online business. However, it also has the potential to pay off. Besides better traffic and increased profits, content creation is how you build a community and engage with your customers.

      In this post, we shared 20 tips that should help you come up with some new content ideas. You might start close to home by reading through your blog comments and looking to improve or repurpose old content. Don’t forget to stay on top of current events in your industry through conference agendas and new product developments. If you’re still stuck, you might want to give a topic generator a try.

      Developing new content takes a lot of time and energy. The last thing you need to worry about is whether your web hosting is reliable. Have a look at our shared hosting plans so you can get your brain back in the content creation process.



      Source link

      How To Adjust the Content, Padding, Border, and Margins of an HTML Element With CSS



      Part of the Series:
      How To Build a Website With CSS

      This tutorial is part of a series on creating and customizing this website with CSS, a stylesheet language used to control the presentation of websites. You may follow the entire series to recreate the demonstration website and gain familiarity with CSS or use the methods described here for other CSS website projects.

      Before proceeding, we recommend that you have some knowledge of HTML, the standard markup language used to display documents in a web browser. If you don’t have familiarity with HTML, you can follow the first ten tutorials of our series How To Build a Website With HTML before starting this series.

      Introduction

      In this tutorial, you will learn about the CSS Box Model, a model used to refer to the content, padding, border, and margins of an HTML element. Understanding the CSS Box Model is helpful for adjusting the size of any of these parts of an HTML element and understanding how the size and position of elements is determined. This tutorial will begin by explaining each of the boxes of the CSS Box Model and then move on to a practical exercise on adjusting their values using CSS style rules.

      Diagram of CSS Box Model

      Prerequisites

      To follow this tutorial, make sure you have set up the necessary files and folders as instructed in a previous tutorial in this series How To Set Up You CSS and HTML Practice Project.

      The CSS Box Model

      An HTML element can be understood as a series of four overlapping boxes:

      • The content box is the innermost box where the text or image content is placed. By default, its size is frequently set by the size of the content it contains. It is also the only box in the box model whose value is typically not zero by default (if it contains content); in contrast, the padding, border, and margin of an element default to zero for many HTML elements (such as <p>, <h1>, and <img> elements) unless you specify otherwise. When you set values for the width and height of an element, you are typically changing the width and height of the content box.
      • The padding box is the second overlapping box, which consists of a transparent space that surrounds the content box. By default, the padding of many HTML elements is set to zero. Increasing the size of an element’s padding increases the distance between the content box and the border box.
      • The border box is the third overlapping box that surrounds the padding box. By default, the border value of most HTML elements is set to zero. Increasing the size of an element’s border increases the distance between the padding box and the margin box. Note that the color, thickness, and style of the border can be adjusted.
      • The margin box is the fourth and final overlapping box that consists of transparent space outside of the border of an element. By default, the margin value of some HTML elements is set to zero, though some elements have specified margin values as their default, such as the <h1> through <h6> heading tags. Margins of two different elements are also allowed to overlap sometimes in a behavior called margin collapse. When this happens, the margin size defaults to the size of whichever element’s margin is the largest.

      Now that you are familiar with the components of the CSS Box Model, you can practice styling these different boxes to explore how they work together to lay out and style an HTML element. You’ll start by creating a <div> element that contains text content and then adjust the values of each of these boxes to help demonstrate their location in an element.

      Adjusting The Content Size of an HTML Element With CSS

      First, make sure you have set up the necessary files and folders as instructed in a previous tutorial in this series How To Set Up You CSS and HTML Practice Project.

      Erase everything in your styles.css file (if the file contains content from previous tutorials) and add the following CSS rule to your styles.css file:

      styles.css

      .yellow-div {
        background-color:yellow;
      }
      

      Save the styles.css file. You have just created a class using the class selector yellow-div. Any <div> element you assign this class will have a yellow background color.

      Next, erase all the content in your index.html file (except for the first line of code: <link rel="stylesheet" href="https://www.digitalocean.com/community/tutorials/css/styles.css">) and add the following code snippet:

      index.html

      <div class="yellow-div">
      Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
      </div> 
      

      Save the file and load it in the browser. You should receive the following results:

      Webpage with text in yellow <div> containers

      Your webpage should display a yellow box that contains the text content you added to the HTML file. Currently, only the innermost box—the content box—has a size and value; the padding, border, and margin are all set to zero. Notice also that the width and height of the yellow box is automatically determined by the size of the text content inside the <div> container. Try adding or subtracting text content to experiment with how the size of the <div> container changes accordingly.

      Note: You can use Firefox’s Web Developer tools to view the box model of an HTML element and the values set for each box. Navigate to the Tools menu item in the top menu bar and select “Web Developer/Toggle Tools” from the dropdown menu. The Developer Tools should appear in the bottom of your window. Click the the arrow icon on the far left of the tool kit menu and then click on the element that you wish to inspect. The box model of the selected element will show up in the bottom right of the Developer Tools window pane. You may need to expand the window to view it.

      Gif of using Firefox Web Developer tools to view the box model of an element

      Next, let’s specify the width of the <div> container to study how that changes the presentation of the element in the browser. Add the following highlighted line to your CSS rule in your styles.css file to set the width to 500 pixels:

      styles.css

      .yellow-div {
        background-color:yellow;
        width: 500px;
      }
      

      Save the file and load it in your browser. Your <div> container should now be 500 pixels wide, with its height automatically adjusting to allow the text content to fit inside:

      Webpage with text `div` container that is 500 pixels wide

      Note that you can also specify the height of a <div> element instead and allow for the width to adjust automatically. Or you can specify both the height and width, but be aware that the content will spill over the <div> container if the <div> element is too small.

      How To Adjust the Padding Size of an HTML Element With CSS

      Next, let’s increase the padding size to study how it changes the display of the <div> element. Add the following highlighted line to your CSS rule in your styles.css file to set the padding to 25 pixels:

      styles.css

       .yellow-div {
        background-color:yellow;
        width: 500px;
        padding:25px;
      }
      

      Save the styles.css file and reload the index.html file in your browser. The size of the yellow box should have expanded to allow for 25 pixels of space between the text content and the perimeter of the box:

      Webpage with a yellow `<div>` container with width and padding specified

      You can change the size of the padding by adjusting the padding value size. You can also change the padding size of specific sides of the element by using the following properties: padding-left, padding-right, padding-top, padding-bottom. For example, try replacing the declaration padding:25px; in your styles.css file with the highlighted snippet below:

      styles.css

       .yellow-div {
        background-color:yellow;
        width: 500px;
        padding-left:25px;
        padding-right: 50px;
        padding-top: 100px;
        padding-bottom: 25px;
      }
      

      Save the styles.css file and load the index.html file in your browser. You should receive something like this:

      Webpage with a yellow <div> container with different padding values set for each side

      Knowing how to specify padding sizes for individual sides of an element can be useful when arranging content on a webpage.

      Adjusting the Border Size, Color, and Style of an HTML Element With CSS

      Let’s now practice setting values for the border of an HTML element. The border property lets you set the size, the color, and the style (such as solid, dashed, dotted, inset, and outset) of an HTML element. These three values are set by assigning them to the border property like so:

      selector {
        border: size style color;
      }
      

      Try adding the following highlighted declaration to add a solid black border that is five pixels wide:

      styles.css

      .yellow-div {
        background-color:yellow;
        width: 500px;
        padding: 25px;
        border: 5px solid black;
      }
      

      (You may want to erase your different padding declarations from the previous tutorial section and replace them with the single padding:25px; declaration to keep the ruleset manageable). Save the styles.css file and reload index.html in your browser to inspect the changes. Your yellow box should now have a border with the values you set in the CSS rule:

      Webpage with yellow `<div>`, padding, and border

      You can try changing the values to study how they change the display of the element in the browser. Like with padding, you can also specify the border side you’d like to adjust with the properties border-right, border-left, border-top, border-bottom.

      Adjusting the Margin Size of an HTML Element With CSS

      Next, let’s try adjusting the size of the margins of an element with CSS. In this exercise, we’ll give the margins a very large value so that it is easy to see how margin size is displayed in the browser. Add the following highlighted declaration to your ruleset in your styles.css file to set the margin to 100 pixels:

      styles.css

        .yellow-div {
        background-color:yellow;
        width: 500px;
        padding: 25px;
        border: 5px solid black;
        margin:100px;
      }
      

      Save the styles.css file and reload index.html in your browser to inspect the changes. The yellow box should have moved 100 pixels down and 100 pixels to the right to allow for the 100 pixels of margin space between its border and the edges of the viewport:

      Webpage with `<div>` with padding, border, margins specified

      Note: You may have noticed that the yellow box originally had a small margin of white space between its top and left side and the edges of the viewport. This margin is automatically created by some browsers to allow for space between the edges of the viewport and the website content. You can remove this margin by setting the top and left margin to zero.

      Like the padding and border, the sizes of specific sides of the margin can be set using margin-left, margin-right, margin-top, and margin-bottom.

      Before moving on, add another <div> container to the page to study how the margin affects the position of nearby content. Without erasing anything, add the additional CSS ruleset to your styles.css file:

      styles.css

      . . .
      .blue-div {
        height:100px;
        width:100px;
        background-color: blue;
      }
      

      Save the file and return to your index.html file. Without erasing anything, add the following <div> element to your file and assign it the blue-div class:

      index.html

      …
      <div class="blue-div"></div>
      

      Save your index.html file and load it in the browser. You should receive something like this:

      Two `<divs>` containers with margin space between them

      The browser should now display a blue box that is 100 pixels wide and 1000 pixels high. This blue box should be 100 pixels below the yellow box on account of the yellow box’s margin. In general, surrounding elements will by default be pushed away from an element on account of its margin. Be aware, however, that the margins of adjacent elements will often overlap due to margin collapse. The size of the overlapping margin is determined by the size of the largest margin between the two elements.

      Conclusion

      In this tutorial you learned about the CSS box model and how to adjust the size of each of its content, padding, border, and margin properties. Understanding the behavior of these properties and how to set values for them is useful when organizing and styling content on a webpage. This knowledge will be useful when building the demonstration website in the remaining tutorials. In the next tutorial, you will set up an index.html file to serve as the website’s homepage.



      Source link