---
title: Roll out a policy
---

Scan the site, send Consepo's policy in report-only mode, fix what the reports show, then enforce it. The site needs a connected account; see [Connect your Consepo account](../getting-started/connect.md).

## The rollout

<!-- wp:docspress/flow {"start":1,"steps":[{"title":"Scan the site","content":"\u003cp\u003eOn the \u003cstrong\u003eOverview\u003c/strong\u003e, choose \u003cstrong\u003eScan this site\u003c/strong\u003e. Consepo crawls the home page and the pages it links to with a real browser and suggests a policy from what they load. Later scans use \u003cstrong\u003eScan again\u003c/strong\u003e, which re-runs the same scan rather than creating a new one.\u003c/p\u003e"},{"title":"Wait for the policy","content":"\u003cp\u003eThe Scan facts show \u003cstrong\u003eQueued\u003c/strong\u003e, then \u003cstrong\u003eComplete\u003c/strong\u003e. The plugin checks every five minutes for up to three hours, and \u003cstrong\u003eRefresh\u003c/strong\u003e asks Consepo straight away.\u003c/p\u003e"},{"title":"Send it in report-only mode","content":"\u003cp\u003eUnder \u003cstrong\u003ePolicy\u003c/strong\u003e, choose \u003cstrong\u003eReport-only\u003c/strong\u003e, keep the source on \u003cstrong\u003eConsepo\u003c/strong\u003e, and choose \u003cstrong\u003eSave\u003c/strong\u003e in the header. Browsers report what the policy would block and block nothing.\u003c/p\u003e"},{"title":"Check the header","content":"\u003cp\u003eThe \u003cstrong\u003eHeader\u003c/strong\u003e section shows exactly what public pages now send.\u003c/p\u003e"},{"title":"Read the reports","content":"\u003cp\u003eWith \u003cstrong\u003eReport violations to Consepo\u003c/strong\u003e on, browsers send reports to the site's Consepo report address. Review them in Consepo and allow what the site genuinely needs.\u003c/p\u003e"},{"title":"Enforce","content":"\u003cp\u003eWhen the reports show nothing you want to keep, switch the mode to \u003cstrong\u003eEnforcing\u003c/strong\u003e and save.\u003c/p\u003e"}]} /-->

<!-- wp:docspress/callout {"tone":"warning","title":"Reports need monitoring turned on in Consepo","content":"\u003cp\u003eConsepo keeps violation reports only while monitoring is on for the site's application in the Consepo dashboard. The plugin sets the report address; it cannot switch monitoring on.\u003c/p\u003e","collapsible":false} /-->

## What a scan leaves behind

The Overview's **What the crawl found** section says whether the crawl saw inline scripts, inline styles or likely `eval()`. Inline scripts are the common case on WordPress; [Script nonces](script-nonces.md) lets WordPress's own inline scripts run without `'unsafe-inline'`.

A site has one Consepo scan. **Scan again** re-runs it in place, so it does not count as a new application against the Consepo plan. While a re-run is in progress, and if it fails, the site keeps sending the last policy that completed. If the scan was deleted in Consepo, the Overview shows **Deleted in Consepo** and the next scan creates a new one.

Scans started from the Overview crawl up to 10 pages, 10 links deep. The [admin REST API](../reference/rest-api.md) accepts `limit` up to 100 and `depth` up to 10.

## Modes and sources

| Setting | Values | Effect |
| --- | --- | --- |
| Mode | Off, Report-only, Enforcing | No header, `Content-Security-Policy-Report-Only`, or `Content-Security-Policy` |
| Source | Consepo, Custom | The last policy a scan produced, or the policy in the **Custom policy** field |
| Report violations to Consepo | On (default), off | Sets `report-uri` to the site's Consepo report address, once a scan exists |

Switch the source to **Custom** to keep changes of your own: paste the policy from Consepo, edit it, and save. A custom policy is cleaned before it is stored and sent:

- Control characters and line breaks become spaces, so a stored policy cannot inject a second header.
- Policies longer than 8,000 characters are cut at the last directive that fits.
- When a directive appears twice, the first one wins, as it does in browsers. Malformed directive names are dropped.
- Any `report-uri` or `report-to` is removed. The plugin adds its own `report-uri` when reporting is on.

## Where the header is sent

The header goes on public pages only, from WordPress's `send_headers` action. It is not sent:

- in wp-admin, on `wp-login.php`, or on REST API responses;
- in the Customizer preview;
- when the mode is **Off**, or the chosen source has no policy yet (no completed scan, or an empty custom policy);
- when PHP has already sent a header with the same name, for example from another security plugin. Two policies would both apply and the page would get the stricter of them, so the plugin leaves the other one alone;
- when the `consepo_csp_scanner_send_policy` filter returns false. See [Hooks and constants](../reference/hooks.md).

An example of what a public page received in report-only mode, with nonces on and before the first scan (so without `report-uri`):

<!-- wp:docspress/colorful-code {"language":"plaintext","filename":"Response header","code":"content-security-policy-report-only: default-src 'self'; script-src 'self' 'nonce-U9khxe9n2dwfUgmnZAyGQk6C'; img-src 'self' data:","highlightedLines":"","showLineNumbers":false,"caption":"Captured from a WordPress 7.1 test site with a custom policy. The nonce is different on every response."} /-->
