How to Configure CSP Headers for Plausible Analytics in Hugo
To configure CSP headers for Plausible-Hugo, include the plausible_csp.html partial in your script-src and connect-src directives, or add plausible_head.html to your templates to display the required CSP snippet in HTML comments when proxy_netlify is disabled.
The plausible-hugo theme provides automated helpers for Content Security Policy (CSP) headers when integrating Plausible Analytics into Hugo sites. When you configure CSP headers properly, you allow the Plausible tracking script to execute while maintaining strict security policies against cross-site scripting vulnerabilities.
Understanding CSP Requirements for Plausible-Hugo
Content Security Policy headers restrict which external resources a browser can load. Since Plausible Analytics loads a JavaScript file from an external domain, you must explicitly permit this source in your CSP configuration. The plausible-hugo repository handles this through specialized partials located in layouts/partials/.
When proxy_netlify is set to true, the Plausible script is served from your own domain via Netlify's proxy, eliminating the need for additional CSP entries. However, when using the standard Plausible CDN or custom domains without proxying, you must declare the domain in your policy.
Key Configuration Parameters
In your config.toml under [params.plausible], these parameters control CSP generation:
proxy_netlify: Boolean that suppresses CSP warnings whentrue(script served from same origin)custom_js_domain: Custom domain hosting the Plausible JS file (e.g.,stats.example.com)selfhosted_domain: Domain of self-hosted Plausible instance (defaults toplausible.io)
Generating the CSP Source String
The plausible_csp.html partial generates the exact string required for your CSP directives. Located at layouts/partials/plausible_csp.html, this template constructs the value 'unsafe-inline' https://<domain> where <domain> is resolved from custom_js_domain or selfhosted_domain parameters.
For a standard Plausible.io setup, this outputs:
'unsafe-inline' https://plausible.io
For custom domains, it adapts accordingly:
'unsafe-inline' https://stats.example.com
You must add this generated string to both your script-src and connect-src directives.
Implementation Methods
Method 1: Manual CSP Integration with HTML Comments
Add the plausible_head.html partial to your site's <head> section. This automatically includes plausible_head_csp.html, which prints an HTML comment reminding you of the exact CSP values needed:
<head>
{{ partial "plausible_head.html" . }}
</head>
When rendered, this produces a comment in your HTML source:
<!-- If you are using Content-Security-Policy, do not forget to add this code to your CSP :
script-src 'unsafe-inline' https://plausible.io
connect-src 'unsafe-inline' https://plausible.io
or just add the partial 'plausible_csp.html' to those 2 csp directives in your 'index.headers' file -->
Method 2: Automated Netlify Headers
For Netlify deployments, create or edit layouts/index.headers and embed the CSP partial directly:
Content-Security-Policy: default-src 'self'; {{ partial "plausible_csp.html" . }}
During the Hugo build process, apply the partial to both directives to generate the complete policy:
Content-Security-Policy: default-src 'self'; script-src 'unsafe-inline' https://plausible.io; connect-src 'unsafe-inline' https://plausible.io
Complete Configuration Example
First, configure your config.toml:
[params.plausible]
enable = true
domain = "example.com"
proxy_netlify = false
custom_js_domain = "stats.example.com" # Optional
Then implement your headers file for Netlify:
# layouts/index.headers
Content-Type: text/html; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Security-Policy: default-src 'self'; script-src {{ partial "plausible_csp.html" . }}; connect-src {{ partial "plausible_csp.html" . }}
Or manually add the output to your web server configuration:
Header set Content-Security-Policy "script-src 'unsafe-inline' https://stats.example.com; connect-src 'unsafe-inline' https://stats.example.com;"
Summary
- The
plausible_csp.htmlpartial atlayouts/partials/plausible_csp.htmlgenerates the required CSP source string'unsafe-inline' https://<domain> - You must add this string to both
script-srcandconnect-srcdirectives in your CSP - When
proxy_netlifyistrue, no CSP configuration is required due to same-origin serving - For manual setups,
plausible_head_csp.htmlprovides HTML comments with the exact values needed - Netlify users can automate CSP injection using the
layouts/index.headersfile with Hugo partials
Frequently Asked Questions
Do I need to configure CSP headers when using Netlify proxy?
No. When you set proxy_netlify = true in your configuration, the Plausible script is served from your own domain via Netlify's rewrite proxy, making it same-origin. This eliminates the need for additional CSP entries since the script no longer loads from an external domain, and the warning partial is automatically suppressed.
Why does the CSP require 'unsafe-inline' for Plausible?
The 'unsafe-inline' value is required because the Plausible tracking script uses inline JavaScript execution patterns for event collection and tracking. The plausible_csp.html partial includes this value automatically alongside the domain specification to ensure the analytics functionality works correctly under strict CSP policies that would otherwise block inline script execution.
Can I use this with a self-hosted Plausible instance?
Yes. Set the selfhosted_domain parameter in your config.toml to your instance domain (e.g., analytics.myselfhosted.com). The plausible_csp.html partial will automatically generate the CSP string using your custom domain instead of the default plausible.io, allowing you to configure CSP headers for self-hosted deployments.
How do I configure CSP headers on non-Netlify hosts?
For Apache or Nginx servers, include the plausible_head.html partial in your templates to reveal the exact CSP values in HTML comments, then copy the generated 'unsafe-inline' https://<domain> string into your server's CSP configuration. Apply this string to both the script-src and connect-src directives in your Content-Security-Policy header.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →