ruby 82 lines · 3 tabs

Content Security Policy (CSP) Starter

Shared by codesnips Jan 2026
3 tabs
Rails.application.config.content_security_policy do |policy|
  policy.default_src :self
  policy.font_src    :self, :https, :data
  policy.img_src     :self, :https, :data, "https://cdn.example.com"
  policy.object_src  :none
  policy.script_src  :self, :https
  policy.style_src   :self, :https
  policy.connect_src :self, "https://api.example.com"
  policy.frame_ancestors :none
  policy.base_uri    :self
  policy.form_action :self
  policy.report_uri  "/csp-reports"
end

Rails.application.config.content_security_policy_nonce_generator = lambda do |request|
  request.session.id.to_s.presence || SecureRandom.base64(16)
end

Rails.application.config.content_security_policy_nonce_directives = %w[script-src style-src]

# Deploy in observe-only mode until reports are clean, then flip the env var.
Rails.application.config.content_security_policy_report_only = ENV.fetch("CSP_REPORT_ONLY", "true") == "true"
3 files · ruby Explain with highlit

Content Security Policy is a browser-enforced allowlist that mitigates cross-site scripting by declaring which sources of scripts, styles, and other resources a page may load. This snippet shows a pragmatic starter that layers a nonce-based inline-script policy on top of Rails' built-in ActionDispatch::ContentSecurityPolicy DSL and wires up a violation-report endpoint.

The csp initializer configures the global policy via config.content_security_policy. Defaults are locked to :self, object-src is fully disabled, and frame-ancestors blocks framing to prevent clickjacking. The interesting part is config.content_security_policy_nonce_generator, which produces a fresh random nonce per request keyed off the session id, and content_security_policy_nonce_directives, which tells Rails to inject nonce-... into the script-src and style-src directives. This is what lets specific inline <script> tags run while everything else inline is blocked, avoiding the blunt and dangerous 'unsafe-inline'. Setting content_security_policy_report_only from an env var allows rolling the policy out in observe-only mode first, so violations are logged without breaking the site — the standard way to deploy CSP safely.

Because the nonce changes each request, any inline tag must reference content_security_policy_nonce. In ApplicationController a before_action calls permit_nonce_for_turbo, exposing the nonce as a helper so views and the layout can emit <%= javascript_tag nonce: true %> correctly, and csp_meta_tag keeps Turbo and importmap scripts trusted. The controller also flips to report-only mode for a marketing route where a looser policy is tolerable.

The CspReportsController receives the browser's application/csp-report POST bodies. create parses the nested csp-report object, extracts violated-directive and blocked-uri, and forwards a compact structured hash to logging and an async CspViolationJob for aggregation, then returns 204 No Content. Note skip_before_action :verify_authenticity_token and skip_before_action :require_login, since reports arrive unauthenticated and without a CSRF token. A subtle pitfall handled here is deduping noisy browser-extension violations by ignoring blocked-uri values that look like extension schemes. Together these files give a policy that is strict by default, observable through reports, and compatible with Rails' nonce machinery.


Related snips

Share this code

Here's the card — post it anywhere.

Content Security Policy (CSP) Starter — share card
Link copied