Content Security Policy (CSP)

What is Content Security Policy?

Content Security Policy (CSP) is a security feature that controls what resources your app can load from external sources. It acts as a whitelist that tells the browser (WebView) which scripts, stylesheets, images, fonts, and APIs are allowed to load.

Why CSP Matters

CSP is your app's first line of defense against:

  • Cross-Site Scripting (XSS) attacks
  • Code injection from compromised dependencies
  • Unauthorized tracking from third-party scripts
  • Data theft through malicious external resources

How CSP Works in Your App

┌─────────────────────────────────────────┐
│  Internal Asset Server                  │
│  Serves: index.html                     │
│  CSP Header: "script-src 'self' ..."    │ ← You configure this
└─────────────────────────────────────────┘


┌─────────────────────────────────────────┐
│  WebView loads index.html               │
│  Enforces the CSP from server           │
└─────────────────────────────────────────┘


┌─────────────────────────────────────────┐
│  Your HTML tries to load resources:     │
│  <script src="https://cdn.com/lib.js">  │ ← CSP must allow this!
│  <link href="https://fonts.com/...">    │ ← CSP must allow this!
└─────────────────────────────────────────┘

Default Configuration

By default, your app is configured with three sets of headers that provide a permissive but secure baseline. These headers are already set for your internal asset server:

Block 1: No Cache

Purpose: Ensure you always get the latest version of your app files.

Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Expires: 0

Why no caching? Unlike a CDN or remote server, your internal asset server is extremely fast (< 1ms) because files are loaded directly from your device. Caching provides zero performance benefit and can cause stale content issues during development and after updates.

Block 2: Security Headers

Purpose: Protect against common web vulnerabilities.

X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
Referrer-Policy: strict-origin-when-cross-origin

