Implementing Strict Content Security Policy ( CSP ) in Vue.js & Django with Nginx

Saurav SharmaSaurav Sharma
4 min read

Introduction

Content Security Policy (CSP) is a security standard that helps prevent cross-site scripting (XSS), clickjacking, and other code-injection attacks by whitelisting trusted sources of content. When correctly configured, CSP ensures that only approved scripts, styles, images, and connections can be loaded by the browser.

Why CSP Matters

Without a strict CSP, an attacker could inject malicious scripts into your application, steal user credentials, or redirect users to phishing pages:

flowchart LR
  subgraph Attacker
    A1[Craft malicious payload] --> A2[Host exploit page or social-engineer user]
  end

  subgraph Server
    B1[Receives request with payload]
    B2[Writes to DB or reflects it back]
    B1 --> B2
  end

  subgraph VictimBrowser
    C1[Fetch page from Server]
    C1 --> C2[Parses HTML including attacker’s `<script>`]
    C2 --> C3[Executes malicious JS]
    C3 --> C4[Steals cookies or redirects to phishing site]
  end

  A2 --> B1
  B2 --> C1

How CSP Works

When the browser receives HTML along with a CSP header, it enforces each directive on every resource request:

flowchart TD
  1[Start: Browser requests page] --> 2[Receive HTML + CSP header]
  2 --> 3[Parse HTML]
  3 --> 4{Resource Tag?}
  4 -->|Yes| 5[Identify Resource Type]
  5 --> 6[Lookup CSP directive for that type]
  6 --> 7{URL in whitelist?}
  7 -->|Yes| 8[Allow load]
  7 -->|No| 9[Block load & log warning]
  4 -->|No| 10[Continue rendering]
  8 --> 10
  9 --> 10
  10 --> 11[Page finishes rendering]
  11 --> 12[End]

Browser Enforcement Flow

This diagram shows how the browser enforces CSP on resources like scripts, styles, images, and XHR/fetch requests:

flowchart LR
  subgraph Server
    A[Serve index.html] -->|Include CSP header| B((HTTP response))
  end

  subgraph Browser
    B --> C{Read CSP header}
    C --> D[Enforce rules on scripts/styles/images/connect]
    D -->|Allowed| E[Load resource]
    D -->|Blocked| F[Drop resource + warn]
  end

  subgraph Page
    E --> G[App JS runs]
    F --> G
  end

Configuring CSP in Nginx for Vue.js

In your Nginx server block for the Vue.js static build, add a CSP header:

server {
    listen 80;
    server_name yourdomain.com;

    root /var/www/vue-dist;
    index index.html;

    add_header Content-Security-Policy "
      default-src 'self';
      script-src 'self' https://cdn.jsdelivr.net;
      style-src 'self' 'unsafe-inline';
      img-src 'self' data:;
      font-src 'self' https://fonts.gstatic.com;
      connect-src 'self' https://api.yourdomain.com;
    " always;

    location / {
        try_files $uri $uri/ /index.html;
    }
}
  • default-src: Fallback for all resource types.
  • script-src: Whitelist your own bundle and trusted CDNs.
  • style-src: Allow inline styles for runtime libraries like Floating-UI.
  • img-src: Permit data URIs for base64 images.
  • font-src: Load web fonts from trusted providers.
  • connect-src: Ensure the SPA can communicate with the Django API.

Configuring CSP in Django

Use the django-csp middleware or a custom header:

Using django-csp

# settings.py
MIDDLEWARE = [
    # ...
    'csp.middleware.CSPMiddleware',
]

CSP_DEFAULT_SRC = ("'self'",)
CSP_SCRIPT_SRC  = ("'self'", "https://cdn.jsdelivr.net")
CSP_STYLE_SRC   = ("'self'", "'unsafe-inline'")
CSP_IMG_SRC     = ("'self'", "data:")
CSP_CONNECT_SRC = ("'self'", "https://api.yourdomain.com")

Custom Middleware

# middleware.py
from django.utils.deprecation import MiddlewareMixin

class ContentSecurityPolicyMiddleware(MiddlewareMixin):
    def process_response(self, request, response):
        policy = (
            "default-src 'self'; "
            "script-src 'self' https://cdn.jsdelivr.net; "
            "style-src 'self' 'unsafe-inline'; "
            "img-src 'self' data:; "
            "connect-src 'self' https://api.yourdomain.com;"
        )
        response['Content-Security-Policy'] = policy
        return response

Note: If your Django project only serves API endpoints (no HTML responses or templates), you typically don't need to configure CSP on the Django side. Simply enforce CSP at your Nginx or frontend layer.

Inline Script Handling

When an attacker tries to inject inline <script> tags via URL parameters or form inputs, CSP will block execution:

flowchart LR
  A[Victim visits /page?msg=PAYLOAD] --> B[Browser requests HTML + CSP header]
  B --> C[Browser parses HTML]
  C --> D[Finds inline `<script>` from PAYLOAD]
  D --> E{script-src allows inline?}
  E -->|No| F[Block execution + throw console error]
  E -->|Yes| G[Would execute script]
  F --> H[User safe]

Testing & Troubleshooting

  1. Check the browser console for “Refused to load” errors.
  2. Whitelisting: Add missing domains in the right directive (e.g., connect-src vs. script-src).
  3. Certificate permissions: Ensure Nginx can read SSL cert files if using HTTPS.
  4. Disable Rocket Loader (Cloudflare) if you must avoid 'unsafe-inline' for scripts.

Conclusion

Implementing a strict Content Security Policy via Nginx and Django dramatically reduces the risk of XSS and resource injection attacks. By carefully whitelisting only trusted sources and monitoring violations, you harden both your Vue.js frontend and Django backend against a wide range of threats.

0
Subscribe to my newsletter

Read articles from Saurav Sharma directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Saurav Sharma
Saurav Sharma

I am a Self Taught Backend developer With 3 Years of Experience. Currently, I am working at a tech Startup based in The Bahamas. Here are my skills so far - 💪Expert at - 🔹Python 🔹Django 🔹Django REST framework 🔹Celery ( for distributed tasks ) 🔹ORM ( Know how to write fast queries & design models ) 🔹Django 3rd party packages along with postgresQL and mysql as Databases. 🔹Cache using Redis & Memcache 🔹Numpy + OpenCV for Image Processing 🔹ElasticSearch + HayStack 🔹Linux ( Debian ) 😎 Working Knowledge - Html, CSS, JavaScript, Ajax, Jquery, Git ( GitHub & BitBucket ), Basic React & React Native, Linux ( Arch ), MongoDB, VPS 🤠 Currently Learning - 🔹More Deep Dive into Django 🔹Docker 🔹Making APIs more Robust 🔹NeoVim 🔹System Design ☺️ Will Be Learn in upcoming months - 🔹GraphQL 🔹 Rust language Other than above, there is not a single technology ever exists that i can't master if needed.