# Server headers

Configure the server headers to refine the content security policy.

Added in v0.27.0

Hinode uses strict security policies to ensure the site is [secure by default](https://observatory.mozilla.org/analyze/demo.gethinode.com). If you want to include external resources, such as images and videos, you will need to explicitly add these sources to the server headers. If omitted, the browser will refuse to load these resources, resulting in broken links or missing elements. Review the next sections to learn how to configure your policies correctly.

## Defining a custom output format

> [!TIP]
> The [server header configuration used by Hugo](https://gohugo.io/getting-started/configuration/#configure-server) is similar to the configuration supported by Netlify. We can therefore reuse the same template code to configure both environments. This simplifies development and testing, and ensures the local development environment and production environment are comparable.

Since Added in v0.27.0
, Hinode includes [a module to generate the server headers automatically](https://github.com/gethinode/mod-csp). It uses a feature from Hugo that enables the definition of a [custom output format](https://gohugo.io/templates/output-formats). The main Hinode repository defines two custom output formats. The first generates the server headers for a local web server provided by Hugo. The second defines a similar configuration for deployment to Netlify.

Add the following configuration to your main configuration (usually `hugo.toml`). The setting `root = true` ensures the output files are placed in the root of the build folder. Otherwise, Hugo would create a separate output for each one of the defined site languages.

```toml
[outputFormats]
  [outputFormats.server]
    mediaType = "application/toml"
    baseName = "server"
    isPlainText = true
    notAlternative = true
    permalinkable = true
    root = true
  [outputFormats.netlify]
    mediaType = "application/toml"
    baseName = "netlify"
    isPlainText = true
    notAlternative = true
    permalinkable = true
    root = true
```

Next we will link the two output formats `server` and `netlify` to the home page. By default, Hinode also generates an `RSS` and `REDIR` file for the site. See the the chapter about [server-side redirection](server-side-redirection)
 for more information.

```toml
[outputs]
home = ["HTML", "RSS", "REDIR", "netlify", "server"]
```

## Generating the server headers

By default, Hinode includes [a module to generate the server headers automatically](https://github.com/gethinode/mod-csp). The module includes two files in the `layouts` folder that hook the two custom output formats to the main index page (which is the site's homepage). The two files are named `layouts/index.netlify.toml` and `layouts/index.server.toml`.

Each file calls a custom partial called `assets/server-headers.html`, which is defined in the [CSP module](https://github.com/gethinode/mod-csp). Hinode mounts the required files automatically. The partial requires one parameter called `header`, which links the partial to a configuration that we will define in the next paragraph.

```go-template
{{ partial "assets/server-headers.html" (dict "header" (path.BaseName .RelPermalink)) }}
```

## Configuring the default server headers

By default, Hinode generates the server headers using a default policy. Click on the tab below to review the default settings.

```toml
# toml-docs-start server-config
[[headers]]
    for = "/**"
    [headers.values]
        Strict-Transport-Security = "max-age=31536000; includeSubDomains; preload"
        X-Content-Type-Options = "nosniff"
        X-XSS-Protection = "1; mode=block"
        Referrer-Policy = "strict-origin"
        Permissions-Policy = """\
            geolocation=(), \
            midi=(), \
            sync-xhr=(), \
            microphone=(), \
            camera=(), \
            magnetometer=(), \
            gyroscope=(), \
            fullscreen=(), \
            payment=() \
            """
        cache-control = """\
            max-age=0, \
            no-cache, \
            no-store, \
            must-revalidate \
            """
        Access-Control-Allow-Origin = "*"
        Content-Security-Policy = """\
            default-src 'none'; \
            script-src 'self'; \
            font-src 'self'; \
            connect-src 'self'; \
            img-src 'self'; \
            style-src 'self'; \
            base-uri 'self'; \
            object-src 'none'; \
            form-action 'self'; \
            manifest-src 'self'; \
            media-src 'self' \
            """
# toml-docs-end server-config
```

You can merge the generated file with another input file. The supported formats are `JSON`, `TOML`, `YAML`, and `XML`. For example, you could include additional settings for the deployment to Netlify. Include the settings in your `data` folder and add the relative path to the `source` parameter of your header configuration. For example, the following configuration merges the file `data/netlify.toml` with the Netlify server headers. Add the configuration to your site's parameters (e.g. `hugo.toml`).

```toml
[params.headers]
  [params.headers.netlify]
    source = "netlify.toml"
```

## Configuring custom 404 pages

When your site uses multiple languages, you may [run into an issue with your custom `404` page](https://github.com/gohugoio/hugo/issues/5161). Hugo does not render the `404` page correctly when `defaultContentLanguageInSubdir` is set to `true` in your site configuration (usually `hugo.toml`). You can include the necessary redirects in `data/server.toml`. This will fix the issue for the hugo server and Netlify server. The following example is used by the [Hinode demo site](https://demo.gethinode.com/), which is available in three languages:

```toml
[[redirects]]
from = '/nl/*'
to = '/nl/404.html'
status = 404

[[redirects]]
from = '/en/*'
to = '/en/404.html'
status = 404

[[redirects]]  # Default language should be last.
from = '/*'
to = '/en/404.html'
status = 404
```

## Merging module policies

Hinode merges the Content Security Policies of each configured module. You can define the policies as regular key-value pairs, using arrays for the directives and domains. The [Content Security Policy Quick Reference Guide](https://content-security-policy.com) provides an overview of available directives and settings. Hinode includes the following policies by default.

```toml
[params.modules.hinode.csp]
    style-src = ["www.youtube.com"]
    font-src = ["fonts.gstatic.com"]
    frame-src = [
        "player.cloudinary.com",
        "www.youtube-nocookie.com",
        "www.youtube.com"
    ]
    img-src = [
        "data:",
        "*.imgix.net",
        "*.imagekit.io",
        "*.cloudinary.com",
        "i.ytimg.com"
    ]
```

You can define additional policies for each included module to improve the maintainability of your site configuration.  For example, the module Google Analytics adds the following directives:

```toml
[params.modules.GoogleAnalytics.csp]
  script-src = [
      "*.google-analytics.com",
      "*.googletagmanager.com"
  ]
  connect-src = [
      "*.google-analytics.com",
      "*.analytics.google.com",
      "*.googletagmanager.com"
  ]
  img-src = [
      "*.google-analytics.com",
      "*.googletagmanager.com"
  ]
```

## Deploying the generated server headers

When building your Hinode site, Hugo will now automatically generate two additional output files that include the latest server headers. To test them locally, we need to ensure the Hugo servers picks up the right configuration. We will use an npm script to automate this task.

First, we will define a new hugo build command `build:headers` as npm script. It uses a new feature introduced in Hugo v0.124.0 to use [build segmentation](https://gohugo.io/getting-started/configuration/#configure-segments). This reduces the build time. Add the following configuration to your site configuration:

```toml
[segments]
  [segments.headers]
    [[segments.headers.includes]]
       kind = '{home}'
       output = '{netlify,server}'
```

As we cannot mount files into the `config` folder or base folder, we need to copy the generated files ourselves. The following script uses the package `cpy-cli` to copy the generated files cross platform.

```json
  "scripts": {
    "build:headers": "hugo --renderSegments headers -d prebuild && cpy prebuild/netlify.toml ./ --flat && cpy prebuild/server.toml config/_default/ --flat",
  },
  "devDependencies": {
    "cpy-cli": "^5.0.0",
  },
```

Test the npm script on your local machine to validate it is working correctly.

```bash
npm run build:headers
(out)Start building sites ...
(out)hugo v0.134.3
(out)
(out)WARN  Generating server headers: /netlify.toml
(out)WARN  Generating server headers: /server.toml
(out)
(out)                   | EN
(out)-------------------+------
(out)  Pages            |   2
(out)  Paginator pages  |   0  
(out)  Non-page files   |   0  
(out)  Static files     | 114  
(out)  Processed images |   0  
(out)  Aliases          |   0  
(out)  Cleaned          |   0  
(out)
(out)Total in 281 ms
```

Add the `/prebuild` folder to your `.gitignore` file to prevent polluting your source code repository.
