Skip to content

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:

  1. Faster iteration - No need to deploy to see violations
  2. Immediate feedback - See violations as you code
  3. Save deployment cycles - Fix issues before pushing
  4. Better debugging - Full access to source code and dev tools
  5. 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 → InspectConsole 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:

# Node.js
npm run dev

# Python Flask
python app.py

# Python Streamlit
streamlit run app.py

Step 3: Check for CSP Violations

Open your application in the browser with the console open.

✅ No CSP violations:

Console is clear - no red error messages

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)

npm install helmet
// 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:

  1. Start your server: node server.js
  2. Open http://localhost:3000
  3. Open browser console (F12)
  4. 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:

  1. Start dev server: npm run dev
  2. Open http://localhost:3000 (or your configured port)
  3. Open browser console
  4. 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:

  1. Start Flask: python app.py
  2. Open http://localhost:5000
  3. Open browser console
  4. 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:

  1. Start FastAPI: python main.py
  2. Open http://localhost:8000
  3. Open browser console
  4. 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:

  1. Start Next.js: npm run dev
  2. Open http://localhost:3000
  3. Open browser console
  4. 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

airbase container build

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:

docker-compose up

Step 3: Test in Browser

  1. Open http://localhost:3000
  2. Open browser console (F12)
  3. Check for CSP violations
  4. 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.

1. CSP Evaluator (Chrome)

How to use:

  1. Install extension
  2. Visit your local dev site
  3. Click extension icon
  4. 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:

❌ Refused to execute inline script
❌ Refused to execute inline event handler

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:

# Download library
curl https://cdn.example.com/library.js > public/js/library.js
<!-- Serve from own domain -->
<script src="/js/library.js"></script>

Scenario 3: Testing eval() Usage

Code with eval():

const code = 'console.log("Hello")';
eval(code);  // Will be blocked

Expected result:

❌ Refused to evaluate a string as JavaScript because 'unsafe-eval'
   is not allowed by CSP

Fix:

// Don't use eval()
console.log("Hello");  // Direct execution

Scenario 4: Testing Dynamic Content

Using innerHTML with scripts:

const container = document.getElementById('container');
container.innerHTML = '<script>alert("XSS")</script>';  // Blocked

Expected result:

❌ Script tags injected via innerHTML won't execute (CSP protection)

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:

(Empty console, no red errors)

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:

  1. ✅ Commit your code
  2. ✅ Deploy to Airbase staging
  3. ✅ 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:

  1. Identify all violations in console
  2. Follow Troubleshoot CSP Violations guide
  3. Fix each violation
  4. Re-test locally
  5. 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

Refused to execute inline event handler because it violates CSP...

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

Refused to evaluate a string as JavaScript because 'unsafe-eval'
is not allowed by CSP

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() or Function() 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:

  1. Hard reload browser (Ctrl+Shift+R / Cmd+Shift+R)
  2. Clear browser cache
  3. Verify server restart after config changes
  4. 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:

nginx -c local-nginx.conf
# Access via http://localhost:8080

Issue: Too many violations to fix

Problem: Hundreds of CSP errors in console

Solution:

  1. Fix one type at a time (start with inline scripts)
  2. Use find/replace for common patterns
  3. 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:

  1. No violations? → Deploy to Airbase staging
  2. Found violations?Troubleshoot CSP Violations
  3. Need workaround?Nginx Proxy Workaround (Python apps only, last resort)

See Also


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!