How To Think About CSP Directives
CSP directives are not all the same kind of rule.
Some control what the browser can fetch, like scripts, styles, images, fonts, frames, and API calls. Some control document behavior, like whether the page can be framed or whether a base tag can change relative URLs. Some control reporting.
The practical question is not "How many directives can I add?" The practical question is "Which browser behaviors does this site need, and which behaviors should never happen?"
default-src
default-src 'self';
default-src is the fallback for many fetch directives. I like setting it restrictive so missing rules do not accidentally become broad permissions.
script-src
script-src 'nonce-{RANDOM_PER_RESPONSE}' 'strict-dynamic';
script-src is usually the highest-value directive because injected JavaScript is one of the main risks CSP is meant to reduce. Long term, aim for nonces or hashes instead of unsafe-inline.
style-src
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
style-src controls stylesheets and inline style behavior. Many WordPress themes and page builders need inline styles, so this often tightens more slowly than script-src.
img-src
img-src 'self' data: https: blob:;
img-src controls images, icons, tracking pixels, and generated image URLs. It is common to allow HTTPS and data URLs at first, then narrow important sites where practical.
font-src
font-src 'self' https://fonts.gstatic.com;
font-src controls web fonts. Broken font rules usually show up as visual issues, so check key pages after tightening this.
connect-src
connect-src 'self' https://api.example.com https://analytics.example.com wss://realtime.example.com;
connect-src controls browser connections: fetch, XHR, WebSockets, EventSource, analytics calls, payment APIs, maps, and monitoring beacons. This directive matters a lot for modern sites.
media-src
media-src 'self' https://media.example.com;
media-src controls audio and video sources. If the site embeds hosted media, test those pages before enforcement.
object-src
object-src 'none';
object-src controls old plugin-style embeds such as object, embed, and applet. For most modern client sites, set this to none.
frame-src
frame-src 'self' https://payments.example.com https://www.youtube.com;
frame-src controls what this page is allowed to embed in frames. Payment providers, maps, scheduling tools, video embeds, and some chat tools often need this directive.
child-src
child-src 'self' https://trusted-embed.com;
child-src is a fallback for frames and workers in some CSP versions. I usually define frame-src and worker-src explicitly when possible so the intent is easier to read.
worker-src
worker-src 'self' blob:;
worker-src controls web workers and service workers. Some build tools, upload widgets, maps, and performance-heavy apps depend on workers or blob workers.
manifest-src
manifest-src 'self';
manifest-src controls where web app manifests can load from. This is usually simple, but it is worth defining for progressive web apps.
form-action
form-action 'self' https://payments.example.com https://crm.example.com;
form-action controls where forms are allowed to submit. This is a business-critical directive for lead forms, checkout, donations, login, and account flows.
base-uri
base-uri 'none';
base-uri controls whether a page can define a base URL for relative links. If an attacker can inject a base tag, relative form and link targets can behave in surprising ways.
frame-ancestors
frame-ancestors 'self';
frame-ancestors controls who can embed your page. Use it for clickjacking protection. It does not fall back to default-src, so define it explicitly.
upgrade-insecure-requests
upgrade-insecure-requests;
upgrade-insecure-requests tells the browser to upgrade HTTP resource requests to HTTPS where possible. It helps clean up old mixed-content behavior, but you still need to fix broken HTTP assets.
report-to And report-uri
Reporting-Endpoints: csp="https://example.com/csp-reports"
Content-Security-Policy:
default-src 'self';
report-to csp;
report-uri https://example.com/csp-reports;
report-to is the modern reporting directive and points at a Reporting-Endpoints group. report-uri is deprecated in CSP Level 3, but it is still useful for compatibility, so many production policies include both.
Directives I Prioritize On Client Sites
- script-src, because unexpected JavaScript is one of the highest-risk browser behaviors.
- form-action, because broken or hijacked forms affect leads, checkout, login, donations, and bookings.
- connect-src, because modern sites rely on browser API calls, analytics, payment calls, and monitoring endpoints.
- frame-src, because payments, videos, maps, calendars, and embedded tools often live here.
- frame-ancestors, because clickjacking protection should be explicit.
- object-src and base-uri, because they are easy wins on most modern sites.
- report-to and report-uri, because a CSP without monitoring is much harder to operate safely.
Common Keywords
- 'self': allows the same origin as the protected document.
- 'none': allows no sources. Use it alone for directives you want fully blocked.
- 'unsafe-inline': allows inline scripts or styles. Avoid it for scripts when you can.
- 'unsafe-eval': allows eval-like behavior. Avoid it in production unless a dependency truly requires it.
- 'strict-dynamic': lets nonce or hash-trusted scripts load additional scripts without listing every host.
- data:: commonly used for inline images or fonts, but should not be allowed everywhere by habit.
- blob:: often needed for workers, generated downloads, previews, and some media behavior.
- https:: allows any HTTPS source, useful during discovery but broad for long-term enforcement.
A Practical Baseline
Content-Security-Policy:
default-src 'self';
script-src 'nonce-{RANDOM_PER_RESPONSE}' 'strict-dynamic';
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
img-src 'self' data: https:;
font-src 'self' https://fonts.gstatic.com;
connect-src 'self' https://api.example.com https://analytics.example.com;
frame-src 'self' https://payments.example.com;
object-src 'none';
base-uri 'none';
frame-ancestors 'self';
form-action 'self' https://payments.example.com;
upgrade-insecure-requests;
report-uri https://example.com/csp-reports;
report-to csp;
This is not a finished policy for every site, but it is a reasonable shape for a monitored rollout after you have inventoried critical paths.
The Takeaway
A good CSP directive set should describe how the site actually works.
If you cannot explain why a source or directive is allowed, that is a review item. If a strict directive breaks a business-critical flow, that is also a review item. CSP is strongest when it is both intentional and monitored.