Define the policy in the Rails initializer, turn on report-only mode first, collect violation reports, fix the legitimate ones, then enforce it once reports go quiet. Rails supports this with content_security_policy_report_only, so a policy can be tested on real traffic without breaking pages.
What a CSP is
A Content Security Policy is a response header that tells the browser which sources of scripts, styles, images and other content are allowed on a page. The Rails security guide recommends defining one to help protect against XSS and injection attacks, and provides a DSL for it in config/initializers/content_security_policy.rb.
It limits the damage if an injection bug exists. It does not replace output escaping.
A starting policy
The guide's example sets the default source to self and https, allows fonts and images from self, https and data, sets object sources to none, and limits scripts and styles to self and https. It also sets a report_uri for violations.
Rails.application.config.content_security_policy do |policy|
policy.default_src :self, :https
policy.object_src :none
policy.script_src :self, :https
policy.style_src :self, :https
policy.report_uri "/csp-violation-report-endpoint"
end
The rollout
| Phase | Setting | Goal |
|---|---|---|
| 1. Observe | content_security_policy_report_only = true | See what would break |
| 2. Fix | Remove inline scripts, add needed sources | Reduce violations |
| 3. Enforce | Remove report-only | Block violations |
| 4. Maintain | Keep reporting on | Catch regressions |
The guide describes report-only mode for exactly this case, migrating legacy content. In that mode the browser sends the Content-Security-Policy-Report-Only header and reports violations without blocking.
Rails.application.config.content_security_policy_report_only = true
You can also set report-only on a single controller, which is handy when you want to tighten one area at a time.
Steps
- Inventory where scripts and styles come from: your assets, a CDN, analytics, payment widgets.
- Write the first policy and turn on report-only.
- Build an endpoint to receive reports, or use a reporting service, and review them for a week or two.
- Move inline scripts into files. Where you cannot, use nonces.
- Add only the third party sources you actually need.
- Enforce, first on a low traffic path.
- Keep the report endpoint live afterward.
Nonces and overrides
If you are tempted to allow unsafe-inline, the guide says to consider nonces, which give a per-request token to scripts you trust. Rails also lets you override the global policy per controller, disable it for one action with content_security_policy false, only: :index, and use lambdas for per request values in multi tenant apps. Use the disable switch sparingly, and note why.
Common pitfalls
- Browser extensions generate noise in reports. Filter, do not panic.
- Admin and legacy pages are often the last to be fixed. Give them a dated plan.
- A wide
https:source permits any HTTPS host, which is weaker than listing hosts. Tighten over time.
Who owns it
A policy decays when nobody owns it. Name an owner, put the policy file in code review, and require a note when someone adds a new source. Review the report endpoint once a month, because new third party scripts arrive quietly through tag managers and marketing requests.
Quick check
Will report-only block anything?
No. It sends the report-only header, so the browser reports violations but allows the content.
Does a CSP replace escaping?
No. It is a second layer for when escaping fails.
Safeguard's policy gates can help enforce release checks for security headers in your pipeline. A free scan is at gold.safeguard.sh/scan.
Sources
Last reviewed: 19 October 2026.