A Basic Content Security Policy for WordPress and Any Website

A basic Content Security Policy for WordPress and any website, why waiting is risky while sites are under attack, and how to send CSP reports to MetricPoints.

Michael
November 12, 2026

A Normal Page Can Still Be Running Someone Else's Script

The homepage loads. The contact form looks fine. Checkout returns a normal page. And the response still does not tell the browser which scripts, frames, and form targets are supposed to be there.

That gap is where a lot of current website attacks live. An injected script, a compromised plugin, a poisoned tag-manager change, or a checkout skimmer does its work in the visitor's browser. The server can keep returning 200 the whole time. The agency hears about it when a client forwards a scary email, a payment provider asks about unexpected script activity, or orders quietly thin out.

A Content Security Policy is the browser rule for what that page may load, run, frame, and submit. A basic CSP will not replace updates, access control, or a careful review of who can publish tags. It does give the browser a limit, and it gives you a report when something outside that limit shows up.

Waiting for a perfect policy is how the gap stays open. A report-only policy this week is enough to start.

What The Basic Policy Is For

CSP is a response header. The browser reads it and applies it to that page. In enforcing mode, a disallowed script does not run, a disallowed frame does not load, and a form cannot post somewhere you left off the list. In report-only mode, the browser still allows the page to work, and it sends a violation report instead.

The reports are the part people skip, and they are the part that makes a basic policy useful. You learn which fonts, analytics calls, payment frames, and plugin scripts the site actually depends on. You also get a dated record when a new script appears on a page that used to be quiet.

Payment pages deserve extra attention. Merchants are expected to know which scripts run there and to notice when that set changes. A reporting CSP will not finish that work by itself. It will show you when the browser sees a script you did not list.

The longer explanation of directives, nonces, and rollout order is in Content Security Policy Basics. This post is the header you can put on a site now.

Start In Report-Only Mode

Publish the first policy as Content-Security-Policy-Report-Only. Visitors keep using the site. Browsers that support CSP reporting send a report each time the page would have violated the policy.

Move the same policy to the Content-Security-Policy header only after you have read those reports and clicked through the paths that make the site money: the lead form, login, booking widget, cart, and checkout, on desktop and on a phone.

If a host, CDN, or security plugin already sends an enforcing Content-Security-Policy, leave that header in place and add reporting to it, or send this starter as a report-only header beside it. Two enforcing policies both apply. Pick one system to own the enforcing header. The MetricPoints WordPress plugin can be that owner in monitor-only mode, which writes the report-only header for you.

A Basic CSP For A General Site

Content-Security-Policy-Report-Only: default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'self'; report-uri https://metricpoints.com/api/csp-reports/YOUR_REPORTING_KEY

This is the same starter MetricPoints shows on a site's Setup tab. default-src 'self' is the fallback, so scripts, styles, images, fonts, and connections must come from your own origin until you add them. base-uri, object-src, and frame-ancestors close three holes that are cheap to close on almost any site.

What That Header Is Saying

  • default-src 'self' — scripts, styles, images, fonts, and connections fall back to your own origin.
  • base-uri 'self' — an injected base tag cannot retarget relative URLs.
  • object-src 'none' — old plugin embeds such as Flash are refused.
  • frame-ancestors 'self' — other sites cannot frame your pages.
  • report-uri — browsers POST violation reports to MetricPoints. Replace YOUR_REPORTING_KEY with the key from Setup. Copy the full URL shown there if your account uses a different ingest host.

Read The First Reports Before You Enforce It

On a brochure site with one analytics tag, this starter is often close to a real enforcing policy. On anything with a CDN, a font host, a chat widget, embedded video, or a payment form, the first day of reports is the inventory.

Add the hosts you trust on purpose. A wildcard, or script-src that allows every https origin, will go quiet for the wrong reason: the browser stops telling you about new scripts. The useful version names the hosts the business actually uses.

When the important paths have been tested, change the header name from Content-Security-Policy-Report-Only to Content-Security-Policy and leave the report-uri line in place. Enforcement without reporting is how the next plugin update fails in silence.

A Basic CSP For WordPress

WordPress can use the same starter above. In report-only mode it is safe, and it will be noisy, because themes, Gutenberg, and most page builders ship inline styles and often inline scripts.

This version is the one I would paste on a typical WordPress site first. It keeps the same locks, allows inline styles so builders can lay out the page, and lets images and fonts load from https origins while you learn the real list. Scripts stay limited to your own origin, so a new third-party script still shows up in the reports.

Keep it on Content-Security-Policy-Report-Only until forms and checkout have been tested. Enforcing script-src 'self' on a live WordPress site will block inline scripts, Google Tag Manager, reCAPTCHA, payment fields, and a lot of plugin behavior.