What these do:

  • X-Content-Type-Options: nosniff - Prevents MIME sniffing attacks
  • X-Frame-Options: SAMEORIGIN - Prevents clickjacking (your app can't be embedded in malicious iframes)
  • X-XSS-Protection: 1; mode=block - Enables browser XSS filter
  • Referrer-Policy: strict-origin-when-cross-origin - Controls what referrer information is sent with requests

Block 3: Permissive Content Security Policy

Purpose: Allow your app to load resources from anywhere while blocking dangerous legacy plugins.

Content-Security-Policy:
  default-src * 'unsafe-inline' 'unsafe-eval' data: blob:;
  script-src * 'unsafe-inline' 'unsafe-eval';
  style-src * 'unsafe-inline';
  img-src * data: blob:;
  font-src * data:;
  connect-src *;
  media-src *;
  frame-src *;

What this allows:

  • ✅ Load scripts from any HTTPS source
  • ✅ Load stylesheets from any HTTPS source
  • ✅ Load images, fonts, media from anywhere
  • ✅ Make API calls to any domain (subject to CORS)
  • ✅ Use inline scripts and eval() (common in game engines)
  • ❌ Blocks Flash, Java plugins, ActiveX (security risk)

Security benefits even with permissive CSP:

  • 🛡️ Prevents Flash Player exploits
  • 🛡️ Blocks Java applets
  • 🛡️ Disables ActiveX controls
  • 🛡️ Stops legacy plugin attacks

Customizing CSP Headers

You can override the default headers by adding custom headers in the SaaS dashboard. Your custom headers will replace the corresponding default headers.

Use Case 1: Self-Contained App (Maximum Security)

If your app doesn't load any external resources (no CDNs, no external APIs, no third-party scripts), use the strictest CSP for maximum security:

Custom headers to add:

Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self'; connect-src 'self'; media-src 'self'; object-src 'none'; frame-ancestors 'none';

What this blocks:

  • ❌ External scripts (CDN libraries must be bundled locally)
  • ❌ External stylesheets (fonts/styles must be local)
  • ❌ External images (all images must be in your app)
  • ❌ External API calls (only localhost allowed)

What this allows:

  • ✅ All resources from your app's files
  • ✅ Inline scripts and styles (needed for many games)
  • ✅ Data URLs and blobs (for dynamic content)

Use this if: Your game/app is completely self-contained with no external dependencies.

Use Case 2: External CDNs and APIs (Balanced Security)

If your app loads external resources like CDN libraries, Google Fonts, or calls external servers, you need to allow HTTPS sources while maintaining security:

Custom headers to add:

Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' https:; style-src 'self' 'unsafe-inline' https:; img-src 'self' data: blob: https:; font-src 'self' data: https:; connect-src 'self' https: wss:; media-src 'self' https:; object-src 'none'; frame-ancestors 'none';

What this allows:

  • ✅ Scripts from any HTTPS source (CDN libraries)
  • ✅ Stylesheets from any HTTPS source (Google Fonts, etc.)
  • ✅ Images from HTTPS sources
  • ✅ API calls to HTTPS endpoints
  • ✅ WebSocket connections (wss://)
  • ✅ Inline scripts (game engines need this)

What this blocks:

  • ❌ HTTP resources (only HTTPS allowed for security)
  • ❌ Flash, Java, and legacy plugins

Use this if: Your game/app loads libraries from CDNs or makes requests to external servers.

Use Case 3: Specific External Sources Only

If you want to restrict external resources to specific trusted domains:

Custom headers to add:

Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://unpkg.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: blob: https://your-cdn.com; connect-src 'self' https://api.yourapp.com; media-src 'self'; object-src 'none'; frame-ancestors 'none';

What this allows:

  • ✅ Scripts only from cdn.jsdelivr.net and unpkg.com
  • ✅ Styles only from fonts.googleapis.com
  • ✅ Fonts only from fonts.gstatic.com
  • ✅ API calls only to api.yourapp.com
  • ✅ Images from your specific CDN

Use this if: You want tight control over exactly which external sources can be used.

Use Case 4: WebAssembly and High-Performance Features

If your game uses WebAssembly, SharedArrayBuffer, or high-resolution timers (for advanced games), you need cross-origin isolation headers:

Custom headers to add:

Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp

Important: These headers are very strict and may break external resource loading. Only use them if your game specifically requires these features.

Use this if: Your game uses WASM with threads, SharedArrayBuffer, or needs high-performance timing APIs.

Understanding CSP Directives

Here's what each CSP directive controls:

DirectiveControlsExample
default-srcFallback for all other directivesdefault-src 'self'
script-srcJavaScript files and inline scriptsscript-src 'self' https://cdn.com
style-srcCSS stylesheetsstyle-src 'self' 'unsafe-inline'
img-srcImagesimg-src 'self' data: https:
font-srcWeb fontsfont-src 'self' https://fonts.com
connect-srcAPI calls (fetch, XHR, WebSocket)connect-src 'self' https://api.com
media-srcAudio and videomedia-src 'self' https:
object-srcFlash, Java pluginsobject-src 'none' (always block)
frame-srcIframesframe-src 'self'
frame-ancestorsWho can embed your appframe-ancestors 'none'

Special Keywords

  • 'self' - Same origin (your app's localhost server)
  • 'unsafe-inline' - Allow inline scripts/styles (needed for many game engines)
  • 'unsafe-eval' - Allow eval() and similar functions
  • data: - Allow data URLs (e.g., data:image/png;base64,...)
  • blob: - Allow blob URLs (dynamic content)
  • https: - Allow any HTTPS source
  • * - Allow everything (use sparingly)

Common CSP Examples

Example 1: Phaser Game with CDN

Your game uses Phaser from a CDN and doesn't call external APIs:

Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; connect-src 'self'; object-src 'none'; frame-ancestors 'none';

Example 2: Unity WebGL Game

Unity WebGL games need permissive settings for inline scripts and eval:

Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; connect-src 'self'; object-src 'none'; frame-ancestors 'none';

Example 3: Game with Google Fonts and External API

Your game uses Google Fonts and calls your API:

Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: blob:; connect-src 'self' https://api.yourapp.com; object-src 'none'; frame-ancestors 'none';

Troubleshooting CSP Issues

Issue 1: CSP Blocks External Script

Error in console:

Refused to load script from 'https://cdn.example.com/lib.js' because it violates
the Content Security Policy directive: "script-src 'self'".

Solution: Add the CDN domain to script-src:

script-src 'self' 'unsafe-inline' https://cdn.example.com

Or allow all HTTPS scripts:

script-src 'self' 'unsafe-inline' https:

Issue 2: Inline Scripts Blocked

Error in console:

Refused to execute inline script because it violates the Content Security Policy
directive: "script-src 'self'".

Solution: Add 'unsafe-inline' to script-src:

script-src 'self' 'unsafe-inline'

Issue 3: eval() Blocked

Error in console:

Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed
source of script.

Solution: Add 'unsafe-eval' to script-src:

script-src 'self' 'unsafe-inline' 'unsafe-eval'

Issue 4: Font Loading Blocked

Error in console:

Refused to load font from 'https://fonts.gstatic.com/...' because it violates
the Content Security Policy directive: "font-src 'self'".

Solution: Add the font domain to font-src:

font-src 'self' https://fonts.gstatic.com

Issue 5: API Call Blocked

Error in console:

Refused to connect to 'https://api.yourapp.com' because it violates the Content
Security Policy directive: "connect-src 'self'".

Solution: Add your API domain to connect-src:

connect-src 'self' https://api.yourapp.com

Or allow all HTTPS APIs:

connect-src 'self' https:

Testing Your CSP Configuration

Step 1: Check Browser Console

Open your mobile app and check the browser console for CSP violations. Any blocked resources will show errors like:

Refused to load ... because it violates the Content Security Policy directive ...

Step 2: Test External Resources

If your app loads external resources, verify they load correctly:

  • ✅ External scripts execute
  • ✅ External stylesheets apply
  • ✅ External fonts render
  • ✅ External images display
  • ✅ API calls succeed

Step 3: Verify Security

Ensure your CSP isn't too permissive:

  • ❌ Flash/Java plugins are blocked (object-src 'none')
  • ❌ Your app can't be embedded in iframes (frame-ancestors 'none')
  • ✅ Only trusted domains are allowed

Best Practices

Start Permissive, Then Restrict

  1. Development: Start with the permissive default CSP to ensure everything works
  2. Testing: Monitor the console for what resources your app actually loads
  3. Production: Create a specific CSP that allows only necessary sources

Never Allow Everything in Production

Avoid using * in production CSP. Instead:

  • ✅ Use https: to allow all HTTPS sources
  • ✅ List specific trusted domains
  • ❌ Don't use * (allows HTTP, which is insecure)

Always Block Plugins

Always include object-src 'none' to block Flash, Java, and other dangerous plugins:

object-src 'none';

Prevent Clickjacking

Always include frame-ancestors 'none' to prevent your app from being embedded in malicious iframes:

frame-ancestors 'none';

Document Your External Dependencies

Keep a list of all external resources your app uses:

  • CDN libraries (jsdelivr, unpkg, cdnjs)
  • Font providers (Google Fonts, Adobe Fonts)
  • API endpoints (your backend servers)
  • Analytics (Google Analytics, etc.)

This helps you create an accurate CSP that allows only necessary resources.

Quick Decision Guide

Your App Uses...Recommended CSP
No external resourcesStrict CSP ('self' only)
CDN libraries onlyAllow https: for scripts
External API callsAllow https: for connect-src
Google FontsAllow fonts.googleapis.com and fonts.gstatic.com
Multiple CDNsAllow https: or list specific domains
WebAssembly/SharedArrayBufferAdd COOP/COEP headers
Game engine (Unity, Phaser)Allow 'unsafe-inline' and 'unsafe-eval'

Summary

Default configuration (already set):

  • ✅ No caching (fast local access)
  • ✅ Security headers (XSS, clickjacking protection)
  • ✅ Permissive CSP (allows external resources, blocks plugins)

You only need to customize if:

  • You want stricter security (self-contained app)
  • You need specific domain whitelisting
  • You use WebAssembly/SharedArrayBuffer features

Remember:

  • CSP controls what your app can load
  • CORS controls what your API can receive
  • Configure CSP in the SaaS dashboard
  • Configure CORS on your API server

Next Steps