Test CSP Compliance Locally¶
Learn how to test your application for Content Security Policy (CSP) compliance before deploying to Airbase.
Test Early, Deploy Faster
Testing CSP compliance locally saves time and deployment cycles. Catch violations early in development instead of discovering them in production.
What You'll Learn¶
- How to use browser developer tools to identify CSP violations
- How to add CSP headers to your local development server
- How to test with Docker locally
- How to interpret CSP violation reports
- Common testing scenarios
Why Test Locally?¶
Benefits of local CSP testing:
- Faster iteration - No need to deploy to see violations
- Immediate feedback - See violations as you code
- Save deployment cycles - Fix issues before pushing
- Better debugging - Full access to source code and dev tools
- Confidence - Deploy knowing your app is CSP-compliant
When to test:
- ✅ During development (continuously)
- ✅ Before committing code
- ✅ Before creating pull requests
- ✅ Before deploying to staging/production
Method 1: Browser Developer Tools¶
The simplest way to test CSP compliance is using your browser's built-in developer tools.
Step 1: Open Developer Console¶
Chrome/Edge: - Press F12 or Ctrl+Shift+I (Windows) - Press Cmd+Option+I (Mac) - Or right-click → Inspect → Console tab
Firefox: - Press F12 or Ctrl+Shift+K (Windows) - Press Cmd+Option+K (Mac)
Safari: - Enable Developer menu: Preferences → Advanced → Show Develop menu - Press Cmd+Option+C
Step 2: Run Your Application¶
Start your local development server:
Step 3: Check for CSP Violations¶
Open your application in the browser with the console open.
✅ No CSP violations:
Your app is likely CSP-compliant!
❌ CSP violations detected:
Refused to execute inline script because it violates the following
Content Security Policy directive: 'script-src 'self''.
You have work to do! See Troubleshoot CSP Violations.
Limitations of This Method¶
This approach only shows CSP violations if your dev server already sends CSP headers. Most development servers don't send CSP headers by default.
Result: Your app might work locally but fail on Airbase.
Solution: Use Method 2 to add CSP headers to your local dev server.
Method 2: Add CSP Headers to Local Dev Server¶
The most effective way to test CSP compliance is to add CSP headers to your local development server that match Airbase's policy.
Airbase CSP Policy¶
Airbase enforces this CSP policy:
default-src 'self';
script-src 'self';
style-src 'self';
img-src 'self' data:;
font-src 'self';
connect-src 'self';
frame-ancestors 'none';
base-uri 'self';
form-action 'self';
Key restriction: Only 'self' scripts are allowed (no inline scripts, no eval()).
For Node.js / Express Applications¶
Add CSP headers using middleware:
Option A: Using helmet package (Recommended)
// server.js or app.js
const express = require('express');
const helmet = require('helmet');
const app = express();
// Add CSP headers matching Airbase policy
app.use(
helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"],
styleSrc: ["'self'"],
imgSrc: ["'self'", "data:"],
fontSrc: ["'self'"],
connectSrc: ["'self'"],
frameAncestors: ["'none'"],
baseUri: ["'self'"],
formAction: ["'self'"]
}
})
);
// Your routes
app.get('/', (req, res) => {
res.send('Hello World');
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
Option B: Manual middleware
const express = require('express');
const app = express();
// Add CSP header manually
app.use((req, res, next) => {
res.setHeader(
'Content-Security-Policy',
"default-src 'self'; " +
"script-src 'self'; " +
"style-src 'self'; " +
"img-src 'self' data:; " +
"font-src 'self'; " +
"connect-src 'self'; " +
"frame-ancestors 'none'; " +
"base-uri 'self'; " +
"form-action 'self'"
);
next();
});
// Your routes
app.get('/', (req, res) => {
res.send('Hello World');
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
Test it:
- Start your server:
node server.js - Open http://localhost:3000
- Open browser console (F12)
- Look for CSP violation errors
For Vite / React / Vue Applications¶
Add CSP headers in your Vite dev server configuration.
vite.config.ts:
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
server: {
port: 3000,
headers: {
'Content-Security-Policy': [
"default-src 'self'",
"script-src 'self'",
"style-src 'self'",
"img-src 'self' data:",
"font-src 'self'",
"connect-src 'self'",
"frame-ancestors 'none'",
"base-uri 'self'",
"form-action 'self'"
].join('; ')
}
}
});
For Vue:
// vite.config.js
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
plugins: [vue()],
server: {
headers: {
'Content-Security-Policy': "default-src 'self'; script-src 'self'; style-src 'self'"
}
}
});
Test it:
- Start dev server:
npm run dev - Open http://localhost:3000 (or your configured port)
- Open browser console
- Check for CSP violations
For Python Flask Applications¶
Add CSP headers using the @after_request decorator.
app.py:
from flask import Flask, make_response
app = Flask(__name__)
CSP_POLICY = (
"default-src 'self'; "
"script-src 'self'; "
"style-src 'self'; "
"img-src 'self' data:; "
"font-src 'self'; "
"connect-src 'self'; "
"frame-ancestors 'none'; "
"base-uri 'self'; "
"form-action 'self'"
)
@app.after_request
def add_csp_header(response):
response.headers['Content-Security-Policy'] = CSP_POLICY
return response
@app.route('/')
def index():
return '<h1>Hello World</h1>'
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)
Test it:
- Start Flask:
python app.py - Open http://localhost:5000
- Open browser console
- Check for violations
For Python FastAPI Applications¶
Add CSP headers using middleware.
main.py:
from fastapi import FastAPI
from fastapi.middleware.trustedhost import TrustedHostMiddleware
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
app = FastAPI()
CSP_POLICY = (
"default-src 'self'; "
"script-src 'self'; "
"style-src 'self'; "
"img-src 'self' data:; "
"font-src 'self'; "
"connect-src 'self'; "
"frame-ancestors 'none'; "
"base-uri 'self'; "
"form-action 'self'"
)
class CSPMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
response = await call_next(request)
response.headers['Content-Security-Policy'] = CSP_POLICY
return response
app.add_middleware(CSPMiddleware)
@app.get('/')
async def root():
return {"message": "Hello World"}
if __name__ == '__main__':
import uvicorn
uvicorn.run(app, host='0.0.0.0', port=8000)
Test it:
- Start FastAPI:
python main.py - Open http://localhost:8000
- Open browser console
- Check for violations
For Next.js Applications¶
Add CSP headers in your Next.js configuration.
next.config.js:
/** @type {import('next').NextConfig} */
const nextConfig = {
async headers() {
return [
{
source: '/:path*',
headers: [
{
key: 'Content-Security-Policy',
value: [
"default-src 'self'",
"script-src 'self'",
"style-src 'self'",
"img-src 'self' data:",
"font-src 'self'",
"connect-src 'self'",
"frame-ancestors 'none'",
"base-uri 'self'",
"form-action 'self'"
].join('; ')
}
]
}
];
}
};
module.exports = nextConfig;
Test it:
- Start Next.js:
npm run dev - Open http://localhost:3000
- Open browser console
- Check for violations
Method 3: Test with Docker Locally¶
The most accurate way to test CSP compliance is to build and run your Docker container locally, simulating the Airbase environment.
Step 1: Build Your Container¶
This builds your container image using your Dockerfile.
Step 2: Run Container Locally¶
Option A: Using Docker directly
# Find the image name from build output
docker images | grep local.airbase.sg
# Run the container
docker run -p 3000:3000 -e PORT=3000 local.airbase.sg/your-app:tag
Option B: Using docker-compose
Create docker-compose.yml:
version: '3.8'
services:
app:
image: local.airbase.sg/your-app:latest
ports:
- "3000:3000"
environment:
- PORT=3000
Run:
Step 3: Test in Browser¶
- Open http://localhost:3000
- Open browser console (F12)
- Check for CSP violations
- Test all application features
Step 4: Clean Up¶
Stop and remove container:
# If using docker run
docker ps # Find container ID
docker stop <container-id>
docker rm <container-id>
# If using docker-compose
docker-compose down
Advantages of Docker Testing¶
- ✅ Exact environment - Matches Airbase deployment
- ✅ Includes build process - Catches build-time issues
- ✅ Tests production build - Not development mode
- ✅ Verifies CSP headers - As set by Airbase
Method 4: Browser Extensions¶
Browser extensions can help visualize and debug CSP policies.
Recommended Extensions¶
1. CSP Evaluator (Chrome)
- Install: Chrome Web Store - CSP Evaluator
- Shows CSP policy for current page
- Highlights potential security issues
How to use:
- Install extension
- Visit your local dev site
- Click extension icon
- Review CSP policy and findings
2. HTTP Header Live (Chrome/Firefox)
- Shows all HTTP headers including CSP
- Real-time header monitoring
3. Developer Tools Built-in
Modern browsers show CSP violations in the Console. No extension needed!
Chrome DevTools:
- Console tab shows CSP violations in red
- Network tab → Headers shows CSP policy
Firefox Developer Tools:
- Console tab shows CSP violations
- Detailed CSP error messages
Common Test Scenarios¶
Scenario 1: Testing Inline Scripts¶
Create test HTML with inline script:
<!DOCTYPE html>
<html>
<head>
<title>CSP Test</title>
</head>
<body>
<h1>CSP Test Page</h1>
<button onclick="alert('Click!')">Click Me</button>
<script>
console.log('Inline script');
</script>
</body>
</html>
Expected result with CSP:
Fix:
<!DOCTYPE html>
<html>
<head>
<title>CSP Test</title>
</head>
<body>
<h1>CSP Test Page</h1>
<button id="myButton">Click Me</button>
<script src="/js/main.js"></script>
</body>
</html>
// /js/main.js
console.log('External script - OK!');
document.getElementById('myButton').addEventListener('click', () => {
alert('Click!');
});
Scenario 2: Testing External Resources¶
Test loading external scripts/styles:
<!DOCTYPE html>
<html>
<head>
<title>External Resources Test</title>
<!-- CDN script - will be blocked by CSP -->
<script src="https://cdn.example.com/library.js"></script>
</head>
<body>
<h1>External Resources</h1>
</body>
</html>
Expected result with CSP:
❌ Refused to load script from 'https://cdn.example.com/library.js'
because it violates CSP directive: 'script-src 'self''
Fix: Download and serve library from your own domain:
Scenario 3: Testing eval() Usage¶
Code with eval():
Expected result:
Fix:
Scenario 4: Testing Dynamic Content¶
Using innerHTML with scripts:
const container = document.getElementById('container');
container.innerHTML = '<script>alert("XSS")</script>'; // Blocked
Expected result:
Safe alternative:
const container = document.getElementById('container');
const p = document.createElement('p');
p.textContent = 'Safe content';
container.appendChild(p);
Interpreting Test Results¶
✅ Success: No Violations¶
Console output:
What it means:
- Your application is CSP-compliant
- Safe to deploy to Airbase
- All JavaScript is in external files
- No inline event handlers
- No eval() usage
Next steps:
- ✅ Commit your code
- ✅ Deploy to Airbase staging
- ✅ Final verification in staging
⚠️ Warning: CSP Violations Detected¶
Console output:
❌ Refused to execute inline script because it violates CSP...
❌ Refused to load script from 'https://cdn.example.com/...'
What it means:
- Your application has CSP violations
- Will not work correctly on Airbase
- Need to fix violations before deploying
Next steps:
- Identify all violations in console
- Follow Troubleshoot CSP Violations guide
- Fix each violation
- Re-test locally
- Deploy only after all violations are fixed
🔍 Detailed Error Analysis¶
Error Type 1: Inline Script
Refused to execute inline script because it violates the following
Content Security Policy directive: 'script-src 'self''.
Fix: Move script to external .js file
Error Type 2: Inline Event Handler
Fix: Use addEventListener() in external JS file
Error Type 3: External Resource
Refused to load the script 'https://cdn.example.com/...' because
it violates CSP directive 'script-src 'self''
Fix: Download and serve from your domain, or use npm package
Error Type 4: eval() Usage
Fix: Rewrite code without eval(), setTimeout(string), or new Function()
Testing Checklist¶
Use this checklist to ensure thorough CSP testing:
Before First Deployment¶
- Browser console shows no CSP errors
- All pages tested (not just homepage)
- All interactive features work
- Forms submit correctly
- API calls work
- No inline
<script>tags - No inline event handlers (
onclick,onload, etc.) - No
eval()orFunction()usage - All JavaScript in external files
- All external resources served from same origin
Before Each Deployment¶
- Added CSP headers to local dev server
- Tested in browser with console open
- No new CSP violations introduced
- Tested with Docker container locally (recommended)
- All new features work with CSP
Optional: Advanced Testing¶
- Tested in multiple browsers (Chrome, Firefox, Safari)
- Tested on mobile devices
- Load tested with realistic data
- Security review completed
Troubleshooting Local Testing¶
Issue: CSP headers not appearing¶
Problem: Headers added but not showing in browser
Solution:
- Hard reload browser (Ctrl+Shift+R / Cmd+Shift+R)
- Clear browser cache
- Verify server restart after config changes
- Check browser Network tab → Headers for CSP header
Issue: Dev server doesn't support custom headers¶
Problem: Framework doesn't allow header customization
Solution:
Use reverse proxy (nginx) locally:
# local-nginx.conf
server {
listen 8080;
location / {
proxy_pass http://localhost:3000;
add_header Content-Security-Policy "default-src 'self'; script-src 'self'";
}
}
Run nginx locally:
Issue: Too many violations to fix¶
Problem: Hundreds of CSP errors in console
Solution:
- Fix one type at a time (start with inline scripts)
- Use find/replace for common patterns
- Consider nginx workaround for Python apps (last resort)
Best Practices¶
✅ Do¶
- Test early and often during development
- Add CSP headers to local dev server for realistic testing
- Test all pages and features, not just main page
- Keep console open while developing
- Test with Docker locally before deploying
- Document any CSP-related decisions in code comments
❌ Don't¶
- Don't skip local testing - it saves deployment time
- Don't test only in dev mode - test production builds
- Don't ignore CSP warnings - they become errors on Airbase
- Don't assume it works - verify in browser console
- Don't deploy without testing - fix violations first
Next Steps¶
After testing locally:
- No violations? → Deploy to Airbase staging
- Found violations? → Troubleshoot CSP Violations
- Need workaround? → Nginx Proxy Workaround (Python apps only, last resort)
See Also¶
- How-To: Write CSP-Compliant Code - CSP best practices
- How-To: Troubleshoot CSP Violations - Fix CSP errors
- How-To: Nginx Proxy Workaround - Last resort for Python apps
- Reference: Content Security Policy - Complete CSP specification
- Tutorial: Getting Started - Deploy your first app
- How-To: Build and Deploy - Deployment workflow
Summary¶
Key takeaways:
- ✅ Always test CSP compliance locally before deploying
- ✅ Add CSP headers to your local dev server for accurate testing
- ✅ Use browser console to identify violations
- ✅ Test with Docker for production-like environment
- ✅ Fix all violations before deploying to Airbase
Testing saves time:
- 10 minutes local testing > 1 hour deployment debugging
- Catch violations early in development
- Deploy with confidence
Remember: CSP violations that work locally will fail on Airbase. Always test with CSP headers enabled!