Example of a medium-strict Content Security Policy
A "medium-strict" CSP strikes a balance: it's protective against common threats like XSS (cross-site scripting) and clickjacking, but realistic for many modern websites that rely on inline styles, data URIs for images, or limited third-party resources (e.g., analytics scripts or CDNs). It avoids the full strictness of nonce/hash-based policies (which require server-side changes) while blocking dangerous features like inline scripts and plugins.
Here's a solid example header:
Content-Security-Policy: default-src 'self';
script-src 'self' https://trusted-scripts.example.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data: https://images.example.com;
font-src 'self';
object-src 'none';
base-uri 'self';
frame-ancestors 'self';
upgrade-insecure-requests;
report-uri https://your-report-endpoint.example.com/csp-report
This policy is delivered via the HTTP response header (preferred method). Below, I'll break down each directive with explanations.
Directive Breakdown
| Directive | Value in example | Explanation |
|---|---|---|
| default-src | 'self' | fallback policy for any resource type not explicitly defined. Allows loading only from the same origin (same protocol, host, and port), this is a strong baseline to restrict everything by default |
| script-src | 'self' https://trusted-scripts.example.com | controls where JavaScript can be loaded from. 'self' allows scripts from your own domain, adding a trusted external host (e.g., for analytics like Google Analytics) permits that, notably, no 'unsafe-inline' here—this blocks inline <script> tags, preventing most XSS injections of malicious scripts. No 'unsafe-eval' either, blocking risky functions like eval(). |
| style-src | 'self' 'unsafe-inline' | controls CSS sources. 'self' for external stylesheets from your domain. 'unsafe-inline' allows inline <style> tags and style attributes—common in many sites (e.g., for dynamic styling), but less risky than for scripts since CSS XSS is rarer, for stricter, remove 'unsafe-inline' and use nonces/hashes. |
| img-src | 'self' data: https://images.example.com | allows images from your domain, data: URIs (e.g., base64-embedded images, common for icons), and a trusted CDN, prevents loading images from arbitrary sources (which could be used for tracking or exploits). |
| font-src | 'self' | restricts custom fonts to your own domain, blocking potential font-based exploits or unauthorized loading. |
| object-src | 'none' | blocks all plugins (e.g., Flash, Java applets)—obsolete and high-risk technologies. 'none' is strongly recommended for modern sites. |
| base-uri | 'self' | prevents manipulation of the <base> element, which could redirect relative URLs and enable phishing-like attacks. |
| frame-ancestors | 'self' | controls who can embed your page in <iframe>, <frame>, etc. 'self' prevents clickjacking by blocking embedding from other sites. (Obsoletes the older X-Frame-Options header.) |
| upgrade-insecure-requests | (no value needed) | automatically upgrades HTTP requests to HTTPS, helping mitigate mixed-content issues without breaking legacy links. |
| report-uri | https://your-report-endpoint.example.com/csp-report | sends violation reports (e.g., blocked resources) to your endpoint for monitoring. Use this to detect attacks or misconfigurations. (modern alternative: report-to with Reporting-Endpoints header.) |
Why this is "medium-strict":
- protective: blocks inline scripts, plugins, and framing; enforces same-origin defaults; upgrades to HTTPS.
- realistic: allows
'unsafe-inline'for styles anddata:for images, common in real-world apps without requiring major refactoring. - for full strictness, switch to nonce-based (e.g.,
script-src 'nonce-randomValue' 'strict-dynamic'; style-src 'nonce-randomValue';)—ideal but needs dynamic nonce generation per page.
Always start with
Content-Security-Policy-Report-Onlyfirst to test without blocking, monitor reports, then enforce. Tools like Google CSP Evaluator can help validate your policy.
