How to configure security headers
Piko ships defaults for CSRF, rate limiting, and content security policy (CSP). This guide covers how to pick the right policy for a project and how to harden the configuration for production. See the bootstrap options reference for the full option list.
Pick a CSP preset
CSP controls which resources a page can load. Piko offers four presets:
| Option | Use when |
|---|---|
piko.WithPikoDefaultCSP() | Default balance of safety and convenience. Recommended starting point. |
piko.WithStrictCSP() | Production sites with no inline scripts, no eval, and a locked-down allow-list. |
piko.WithRelaxedCSP() | Legacy content that cannot yet meet strict rules; use as a migration stepping stone. |
piko.WithAPICSP() | API-only deployments with no HTML output; minimal policy. |
ssr := piko.New(
piko.WithStrictCSP(),
)
Customise the CSP
For fine-grained control, use piko.WithCSP(configure) and build the policy programmatically:
ssr := piko.New(
piko.WithCSP(func(b *piko.CSPBuilder) {
b.ScriptSrc(piko.CSPSelf, piko.CSPHost("cdn.example.com"))
b.StyleSrc(piko.CSPSelf, piko.CSPUnsafeInline)
b.ImgSrc(piko.CSPSelf, piko.CSPData, piko.CSPHost("images.example.com"))
b.ConnectSrc(piko.CSPSelf, piko.CSPHost("api.example.com"))
b.FrameAncestors(piko.CSPNone)
}),
)
CSP source helpers cover the common keywords and host patterns: piko.CSPSelf, piko.CSPNone, piko.CSPUnsafeInline, piko.CSPUnsafeEval, piko.CSPData, piko.CSPBlob, piko.CSPHTTPS, plus piko.CSPHost("cdn.example.com") and piko.CSPScheme("wss:") for explicit hosts and schemes. Each source is a value of the named type Source. Passing raw strings does not compile.
Or pass a raw policy string with piko.WithCSPString(policy).
Report CSP violations
Route violation reports to an endpoint:
ssr := piko.New(
piko.WithStrictCSP(),
piko.WithReportingEndpoints(
piko.ReportingEndpoint{Name: "csp-endpoint", URL: "https://reports.example.com/csp"},
),
)
The browser sends report-to POSTs to the endpoint for every blocked resource. Log them, alert on new patterns, and tighten the policy as the noise drops.
Trusted proxies
If Piko sits behind a reverse proxy (Caddy, nginx, Cloudflare), tell it which proxies to trust so r.ClientIP() resolves correctly:
ssr := piko.New(
piko.WithTrustedProxies(
"10.0.0.0/8",
"172.16.0.0/12",
"192.168.0.0/16",
),
)
For Cloudflare specifically:
ssr := piko.New(
piko.WithCloudflareEnabled(true),
)
This enables trust of the CF-Connecting-IP header in addition to the trusted-proxy chain.
CSRF
Piko validates CSRF tokens on every action request. Configure the signing secret and token lifetime:
ssr := piko.New(
piko.WithCSRFSecret([]byte(os.Getenv("CSRF_SECRET"))),
piko.WithCSRFTokenMaxAge(24*time.Hour),
piko.WithCSRFSecFetchSiteEnforcement(true),
)
WithCSRFSecrettakes a random 32-byte key. Generate once and store in your secret manager.WithCSRFTokenMaxAgedefaults to a sensible production value; shorten for extra safety, lengthen for long-running SPA sessions.WithCSRFSecFetchSiteEnforcementaddsSec-Fetch-Siteheader checks on top of the token. Requires modern browsers; leave enabled unless you need to support legacy user agents.
Rate limiting
Enable the built-in limiter:
ssr := piko.New(
piko.WithRateLimitEnabled(true),
)
The limiter applies to the action dispatch path. Actions can declare their own limits by implementing piko.RateLimitable. See the server actions reference.
Cross-Origin Resource Policy
Set the Cross-Origin-Resource-Policy header for images, fonts, and other subresources:
ssr := piko.New(
piko.WithCrossOriginResourcePolicy("same-origin"),
)
Values: "same-site", "same-origin", "cross-origin". Most projects want "same-origin".
TLS and HSTS
For direct HTTPS termination:
ssr := piko.New(
piko.WithTLS(
piko.WithTLSCertFile("/etc/ssl/cert.pem"),
piko.WithTLSKeyFile("/etc/ssl/key.pem"),
piko.WithTLSMinVersion("1.3"),
piko.WithTLSHotReload(true),
piko.WithTLSRedirectHTTP(80),
),
)
For HSTS, add the header through your CSP builder or your reverse proxy. Most production Piko deployments terminate TLS at a reverse proxy (Caddy, nginx, a load balancer) instead of in-process. Choose based on operational preference.
Secrets
Never embed credentials in source code or in any committed file. Use Secret[T] with a resolver and load the placeholder reference from your environment or secret store. See the secrets how-to.
See also
- Bootstrap options reference: Security.
- Server actions reference for per-action rate limiting and captcha protection.
- How to secrets.
- Scenario 013: TLS and HTTPS for a runnable TLS setup.
- How to frontend analytics for GA4/GTM CSP recipes that extend the strict baseline.