· 4 min read
How to Draft and Test a Content Security Policy
Manesh Jayawardhana
CIO & Co-founder
You add a Content-Security-Policy header that allows only 'self', deploy it, and discover that analytics, fonts, styles, and a payment frame no longer work. Removing the header restores the page, but it also abandons a useful browser security layer instead of identifying the required sources.
A CSP header builder makes the directive syntax easier to draft. The safe workflow is inventory, report-only testing, violation review, refinement, and then enforcement. Copying an example policy directly into production is risky because a policy must match the resources and inline code used by that specific application.
What building a CSP header actually involves
Content Security Policy tells compatible browsers which sources may provide resources such as scripts and styles. default-src is a fallback for resource types without a more specific directive. script-src and style-src override that fallback for scripts and styles.
The builder accepts space-separated source expressions for those three directives. It can add object-src 'none', add upgrade-insecure-requests, and switch the header name between enforced Content-Security-Policy and monitoring-oriented Content-Security-Policy-Report-Only.
| Directive | Controls | Safer Starting Point | Test For |
|---|---|---|---|
default-src | General fallback | 'self' | Unlisted resource types |
script-src | JavaScript sources | Intentional allowlist | Inline and third-party code |
style-src | CSS sources | Intentional allowlist | Inline styles and fonts |
object-src | Plugin embeds | 'none' | Legacy object content |
upgrade-insecure-requests | HTTP subresources | Upgrade to HTTPS | Resources lacking HTTPS |
A CSP reduces the impact of some content injection attacks, but it is not a replacement for output encoding, input handling, dependency security, authentication, or secure application design.
Test more than the homepage. Authentication, checkout, dashboards, uploads, embedded media, error pages, and lazy-loaded components may contact sources that never appear during a quick first load. Record why each allowed origin exists so obsolete access can be removed during later reviews instead of accumulating forever.
Why people get stuck here
- They enforce the first draft and break legitimate resources.
- A broad domain is allowed when only one specific origin is needed.
'unsafe-inline'is added permanently to silence violations.- Development hosts, browser extensions, or test tools pollute reports.
- A report-only header is expected to send reports without a reporting directive and endpoint.
The current builder changes the header name for report-only mode but does not configure report-to, report-uri, or a Reporting-Endpoints response header. You can observe console violations during manual testing, but collecting reports requires additional server configuration.
What a practical policy looks like
Based on a resource inventory
List first-party and third-party scripts, styles, images, fonts, connections, frames, and workers. Remove services that are no longer required before allowing their domains.
Tested without immediate blocking
Deploy an appropriate report-only policy in a controlled environment, exercise important routes, and inspect violations. Separate genuine application needs from injected extensions and development-only sources.
Narrowed over time
Prefer nonce- or hash-based approaches for scripts when the application can support them. Avoid leaving broad schemes, wildcards, 'unsafe-inline', or 'unsafe-eval' merely because they make the console quiet.
Common mistakes to avoid
- Copying another site’s sources without understanding them.
- Omitting resources loaded only after login or payment actions.
- Including the
Content-Security-Policy:label inside a platform field that expects only the value. - Assuming report-only mode blocks anything.
- Enforcing without testing rollback and monitoring.
Header installation differs across web servers, frameworks, CDNs, and hosting providers. Review the generated result with someone responsible for the application’s security and deployment. For another public control file with different responsibilities, read how to test robots.txt rules.
How to do it with CSP Header Builder
Open the CSP Header Builder after listing the sources the application genuinely needs.
- Set the default source, commonly beginning with
'self'. - Add only required script origins to Script sources.
- Add required style origins and review any
'unsafe-inline'use. - Keep Block plugins enabled unless the application has a tested reason not to.
- Choose whether to upgrade insecure requests and enable report-only mode for the draft.
- Generate, copy, deploy in a test environment, and review violations before enforcement.
The tool produces a compact header locally. It does not scan your site, create nonces or hashes, validate every source expression, configure a reporting endpoint, or deploy the policy.
Frequently asked questions
Does report-only mode block resources?
No. It observes policy violations without enforcing the restrictions. To collect reports remotely, configure the required reporting directives and endpoint outside this builder.
Why is unsafe-inline discouraged?
It permits inline code or styles covered by the directive and can weaken an important CSP protection. Where practical, refactor inline code and use carefully implemented nonces or hashes.
Can I use the same CSP on every website?
No. Each application loads a different set of resources and may use different frameworks or inline patterns. Start from the real resource inventory and test every important route.
Final thought
A CSP is strongest when it describes the application narrowly and remains maintainable. Draft from evidence, use report-only testing deliberately, and enforce only after legitimate resource needs are understood.