Originally published on Sitecheckly.
Start with a Content-Security-Policy-Report-Only header that restricts resources to your own origin. Test the page, review browser console violations, and adjust the policy before enforcing it. Report-only mode lets you find what the policy would block without blocking those resources. MDN explains the report-only behavior.
Survey method
The November 2026 Sitecheckly survey examined the Tranco top 10,000, using Tranco list 7462X. Analysis was possible for 4,934 of 10,000 domains. The others were mostly CDN, DNS and API hosts, sites whose robots.txt asked crawlers to stay away, or sites that blocked automated requests. Among the analyzed sites, 66% were missing CSP. See the survey methodology for scope and limitations.
That finding is a reason to check your response headers, not a reason to copy an enforcing policy into production without testing.
What does a starter Content Security Policy do?
CSP tells the browser which sources a page may use for resources such as scripts, styles and images. It can reduce the impact of cross-site scripting by restricting which code may execute. It is an additional defense, not a substitute for fixing injection flaws. See MDN’s CSP guide.
This content security policy example is a starting point for a page that serves its scripts and styles from its own origin:
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data: The value uses 'self' for the page’s own origin. The image rule also permits data URLs through data:. Keep that colon: data' is not the correct source expression. These source expressions and directives are documented in the CSP header reference.
This is deliberately a small baseline. It is not a policy guaranteed to fit your page, and it is not a complete hardening policy. Expect to review anything loaded from another origin, along with inline code.
How does CSP report-only mode work?
A report-only policy observes violations without enforcing its restrictions. A resource can therefore load successfully while still producing a violation. Read that message as “this would violate the proposed policy,” not “this resource was blocked.” See MDN’s report-only reference.
Use this testing sequence:
- Open browser developer tools and select the Console and Network panels.
- Reload the page and inspect the document’s response headers.
- Confirm that the intended report-only header is present.
- Exercise menus, forms, search, media and other interactive features.
- Record each violation, the resource involved and the feature that needs it.
Test logged-in flows and third-party integrations too. A clean test of a public landing page does not establish that checkout, account pages or embedded tools are ready.
The starter header above does not configure a report collector. If you want reports delivered to a service, set up a working reporting endpoint and the relevant reporting directive. For example, report-uri names an endpoint that receives violation reports, though it is deprecated. Review MDN’s reporting guidance before choosing a reporting setup.
Do not assume reports are being stored just because the header is present. Verify delivery at your collector separately.
Which CSP directives should you check first?
Start with the resource types most likely to affect visible features:
| Directive | What to investigate |
|---|---|
script-src | External scripts, inline script blocks and inline event handlers. |
style-src | External stylesheets, inline style blocks and style attributes. |
img-src | Images from other origins and any data URLs the page uses. |
default-src | Resource types without their own explicit fetch directive. |
default-src is a fallback for fetch directives that are not explicitly set. In this baseline, review fonts, connections and frames as well as scripts and styles. When you define a specific fetch directive, its source list takes the place of that fallback for the resource type. These behaviors are covered in the CSP directive reference.
For each violation, ask:
- Does the page still need this resource?
- Which feature depends on it?
- Is the reported origin the source you intended to trust?
- Can you remove the dependency instead of expanding the policy?
Keep a short record of each allowed source and its purpose. That makes later reviews easier than reconstructing why an origin was added.
How do you add the policy in Nginx?
Place this directive inside the relevant existing server or location block:
add_header Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:" always; The always parameter applies the header regardless of the response status code. Header inheritance depends on configuration context, so check for other add_header directives in nested blocks rather than assuming this setting reaches every response. See the Nginx headers module documentation.
Before considering the change deployed:
- Check the Nginx configuration for errors.
- Reload Nginx after the check succeeds.
- Request the public page and inspect its actual response headers.
- Check relevant routes that use different location blocks.
Inspect the response the browser receives, not just the configuration file you edited.
How do you add the policy in Apache?
With mod_headers enabled, add this directive to the applicable server or virtual-host configuration:
Header always set Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:" Apache’s Header directive supports the always condition shown here. Its documentation also explains how header tables can lead to duplicate headers, especially when other components set them. See the Apache mod_headers documentation.
Check the configuration, reload Apache, then inspect the public response. Look for:
- A missing report-only header.
- Duplicate CSP headers.
- An existing enforced policy that you did not intend to change.
- Different header values on different routes.
If another application or proxy already sets CSP, identify which layer should own the policy before adding another configuration.
How do you move from reports to enforcement?
Investigate violations individually. Do not turn the console output into an automatic allowlist.
Remove unnecessary dependencies first. Then allow only the sources the page actually needs. Avoid adding broad permissions just to make the messages disappear.
For inline scripts, consider a nonce- or hash-based approach instead of broadly allowing inline code. A nonce must be generated for each response and applied to the intended script elements; a hash must match the allowed script content. These approaches require changes beyond copying a static header. Follow the OWASP CSP implementation guidance.
Once the reviewed policy passes your functional tests, deploy its value under Content-Security-Policy instead of relying only on report-only mode. Repeat the same tests after enforcement. Do not promote the starter value unchanged unless your testing shows it fits the page.
Keep reviewing violations as dependencies and page features change. If you want collected reports after enforcement, retain a working reporting configuration in the enforced policy.
What to do next
- Run the free Sitecheckly security audit. It passively checks security headers on the single URL you enter, with no sign-up. It does not replace browser interaction testing.
- Test other representative pages separately before deploying a shared policy across them.
- Review the security headers guide for related headers that need their own configuration decisions.
Frequently asked questions
Can CSP replace output encoding?
No. Treat CSP as defense in depth, not a replacement for preventing injection. See the OWASP CSP cheat sheet.
Can an enforced policy and a report-only policy run together?
Yes. You can enforce one policy while testing another in report-only mode. See MDN's report-only reference.
Does CSP prevent other sites from embedding my page?
The frame-ancestors directive can control which parents may embed a page. Add it explicitly if embedding restrictions are needed; it does not fall back to default-src. See MDN's frame-ancestors reference.
Want to see how your own site does? Run a free audit at sitecheckly.com.
Figures in this article come from Sitecheckly's own audit data; see the methodology.

