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 when true (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 to plausible.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.html partial at layouts/partials/plausible_csp.html generates the required CSP source string 'unsafe-inline' https://<domain>
  • You must add this string to both script-src and connect-src directives in your CSP
  • When proxy_netlify is true, no CSP configuration is required due to same-origin serving
  • For manual setups, plausible_head_csp.html provides HTML comments with the exact values needed
  • Netlify users can automate CSP injection using the layouts/index.headers file 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →