Enterprise Content Security Policy: Architecture, Deployment, and Hardening Guide
Prerequisites
- Basic HTTP header knowledge
- Experience with reverse proxies or web application deployment
Steps
Content Security Policy (CSP) is a browser-enforced control that reduces XSS, data injection, and unauthorized resource loading in modern web applications. This guide explains how enterprise teams design, deploy, monitor, and harden CSP across reverse proxies, CDNs, and application platforms.
Overview
Content Security Policy (CSP) is an HTTP response header that tells browsers which sources are allowed to load scripts, styles, images, frames, fonts, and network connections. Its primary purpose is to reduce client-side attack surface, especially cross-site scripting (XSS), malicious third-party content injection, clickjacking exposure when combined with related headers, and data exfiltration through unauthorized outbound requests.
Enterprises use CSP to enforce consistent browser-side controls across internet-facing applications, internal portals, and SaaS-integrated front ends. In practice, CSP is most effective when paired with secure coding, dependency governance, Subresource Integrity (SRI), and telemetry pipelines that collect report-to or report-uri violations for tuning.
Architecture
A typical enterprise CSP deployment has four components:
- Application tier: emits CSP headers dynamically or via framework middleware
- Edge tier: CDN, WAF, ingress, or reverse proxy injects baseline policy
- Browser: enforces the policy and blocks noncompliant resources
- Reporting pipeline: receives violation reports for analysis in SIEM or observability tools
Deployment models
- Application-managed CSP: best when each app needs granular nonces, hashes, or route-specific policies
- Proxy-managed CSP: useful for standardized controls across many legacy apps
- Hybrid model: edge injects a strict baseline, app adds nonces and exceptions
Data flow
- User requests a page.
- Web server or proxy returns
Content-Security-Policyand optionallyContent-Security-Policy-Report-Only. - Browser evaluates every requested resource against directives such as
default-src,script-src,style-src,img-src, andconnect-src. - Violations are blocked or only reported, depending on enforcement mode.
- Reports are sent to a collector endpoint and forwarded to Splunk, Microsoft Sentinel, or Elastic.
Implementation Guide
1. Start with report-only mode
Use report-only first to avoid breaking production traffic.
curl -I https://app.example.com | grep -i content-security-policy
2. Add CSP in NGINX
Create /etc/nginx/conf.d/csp.conf:
add_header Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self' 'nonce-$request_id' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self' https://api.example.com; frame-ancestors 'none'; base-uri 'self'; object-src 'none'; report-uri https://csp-report.example.com/report;" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
Validate and reload:
sudo nginx -t && sudo systemctl reload nginx
3. Generate nonces in the application
For dynamic pages, generate a cryptographically random nonce per response and attach it to both the CSP header and allowed inline script tags.
4. Roll out enforcement gradually
After reviewing reports, move from report-only to blocking mode by replacing Content-Security-Policy-Report-Only with Content-Security-Policy.
5. Monitor violations centrally
Forward CSP reports to your SIEM. Filter noisy browser extensions and known false positives before creating detection rules.
Code Examples
Example 1: NGINX enforcement policy
server {
listen 443 ssl http2;
server_name app.example.com;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'nonce-$request_id' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' https://api.example.com; object-src 'none'; frame-ancestors 'none'; base-uri 'self'; upgrade-insecure-requests;" always;
}
Example 2: Kubernetes Ingress annotations
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: webapp
annotations:
nginx.ingress.kubernetes.io/configuration-snippet: |
add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' https://api.example.com; object-src 'none'; frame-ancestors 'none';" always;
spec:
ingressClassName: nginx
Example 3: Python Flask nonce generation
from flask import Flask, g, render_template
import secrets
app = Flask(__name__)
@app.before_request
def set_nonce():
g.csp_nonce = secrets.token_urlsafe(16)
@app.after_request
def apply_csp(response):
response.headers["Content-Security-Policy"] = f"default-src 'self'; script-src 'self' 'nonce-{g.csp_nonce}'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'"
return response
@app.route("/")
def index():
return render_template("index.html", csp_nonce=g.csp_nonce)
Security Hardening
- Prefer nonces or hashes over
'unsafe-inline'for scripts. - Set
object-src 'none'andbase-uri 'self'in nearly all applications. - Use
frame-ancestors 'none'or explicit trusted domains to reduce framing abuse. - Restrict
connect-srcto approved APIs to limit browser-based exfiltration. - Combine CSP with TLS 1.2+, HSTS, SRI, secure cookies, and dependency pinning.
- Limit who can modify CSP at the CDN, ingress, or application layer through RBAC and change control.
Comparison
| Capability | Content Security Policy | Cloudflare Page Shield | Report URI |
|---|---|---|---|
| Pricing | Native browser standard, no license cost; operational cost only | Commercial add-on within Cloudflare platform | Commercial SaaS pricing based on reporting volume and features |
| Deployment | Header-based via app, CDN, WAF, or proxy | Cloudflare-managed for proxied applications | SaaS collector for CSP and related browser reports |
| Scalability | High, enforced by browsers globally | High, leverages Cloudflare edge | High for report ingestion and analytics |
| Security | Strong preventive control for resource loading and XSS reduction | Adds script inventory, monitoring, and supply-chain visibility | Strong reporting and analysis, not direct prevention by itself |
Troubleshooting
1. Inline script blocked
Log sample:
{"csp-report":{"document-uri":"https://app.example.com/","violated-directive":"script-src-elem","blocked-uri":"inline","original-policy":"default-src 'self'; script-src 'self'","disposition":"enforce","status-code":200}}
Fix: replace inline scripts with external files or add a per-request nonce and update script-src.
2. API calls blocked by connect-src
Browser console sample:
Refused to connect to 'https://api.partner.com/v1/data' because it violates the following Content Security Policy directive: "connect-src 'self' https://api.example.com".
Fix: add the required partner endpoint explicitly to connect-src after vendor risk review.
3. CDN-hosted assets blocked
NGINX access/error sample:
2026/08/28 10:14:22 [info] 2145#2145: *8813 client violated policy, blocked-uri: https://static.cloudflareinsights.com/beacon.min.js, host: app.example.com, request: "GET / HTTP/2.0"
Fix: inventory all third-party assets, approve required domains, and avoid broad wildcards such as https: in script-src.
Best Practices
Do
- Start with
Report-Onlyfor at least one release cycle. - Use route-specific policies for admin portals, payment pages, and public marketing sites.
- Maintain a controlled allowlist for CDNs, analytics, and partner APIs.
- Version CSP changes in Git and validate them in CI with integration tests.
Don't
- Do not rely on
'unsafe-inline'and'unsafe-eval'unless there is a documented exception. - Do not use overly broad sources like
*orhttps:for scripts. - Do not let multiple teams inject conflicting CSP headers at CDN, ingress, and app layers.
- Do not treat CSP as a substitute for output encoding, input validation, or secure SDLC controls.
Have a project in mind?
Get an instant AI price estimate for it, or talk directly to our team.
One email a month on what we learn building with AI