Server headers
Configure the server headers to refine the content security policy.
Hinode uses strict security policies to ensure the site is secure by default . 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.
Tip
The server header configuration used by Hugo 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 v0.27.0 , Hinode includes a module to generate the server headers automatically . It uses a feature from Hugo that enables the definition of a custom output format . The main Hinode repository defines two custom output formats. The first one generates the server headers for a local web server provided by Hugo. The second output format defines a similar configuration for the 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.
[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 for more information.
[outputs]
home = ["HTML", "RSS", "REDIR", "netlify", "server"]
By default, Hinode includes
a module to generate the server headers automatically
. The module includes two files in the layouts
folder that hook the two custom outfut 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
. 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.
{{ partial "assets/server-headers.html" (dict "header" (path.BaseName .RelPermalink)) }}
By default, Hinode generates the server headers using a default policy. Click on the tab below to review the default settings.
# 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"
X-Frame-Options = "SAMEORIGIN"
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
).
[params.headers]
[params.headers.netlify]
source = "netlify.toml"
When your site uses multiple languages, you may
run into an issue with your custom 404
page
. 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
, which is available in three languages:
[[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
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 provides an overview of available directives and settings. Hinode includes the following policies by default.
[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:
[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"
]
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
. This reduces the build time. Add the following configuration to your site configuration:
[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.
"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.
npm run build:headers
Start building sites ...
hugo v0.134.3
WARN Generating server headers: /netlify.toml
WARN Generating server headers: /server.toml
| EN
-------------------+------
Pages | 2
Paginator pages | 0
Non-page files | 0
Static files | 114
Processed images | 0
Aliases | 0
Cleaned | 0
Total in 281 ms
Add the /prebuild
folder to your .gitignore
file to prevent polluting your source code repository.