Most Privacy Companies Ship Security Headers That Look Complete — and Aren’t

Published September 12, 2026 | 9 min read

Key Findings

Why We Re-Ran This

The obvious version of this article is a scoreboard: scan a list of competitors, count how many security headers each one sends, publish the table. We built that table first. Then we checked it against the live responses and found it was wrong — badly enough, and consistently enough in our own favour, that we threw it out.

The problem was not arithmetic. It was the question. Does this company send a Content-Security-Policy? is trivially checkable and tells you almost nothing. A CSP can be one directive long, block nothing a real attacker would attempt, and still register as present on every automated scanner in existence.

So we asked a harder question instead: not is the header there, but does it do anything. That question is less flattering to us, and considerably more useful to you.

The frame-ancestors Trick

Content-Security-Policy is the most powerful security header a site can send. It is also the only one that is genuinely difficult to deploy, because a strict policy breaks inline scripts — and most marketing sites are held together with inline scripts.

There is a shortcut. You can send a CSP that contains a single directive:

Content-Security-Policy: frame-ancestors 'self'

This is a real CSP header. It will show as present in any audit. What it does is prevent other sites from embedding yours in an iframe — clickjacking protection, essentially a modern replacement for X-Frame-Options.

What it does not do is govern scripts. There is no script-src. There is no default-src. Any script from any origin, any inline block, any eval() — all permitted. If an attacker finds a cross-site scripting hole in that site, the CSP contributes nothing to stopping it.

Four of the twenty-two companies we scanned ship exactly this:

That is the entire policy in each case. On a checkbox audit, all four earn a tick in the CSP column and appear to be doing the hardest thing on the list. In practice they have implemented the easiest part of it.

We want to be careful here: these are not fake headers, and sending them is better than sending nothing. Clickjacking is a real attack and these policies prevent it. The criticism is narrower — a CSP is widely treated as shorthand for "this site defends against XSS," and in these four cases it does not.

What We Actually Found

Grouping all 22 by what their CSP does, rather than whether it exists:

Restricts scripts, no unsafe-* keywords — 2 sites

EasyOptOuts and Vigilant Privacy. When this article first ran, the answer was nobody — ourselves included. We have since fixed ours, and the section below says exactly what that took. EasyOptOuts was already there, and their policy is the better of the two.

Restricts scripts but permits unsafe-inline or unsafe-eval — 8 sites

OneRep, DeleteMe, ReputationDefender, Privacy Bee, PrivacyDuck, BrandYourself, VanishID, Bitdefender. These policies have a real script-src and do restrict which origins may serve scripts — genuinely useful against script injection from a third-party domain. But 'unsafe-inline' permits any inline script the attacker can inject into the page, which is the most common form XSS takes.

Framing only — 4 sites

Optery, Aura, Cloaked, Mine. Described above.

No CSP at all — 9 sites

Kanary, Norton/LifeLock, Surfshark, Incogni, IDX Privacy, HelloPrivacy, Ironwall360, McAfee, Mozilla Monitor. Two of these sell security software.

Where We Land, Specifically

Our own policy is this:

default-src 'self'; script-src 'self' https://js.stripe.com https://checkout.stripe.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' https://api.stripe.com; frame-src https://js.stripe.com; font-src 'self' data:;

'unsafe-inline' is gone from script-src as of 13 September 2026. It remains on style-src, highlighted above, which is a separate and larger job we have not done. When this article first published, both were present and we said so.

The blocker was never React or Stripe — our React bundle contains zero inline scripts, and Stripe runs fine under a strict policy. It was 42 inline event handlers (onclick="...") across nine pages. Neither a nonce nor a hash can authorise those: CSP attaches them to a <script> element, and an attribute handler has no element to attach one to. Fixing it meant rewriting all 42 as delegated event listeners and moving 158 inline script blocks into external files.

That work has shipped. We wrote the original sentence — that our CSP was weaker than it looked — before we had fixed it, because we would rather say it ourselves than have a competitor say it for us. The same standard applies to what follows.

The most complete policy in this set is still not ours. EasyOptOuts combines 'strict-dynamic' with fifteen script hashes — a harder policy to build than ours, which simply has no inline script left to authorise. Reading their header prompted us to add object-src 'none', base-uri 'self' and form-action 'self', which we had been missing. We still do not set frame-ancestors, because X-Frame-Options: DENY already covers it. Bitdefender also runs nonces plus 'strict-dynamic', held back only by 'unsafe-eval'. Both are ahead of us on parts of this, and pretending otherwise would make the rest of this article worthless.

Other Things Worth Noting

OneRep sends Strict-Transport-Security: max-age=0. A max-age of zero instructs the browser to forget the HTTPS-only rule rather than remember it — functionally the opposite of what HSTS is for, despite the header being present and carrying a preload directive. This almost certainly is not deliberate.

HSTS lifetimes vary more than they should. Surfshark sends 60 days and Privacy Bee 180; one year is the usual floor and the minimum for preload eligibility. Optery sends two years, the longest in the set.

Referrer-Policy is missing more often than any other header. Without it, browsers fall back to a default that can leak the full URL of the page a user came from — which, on a data-broker removal service, may itself disclose something about that user.

How To Check For Yourself

Every claim here is reproducible in one command. No account, no scanner, no waiting:

curl -sI https://example.com | grep -i "content-security-policy"

Then read the value. If it contains no script-src and no default-src, it is not protecting scripts regardless of what any grading site reports. If it contains 'unsafe-inline', it permits injected inline script — ours included.

Methodology. Scanned 12 September 2026 from a single US host, requesting each company's primary marketing domain over HTTPS with a standard desktop browser user-agent, following redirects. We recorded response header values rather than a third-party grade.

Limitations, stated plainly. Five domains — OneRep, DeleteMe, VanishID, McAfee and Mozilla Monitor — returned bot-mitigation responses (403 or 406) rather than their real homepage, so their headers may differ for an ordinary visitor; treat those five as provisional. Headers can also vary by path, by CDN edge, by region, and can change at any time. This is one measurement from one place at one moment, not a verdict on any company's overall security. A site can send perfect headers and be insecure, or send none and be well defended.

We Delete Your Data from 1,493 Data Brokers

Security headers are one signal among many, and we have just published our own shortcomings in them. What we do claim is this: we send legal CCPA deletion demands to 1,493 data brokers, resubmit every 45 days, and publish the numbers we can prove. $9.95/month.

Start Your Free 7-Day Trial

No credit card required. Cancel anytime. We never sell your data.