CSP Directives Guide: What Each Directive Actually Protects

A practical guide to CSP directives for real sites, including script-src, connect-src, form-action, frame-ancestors, reporting, and WordPress tradeoffs.

Michael
September 2, 2025

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.

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

Csp directives Script-src Connect-src Form-action Frame-ancestors

Related Articles