WordPress Report-Only Header

Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data: https:; connect-src 'self'; frame-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'; form-action 'self'; report-uri https://metricpoints.com/api/csp-reports/YOUR_REPORTING_KEY

Replace YOUR_REPORTING_KEY with the key MetricPoints shows for that site. The reporting URL does not need to be added to connect-src. Browsers send CSP reports to report-uri even when connect-src is locked down.

WordPress Details That Matter

style-src keeps 'unsafe-inline' because Gutenberg and page builders write style attributes. Tightening styles can wait.

script-src does not include 'unsafe-inline'. The reports will list the inline scripts. Move the ones you control into files, or plan a nonce, before you enforce this header. Adding 'unsafe-inline' to script-src makes the policy much weaker against injected scripts.

form-action 'self' will report forms that post to a CRM, payment provider, or marketing tool. That report is a clue. Add the real destination before you enforce the policy, or the form will fail while the page still looks fine.

frame-src 'self' will report YouTube, maps, chat, and payment iframes. Same rule: name the ones the page needs, then enforce.

A cache plugin, CDN, or security plugin may already send a CSP. Confirm the live response has one policy owner. The walkthrough for plugin, form, and WooCommerce cases is Implementing CSP on WordPress Without Breaking Forms or Checkout.

Send The Reports To MetricPoints

The report-uri value is the whole integration. Create an account, add the site, and open that site's Setup tab. MetricPoints shows a report URI for the site. Paste it into the policy as report-uri, using the header above.

A browser that hits a violation POSTs a small JSON report to that URL. The path looks like this:

The Reporting Directive

report-uri https://metricpoints.com/api/csp-reports/YOUR_REPORTING_KEY

Use the full URI from Setup. The key in the path is how MetricPoints files the report on the right site.

What Shows Up After You Sign Up

Each report names the page, the directive that matched, and the blocked resource when the browser includes it. You can see whether the policy was report-only or enforcing. That distinction matters: a report-only finding means the resource still loaded.

Those reports stay on the site, next to the rest of its health. A one-off violation from a browser extension can sit as evidence. A new script repeating on checkout, or a form target you have never seen, is the thing to open. MetricPoints does not turn every raw CSP report into an incident. The useful question is whether the site's important path is actually failing, or whether a new resource needs a decision.

External checks still run if you never install anything. CSP reporting is the extra browser evidence. On WordPress, the MetricPoints plugin can write the report-only header once the site is connected. On any other stack, the header belongs wherever you already set response headers: the app, the host, or the CDN.

The product page is CSP monitoring. Account setup starts at sign up. After the site exists, Setup is where the report URI is copied from.

Use The CSP Builder Before You Guess

The starter headers above are deliberately small. The moment the site has analytics, a font host, a payment frame, or a booking embed, you want a policy you reviewed rather than a policy you hoped was complete.

The MetricPoints CSP Policy Builder is free. Pick a template, review the sources, and copy a candidate policy. Put your MetricPoints report URI in it, deploy that candidate as Content-Security-Policy-Report-Only, and read what real pages report.

The Security Headers Checker shows whether the live response is actually sending a CSP, and whether a reporting endpoint is present. A policy saved in a plugin screen and a policy the browser receives are often different, especially behind a CDN.

The builder is a starting point. It does not prove the contact form or checkout still works. Submit a test entry and place a test order after any change you intend to enforce. Notes on reading the reports are in CSP Monitoring Best Practices for Agency Client Sites, and the rollout sequence is in Implementing CSP in Production Without Breaking Client Sites.

Do This Week

  • Check the live response with the Security Headers Checker. Confirm whether a CSP is already being sent.
  • Create the site in MetricPoints and copy the report URI from Setup.
  • Publish the general starter, or the WordPress starter, as Content-Security-Policy-Report-Only.
  • Browse the homepage, a landing page, the form, and checkout. Confirm reports arrive on the site.
  • Separate trusted tools from sources you do not recognize. Add the trusted hosts on purpose.
  • Test the form and checkout again, then enforce. Leave report-uri in the enforcing header.

Leave The Reporting Line In Place

A CSP is a living header. The next plugin update, theme deploy, tag-manager publish, or payment-provider change can add a script the policy has never seen. With reporting on, that change becomes a report on the site. With reporting off, it becomes a broken form, a blank card field, or a script you find out about later.

Start with the basic policy. Point the reports at MetricPoints. Tighten from the evidence, on the CSP Policy Builder when you want a cleaner draft, and keep the report URI in every version you ship.

Follow the MetricPoints build

Occasional emails when we publish a new guide or ship something agencies can use: new monitoring checks, lessons from real client-site failures, and product updates. No fixed schedule. Unsubscribe anytime.

Tags

Content security policy Wordpress csp Csp reporting Xss prevention Website security

Related Articles