Defaults.Exposed › Fixes › Content-Security-Policy (CSP)
How to fix Content-Security-Policy (CSP)
A Content Security Policy is a safety rule your website hands to every visitor's browser, telling it exactly which code is allowed to run. Without one, if anything malicious ever lands on a page — through a comment box, a hacked plugin, or a third-party script — the browser will run it freely, including code that quietly skims your customers' card numbers and passwords as they type, with the padlock still showing.
Bottom line for your business: If your site is ever tampered with, malicious code can read your customers' payment-card and login details straight off your own checkout while everything looks completely normal — leaving you with chargebacks, fraud claims, a reportable data breach, and a check failure that larger clients' security teams use to stall or kill a deal.
What this can cost you
- Hidden code slips into one of your pages and silently copies every card number and password your customers enter at checkout, sending it to an attacker while your site looks completely normal — you only find out when the fraud complaints arrive.
- A scammer plants a fake 'pay here' form on your real website that captures payments into their own account; customers think they paid you, blame you when the goods never come, and demand their money back.
- A larger client's security team scans your site, sees this basic protection is switched off, and flags it — stalling or losing you the contract until you can prove it's fixed.
- A breach traced to a missing standard safeguard becomes a reportable incident: regulator questions, customer notifications, and a reputation hit that costs far more than the free fix.
Why it matters. The padlock proves the connection to your site is private, but it does nothing to control what code runs once a visitor is on the page. A Content Security Policy is the safeguard that does — it tells browsers to ignore any script that didn't come from a source you trust, so a single tampered field, ad, or plugin can't be turned into a tool for stealing your customers' money and data. It is a graded check on your scorecard, worth real points, and one of the first things a professional security review looks for.
What this is, in plain words
When someone visits your website, their browser downloads your page and runs whatever code is on it — the scripts that make menus drop down, buttons work, payment forms submit, and so on. By default, the browser trusts all of it. It has no way of knowing which code is genuinely yours and which was sneaked in by someone else.
A Content Security Policy (often shortened to CSP) is a short list of rules your website attaches to every page, telling the browser: “only run code from these sources I’ve approved, and refuse everything else.” It’s the difference between a nightclub that lets anyone in and one with a guest list on the door.
The reason this matters so much is that websites get tampered with constantly — not always by hacking your server, but through the back doors most sites leave open: a comment field, a search box, an outdated plugin, a third-party script for ads or analytics, or a chat widget. If an attacker gets even one line of their own code onto one of your pages, the browser runs it as if it were yours. From there it can read everything your customers type — card numbers, passwords, addresses — and quietly send it elsewhere. A Content Security Policy shuts that door by refusing to run anything from a source you didn’t approve.
What this can cost you
This is not abstract. The attack a Content Security Policy prevents — code injected into a page that steals data from your own customers — is behind some of the largest card-skimming breaches on record. Here is how it tends to play out for a normal business:
-
The invisible checkout skimmer. An attacker slips a few lines of code onto your checkout page through a vulnerable plugin or a compromised third-party script. Every card number, name, and CVV your customers type is copied and sent to the attacker in real time. Your site looks and works perfectly; the padlock is there. You discover it weeks later when your payment provider calls about a cluster of fraud reports all tracing back to your store. Now you’re facing chargebacks, a forced security audit, possible loss of card-processing rights, and a breach you may be legally required to report.
-
The fake payment form. A scammer injects a convincing “pay here” box onto your real website. Customers enter their details trusting your brand; the money and data go to the attacker. The customers blame you — and they’re not wrong to, because it happened on your site.
-
The lost contract. A bigger prospect’s security team runs an automated scan of your site as part of vendor due diligence. A missing Content Security Policy shows up immediately as a high-severity gap. To a procurement or security reviewer, that single missing safeguard reads as “this supplier doesn’t do the basics” — and the deal stalls while they ask for remediation, or quietly goes to a competitor who passed.
-
The reportable breach. When a data breach is traced back to a missing, standard, free safeguard, it stops being bad luck and starts looking like negligence. That changes the regulator’s questions, the customer-notification obligation, and the cost — both the fine and the reputational damage, which lingers long after the incident is closed.
-
The compromised ad or widget. Many sites load code from outside parties — ad networks, fonts, support chat, analytics. If any one of those is breached, the malicious code rides straight into your pages. A Content Security Policy limits the blast radius by only allowing the specific outside sources you trust, so one supplier’s breach doesn’t automatically become yours.
What it actually is (the detail)
A Content Security Policy is delivered as a single HTTP response header — a line your web server sends with every page. Its value is a set of directives, each naming a type of content and the sources allowed for it. For example:
Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; frame-ancestors 'self'
In plain terms, that says: by default only load things from my own site; only run scripts from my own site; allow no old-style plugins; and don’t let other sites embed mine in a frame.
What “good” looks like. Our check does not just look for the header’s presence — it reads the policy directive by directive and scores how strong it actually is, the way a security reviewer would. A strong policy:
- Sets a restrictive baseline (
default-src 'self') and only widens it for the specific trusted third parties you genuinely use. - Avoids the loopholes. It does not use
'unsafe-inline'or'unsafe-eval'for scripts, and does not use wildcard (*) or bare scheme sources (likehttps:) for scripts — these effectively re-open the door the policy is meant to close. Where inline scripts are genuinely needed, it uses a nonce or hash so only your specific approved code runs. - Locks down framing with
frame-ancestors 'self'(this also blocks the “embed your site to trick your customers” attack) and disables legacy plugins withobject-src 'none'. - Is enforcing, not report-only. A
Content-Security-Policy-Report-Onlyheader only watches; it provides zero runtime protection. Our check gives it a small fraction of the credit and never records it as a pass. You’re only protected once the policy is enforcing.
A policy that exists but leans on 'unsafe-inline', 'unsafe-eval', or wildcards will still score poorly — because in practice it provides little real protection. The goal is a tight policy, not just any policy.
How to fix it (free, ~1–2 hours)
Hand this to your IT person or whoever runs your website — the fix itself is completely free. We only ever charge to monitor that it stays in place and stays correct over time; switching it on costs nothing. The reason this takes an hour or two rather than minutes is the careful trial step that stops it accidentally blocking parts of your own site.
-
Start in report-only mode — do not enforce yet. Add a
Content-Security-Policy-Report-Onlyresponse header. This watches and logs what would be blocked without actually blocking anything, so the live site keeps working while you learn what every page genuinely depends on. (Important: report-only on its own gives visitors no protection — it is only the safe first step.) -
Build the policy from what your site actually uses. Review the reports to find every legitimate source of scripts, styles, fonts, and images — your own domain, your analytics, your payment provider, your fonts host, your chat widget — and list them as allowed sources. A solid starting point is
default-src 'self'plus explicit entries for the trusted third parties you really use. -
Avoid the loopholes that defeat the whole point. Steer clear of
'unsafe-inline'and'unsafe-eval'for scripts, and avoid wildcard sources like*and bare schemes likehttps:for scripts — these re-open the exact gap the policy is meant to close. Where inline scripts are unavoidable, use a nonce or hash so only your specific approved code runs. -
Lock down framing and plugins. Add
frame-ancestors 'self'(this also stops other sites embedding yours to trick your customers, and it satisfies the related clickjacking check) andobject-src 'none'to block legacy plugin-based attacks. -
Switch from report-only to enforcing. Once the reports are clean and the site works, change the header name from
Content-Security-Policy-Report-OnlytoContent-Security-Policy. This is the step that actually delivers protection — a report-only policy alone does not, and will not pass the check.Where you set the header depends on your platform:
- Cloudflare: Rules → Transform Rules → Modify Response Header → set
Content-Security-Policy. (You can use Cloudflare for the report-only trial too.) - Nginx:
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none'; frame-ancestors 'self';" always; - Apache:
Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none'; frame-ancestors 'self';" - IIS (web.config): add a custom HTTP response header named
Content-Security-Policywith the policy as its value. - Google Workspace / Microsoft 365: these run your email, not your public website, so the policy is set wherever your website is actually hosted (Cloudflare or your web host above), not in your email admin console.
- Cloudflare: Rules → Transform Rules → Modify Response Header → set
-
Re-check your domain to confirm the policy now shows as switched on and enforcing, with no weakening loopholes.
Common mistakes
- Stopping at report-only. The single most common error: a policy is added in report-only mode, everyone moves on, and the site is never actually protected. Report-only blocks nothing. You must switch to enforcing.
- Reaching for
'unsafe-inline'to make it “just work.” When the policy blocks a legitimate inline script, the quick fix is to allow all inline scripts — but that re-opens the exact hole the policy was meant to close. Use a nonce or hash instead. - Using a wildcard. A bare
*(orhttps:) inscript-srcallows scripts from anywhere, which means the policy gives almost no real protection and will still score low. - Forgetting third-party sources. Enforcing a strict policy without first listing the legitimate outside services you use (analytics, fonts, payment widgets) can break parts of your own site — which is exactly why the report-only trial step exists.
- Setting it only on the homepage. The policy needs to cover every page, especially checkout, login, and account pages — those are the ones worth attacking.
- Treating it as a replacement for the padlock. A Content Security Policy and HTTPS/HSTS protect different things. You want all of them; one does not cover for another.
FAQ
I'm not technical — can I deal with this myself?
You don't need to understand the details. This is a setting added by whoever runs your website or hosting, and on services like Cloudflare it's largely guided. Hand them the 'How to fix it' section below. It's free; the one caution is that it should be rolled out carefully in a watch-only trial first so it doesn't accidentally block parts of your own site — which is exactly what the steps cover.
I already have the padlock and an SSL certificate — isn't my site secure?
The padlock secures the delivery of your page; it doesn't police what runs inside it. If malicious code ever ends up on a page — via a hacked plugin, a compromised ad, or an injected field — the padlock won't stop it from stealing data. A Content Security Policy is the layer that limits what's allowed to run in the first place. They protect different things, and you want both.
Could turning this on break my site?
It can if it's switched on aggressively all at once, because it may block legitimate scripts you actually use. That's why the standard approach is to start in a 'report-only' trial mode that watches without blocking, fix anything it flags, and only then enforce it. Done this way it's safe — and the trial step is built into the fix below.
We put it in 'report-only' mode already — are we covered?
No, and this is the most common false sense of security. Report-only mode watches and logs what would be blocked, but it blocks nothing — visitors get zero real protection. It's only the safe first step. Our check gives report-only a small fraction of the credit of a real policy and will not record it as a pass. You're only protected once you switch to enforcing mode.
Does this affect our score, or is it just advisory?
It affects your score. The Content Security Policy check is graded and worth up to 25 points in the Web Security category. A missing or weak policy is marked high severity and drags your grade down — and it's exactly the kind of gap a client's security questionnaire asks about.
Our developer added a policy but the score is still low — why?
A policy can exist and still be weak. The most common culprits are loopholes like 'unsafe-inline' and 'unsafe-eval' for scripts, or wildcard sources (a bare *), which re-open the exact gap the policy is meant to close. Our check reads the policy directive by directive and scores down those weaknesses — a policy that allows anything scores little better than having none. The fix is to tighten the script rules using nonces or hashes instead of those loopholes.