Start With The Right Goal
A production CSP rollout is not about writing the strictest possible header on day one.
The goal is to reduce script and injection risk without breaking the public site, then keep watching for changes after the policy is live.
That distinction matters for agencies. A client site may have forms, checkout, analytics, ad pixels, chat widgets, page builders, embedded media, and old campaign scripts. If you enforce a tight policy before you understand those moving parts, the security project can become the incident.
A good rollout gives you evidence first, confidence second, and enforcement last.
Build A Real Site Inventory
- Homepage and high-traffic landing pages
- Lead forms, booking forms, quote forms, newsletter forms, and login pages
- Cart, checkout, donation, account, membership, or payment flows
- Tag managers, analytics, pixels, heatmaps, chat widgets, cookie banners, and review widgets
- Fonts, media embeds, maps, CDNs, API calls, browser beacon endpoints, and payment frames
- Mobile layouts and page-builder templates that may load different assets than desktop
- Admin-adjacent or authenticated public pages if they matter to customers or members
Use Report-Only First
Reporting-Endpoints: csp="https://example.com/csp-reports"
Content-Security-Policy-Report-Only:
default-src 'self';
script-src 'self' https: 'report-sample';
style-src 'self' 'unsafe-inline' https:;
img-src 'self' data: https:;
font-src 'self' data: https:;
connect-src 'self' https:;
frame-src 'self' https:;
object-src 'none';
base-uri 'self';
frame-ancestors 'self';
form-action 'self';
upgrade-insecure-requests;
report-uri https://example.com/csp-reports;
report-to csp;
Report-only mode lets you see what the browser would block without interrupting visitors. It is the best first move for existing production sites.
Do Not Approve Sources Too Quickly
The first week of reports will usually look messy.
Some violations will be legitimate site behavior. Some will be browser extensions. Some will be old tags nobody remembers. Some may be suspicious. The mistake is to turn the report list into a whitelist without asking what each source is doing.
I like sorting every repeated source into four buckets: required, questionable, suspicious, and cleanup. Required sources support a real site function. Questionable sources need an owner. Suspicious sources need investigation. Cleanup sources are leftovers that should be removed instead of allowed.
Tighten In The Right Order
- Set object-src to none unless the site has a real legacy embed requirement.
- Set base-uri to self or none so injected base tags cannot rewrite relative URLs.
- Set frame-ancestors to self or none to control who can embed the site.
- Lock form-action before enforcement so forms only submit to expected destinations.
- Tighten connect-src around APIs, analytics, payment providers, maps, and monitoring endpoints.
- Move script-src toward nonces or hashes instead of relying on unsafe-inline.
- Narrow broad https: allowances after you understand which third parties are actually required.
Handle Inline Scripts Honestly
Inline JavaScript is usually the hardest part of a real CSP rollout.
For new code, move behavior into external files or use framework patterns that do not need inline event handlers. For server-rendered code, a nonce per response is often the practical path. For stable static snippets, hashes can work, but they break when the inline code changes, even for small edits.
Sometimes you need a temporary compromise while old templates are cleaned up. That is fine if it is named, documented, and monitored. What I try to avoid is leaving unsafe-inline in script-src forever and pretending the policy is finished.
A More Mature Enforcement Policy
Reporting-Endpoints: csp="https://example.com/csp-reports"
Content-Security-Policy:
default-src 'self';
script-src 'nonce-{RANDOM_PER_RESPONSE}' 'strict-dynamic';
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
img-src 'self' data: https://cdn.example.com;
font-src 'self' https://fonts.gstatic.com;
connect-src 'self' https://api.example.com https://analytics.example.com;
frame-src 'self' https://payments.example.com;
object-src 'none';
base-uri 'none';
frame-ancestors 'self';
form-action 'self' https://payments.example.com;
upgrade-insecure-requests;
report-uri https://example.com/csp-reports;
report-to csp;
The final policy depends on the site, but a mature policy usually moves from broad discovery toward named sources and nonce or hash-based script execution.
Test Like A Visitor
- Load the homepage, landing pages, and key public pages on desktop and mobile.
- Submit lead forms and confirm the expected thank-you state, email, CRM entry, or stored submission.
- Walk through checkout or payment in a safe test mode when the site sells anything.
- Accept and reject cookie banners because they often change which scripts load.
- Use search, menus, filters, account pages, embedded maps, video, chat, and reviews.
- Check logged-out and logged-in states when both matter.
- Verify analytics, conversion tracking, and browser monitoring still receive expected events.
Launch With A Recovery Plan
Before switching from report-only to enforcement, know exactly how to turn enforcement off or roll back the header.
That does not mean you expect the rollout to fail. It means you respect production. If a client's checkout or lead form breaks, speed matters more than pride.
I also like having a short observation window after launch. Watch violations, browser errors, form delivery, checkout signals, and support messages. If the policy is doing its job, enforcement should make the site safer without becoming the thing users notice.
What Good Looks Like
- CSP is active in enforcement mode on important pages.
- The policy blocks unexpected scripts and limits form, frame, object, base, and connection behavior.
- Known third-party tools are documented by purpose, not just allowed by habit.
- Violations are monitored and grouped so noise does not bury real site problems.
- Critical paths are tested after header, plugin, theme, tag manager, CDN, or deployment changes.
- The agency can explain what changed, what was blocked, and whether the client site is currently affected.