---
title: "Django Security: Properly Configuring CSRF, CORS, and CSP"
description: "Understand and properly configure CSRF, CORS, and CSP in Django: Key differences, best practices for Django 6.0 & REST APIs, and common pitfalls to avoid."
canonical_url: "https://www.blueshoe.io/blog/django-security-csrf/"
---

> Index of every page on this site: [llms.txt](https://www.blueshoe.io/llms.txt)

Anyone who has encountered a CORS error in the browser knows the knee-jerk reflex: temporarily slap on `Access-Control-Allow-Origin: *`, refresh the tab, and move on. The problem? You rarely resolve the root issue; you merely relocate a critical security boundary. In modern Django projects, **CSRF**, **CORS**, and **CSP** are frequently lumped together - even though they target fundamentally different threats in the web security ecosystem. In this guide, you will learn how each mechanism works, how they interlock, and how to configure them for production in Django 6.0, modern SPAs, and REST APIs.

![Django Security: Properly Configuring CSRF, CORS, and CSP](https://www.blueshoe.io/img/blogs/cors.webp)

<global-blog-level-info>

- [Django Fundamentals](https://docs.djangoproject.com/en/stable/) (`settings.py`, views, [middleware architecture](https://docs.djangoproject.com/en/stable/topics/http/middleware/))
- HTTP Basics (request/response headers, cookies such as `SameSite` & `HttpOnly`, methods like `POST` & `OPTIONS`)
- [Same-Origin Policy (SOP)](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy) in modern browsers
- Basics of modern web architectures (REST APIs, SPAs with [Vue.js / Nuxt](https://www.blueshoe.io/blog/web-development-with-vuejs-and-nuxt/) or React)
- Core web security concepts ([OWASP Top 10](https://owasp.org/www-project-top-ten/), especially [CSRF](https://owasp.org/www-community/attacks/csrf) and [XSS](https://owasp.org/www-community/attacks/xss/))

If you have any questions or if anything is unclear, feel free to use the comment section below the article.

</global-blog-level-info>

Django ships with exceptional out-of-the-box security defaults. As an [experienced Python and Django agency](https://www.blueshoe.io/technologies/python-django-agency/), our security reviews repeatedly reveal the same pattern: the real vulnerability almost never lives within the framework itself, but in its configuration. Classic missteps include prematurely applied `@csrf_exempt` decorators, overly permissive CORS permissions, or Content Security Policies (CSP) effectively neutralized by dozens of `unsafe-inline` exceptions.

Let's demystify these three pillars and cleanly separate their responsibilities.

---

## TL;DR: CSRF vs. CORS vs. CSP at a Glance

For developers and architects in a hurry, here is the essential distinction in three sentences:

> - **CSRF (Cross-Site Request Forgery):** Protects your application against *unintended actions* executed by a malicious site on behalf of an authenticated user (**Write Protection**).
> - **CORS (Cross-Origin Resource Sharing):** Dictates as a browser mechanism whether client-side JavaScript from another origin is permitted to read *responses* from your server (**Read Protection**).
> - **CSP (Content Security Policy):** Restricts as an HTTP response header which *executable assets and resources* (scripts, stylesheets, images, frames, connections) the browser is allowed to load and execute (**Execution Protection / XSS Mitigation**).

Separating these three concepts prevents dangerous misconfigurations, cuts down troubleshooting time, and significantly reduces security tickets during [agile sprints](https://www.blueshoe.io/blog/project-start-with-agile-backlog/).

---

## Current State for Django Projects (Django 6.0, 5.2 LTS & 4.2)

Before diving into configurations, let's review the current version milestones across the Django ecosystem:

1. **Django 6.0** is the modern stable release and brings a long-awaited milestone: **built-in Content Security Policy (CSP)** directly inside the core framework (`django.middleware.csp.ContentSecurityPolicyMiddleware`).
2. **Django 5.2 LTS** is the designated Long-Term Support release with enterprise security support extending into 2028.
3. **Django 4.2 LTS** reached its official End-of-Life in **April 2026**. Existing production deployments must be migrated to an active release without delay.
4. **No mandatory third-party package needed for CSP in Django 6.0+:** While older versions rely on `django-csp`, greenfield 6.0 setups should leverage the framework's native capabilities.

Many tutorials across the web still exclusively reference `django-csp`. While completely valid for existing 5.2 LTS codebases, new Django 6.0 architectures should prioritize the built-in middleware.

---

## Why CSRF, CORS, and CSP Are Frequently Confused in Practice

In modern distributed web architectures, these topics rarely appear in isolation. A standard enterprise stack typically encompasses:

- A Django backend serving the ORM, business logic, and admin panel.
- A decoupled Single-Page Application (SPA) frontend (built with React or [Vue.js & Nuxt](https://www.blueshoe.io/blog/web-development-with-vuejs-and-nuxt/)) hosted on a separate subdomain or CDN origin.
- A [REST API powered by Django REST Framework (DRF)](https://www.blueshoe.io/our-services/api-development-company/) with session authentication for internal staff and bearer tokens for third-party consumers.
- Third-party integrations for monitoring, analytics, and hosted fonts.
- A hardened Django admin interface that must remain strictly secured without disrupting daily editorial operations.

When multiple security boundaries intersect, everything seems connected. However, you can clarify almost any review by asking **three diagnostic questions**:

1. **Is the browser being coerced into executing a state-changing write request on behalf of an active session?**<br />


→ That is a **CSRF** issue.
2. **Is client-side JavaScript on Origin A attempting to read data returned by Origin B?**<br />


→ That is a **CORS** issue.
3. **Is the browser authorized to fetch, parse, or execute this particular script, inline block, or external connection?**<br />


→ That is a **CSP** issue.

Asking these three questions resolves more than 80% of cross-origin confusion during architectural reviews.

---

## CSRF in Django: Protecting Against Forged Requests in Active Sessions

**Cross-Site Request Forgery (CSRF)** exploits the browser's implicit trust in stored session cookies.

### The Attack Vector

The attack model is straightforward: A user logs into your Django web application and receives a session cookie. Without logging out, they open another tab and visit a malicious website, `evil.com`. That site contains a hidden form or background script targeting `https://your-app.com/api/account/delete/`.

Because web browsers **automatically attach ambient session cookies** to cross-origin requests targeting `your-app.com`, your server would treat the forged request as genuine and execute the account deletion if no secondary defense were in place.

### How Django and CsrfViewMiddleware Defend Requests

Django includes one of the industry's most robust CSRF defenses in `CsrfViewMiddleware`. This middleware intercepts all non-safe HTTP methods (`POST`, `PUT`, `PATCH`, `DELETE`) and verifies that a valid, masked CSRF secret accompanies the request.

In standard template-driven views, this is handled via the form tag:

```html
<form method="post" action="/profile/save/">
  {% csrf_token %}
  <!-- Form fields -->
  <button type="submit">Save Profile</button>
</form>
```

Django checks the token submitted in the form against the value stored in the `csrftoken` cookie (using a masked double-submit cookie pattern). Because the browser's Same-Origin Policy prevents `evil.com` from **reading** your site's DOM or cookies, the attacker cannot forge the token.

### Origin Verification and `CSRF_TRUSTED_ORIGINS` Since Django 4.0

Since version 4.0, Django strictly evaluates the HTTP `Origin` (and `Referer`) header on unsafe requests. When write requests originate from a different origin, Django demands that the origin be explicitly whitelisted in `CSRF_TRUSTED_ORIGINS`.

> **Crucial Rule:** Since Django 4.0, all entries must include the explicit URL scheme (`https://` or `http://`):

```python
# settings.py

CSRF_TRUSTED_ORIGINS = [
    "https://app.example.com",
    "https://admin.example.com",
    "https://*.example.com",  # Wildcard subdomains supported since 4.0
]

CSRF_COOKIE_SECURE = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SAMESITE = "Lax"
```

If your Django application sits behind a reverse proxy or Kubernetes ingress controller (such as NGINX or Traefik), you must configure `SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")`. Otherwise, Django perceives incoming HTTPS requests as unencrypted HTTP and aborts CSRF verification.

### AJAX and Fetch: Sending the `X-CSRFToken` Header

In decoupled SPAs or asynchronous client components within the same origin, read the CSRF token from the browser cookie and pass it through the `X-CSRFToken` HTTP header:

```javascript
// Extract csrftoken cookie value
function getCookie(name) {
  let cookieValue = null;
  if (document.cookie && document.cookie !== "") {
    const cookies = document.cookie.split(";");
    for (let i = 0; i < cookies.length; i++) {
      const cookie = cookies[i].trim();
      if (cookie.substring(0, name.length + 1) === name + "=") {
        cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
        break;
      }
    }
  }
  return cookieValue;
}

const csrftoken = getCookie("csrftoken");

await fetch("/api/v1/profile/", {
  method: "PATCH",
  headers: {
    "Content-Type": "application/json",
    "X-CSRFToken": csrftoken,
  },
  body: JSON.stringify({ bio: "Full-Stack Engineer" }),
});
```

### Django REST Framework (DRF): SessionAuth vs. TokenAuth

Django REST Framework creates frequent confusion regarding CSRF enforcement:

- **When using SessionAuthentication:** Djangos CSRF checks apply fully. If you rely on session cookies for browser clients, you **must** supply the CSRF token on write operations.
- **When using TokenAuthentication or JWT (Bearer Tokens):** If clients authenticate exclusively through `Authorization: Bearer <Token>` headers and no ambient session cookies exist in the browser context, CSRF attacks are fundamentally impossible.

Blindly applying `@csrf_exempt` across API viewsets is a dangerous anti-pattern. If you are not utilizing session cookies, configure DRF's `authentication_classes` appropriately rather than disabling framework-level security checks.

---

## CORS in Django: The Browser Bouncer for Cross-Origin Requests

**Cross-Origin Resource Sharing (CORS)** is a standardized browser mechanism that provides controlled exceptions to the Same-Origin Policy (SOP).

A frequent misconception is assuming CORS acts as a server firewall. It does not:

- `curl` ignores CORS entirely.
- Server-to-server HTTP calls ignore CORS entirely.
- Native mobile apps and Python scripts ignore CORS entirely.

CORS answers one single question enforced by web browsers:<br />

**Is client-side JavaScript running on Origin A permitted to read the response payload delivered by Origin B?**

For deep-dive scenarios regarding Docker and proxy setups, consult our dedicated guide on [Django CORS in Production](https://www.blueshoe.io/blog/django-cors-in-production/).

### What Defines an Origin?

Two URLs share the same origin only if their **protocol**, **domain**, and **port** match identically:

- `http://localhost:3000` (Local Frontend Dev Server)
- `http://localhost:8000` (Django API Backend)

Because the ports differ, any `fetch()` call between them constitutes a cross-origin request. The browser will block JavaScript from inspecting the response body unless Django returns the appropriate CORS access control headers.

### Preflight Requests (`OPTIONS`)

For requests carrying non-simple headers or content types (such as `application/json`), the browser automatically sends an HTTP `OPTIONS` preflight request first. Only when the server validates the preflight by returning compatible `Access-Control-Allow-*` headers will the browser proceed with the actual `GET`, `POST`, or `PUT` call.

### Clean Setup with `django-cors-headers`

In the Django ecosystem, `django-cors-headers` remains the industry standard:

```bash
pip install django-cors-headers
```

Ensure the middleware is placed at the very top of your middleware pipeline:

```python
# settings.py

INSTALLED_APPS = [
    # ...
    "corsheaders",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    # ...
]

MIDDLEWARE = [
    "corsheaders.middleware.CorsMiddleware",  # Must be placed at the top!
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
    # ...
]

# Explicitly whitelist client origins:
CORS_ALLOWED_ORIGINS = [
    "https://app.example.com",
    "https://client.example.com",
]

# Allow credentials (cookies, auth headers)
CORS_ALLOW_CREDENTIALS = True
```

### Why `CORS_ALLOW_ALL_ORIGINS = True` Is a Production Red Flag

Setting `CORS_ALLOW_ALL_ORIGINS = True` (`Access-Control-Allow-Origin: *`) exposes your endpoints to any website on the internet, allowing rogue scripts to read sensitive API data.

Furthermore, if your API utilizes credentials (`CORS_ALLOW_CREDENTIALS = True`), browser specifications strictly disallow the wildcard `*`. You **must** define explicit domains using `CORS_ALLOWED_ORIGINS`.

### The Interaction: `CORS_ALLOWED_ORIGINS` vs. `CSRF_TRUSTED_ORIGINS`

When your SPA lives on `https://app.example.com` and communicates with your Django backend on `https://api.example.com` using session cookies, both settings must be configured together:

```python
# Grants the browser permission to read API responses:
CORS_ALLOWED_ORIGINS = [
    "https://app.example.com",
]

# Instructs Django to accept cross-origin POST requests in CSRF verification:
CSRF_TRUSTED_ORIGINS = [
    "https://app.example.com",
]
```

- **CORS_ALLOWED_ORIGINS** instructs the *browser*: allow JavaScript to parse responses.
- **CSRF_TRUSTED_ORIGINS** instructs *Django*: accept state-changing HTTP requests from this cross-origin sender.

---

## CSP in Django: Content Security Policy as Effective XSS Mitigation

When mitigating **Cross-Site Scripting (XSS)** and code injection, **Content Security Policy (CSP)** represents the browser's most formidable defense.

Even with Django's automatic template escaping, user-generated rich-text, careless `mark_safe()` invocations, compromised npm packages, or third-party widgets can introduce vulnerabilities. CSP serves as your second line of defense (*Defense-in-Depth*).

### The Major Update: Built-in CSP Support in Django 6.0

Django 6.0 introduces native Content Security Policy support directly within the core framework, eliminating the requirement for third-party packages in new projects.

Activate it via the new middleware:

```python
# settings.py
from django.utils.csp import CSP

MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "django.middleware.csp.ContentSecurityPolicyMiddleware",  # Built into Django 6.0!
    # ...
]

SECURE_CSP = {
    "default-src": [CSP.SELF],
    "script-src": [CSP.SELF, CSP.NONCE],
    "style-src": [CSP.SELF],
    "img-src": [CSP.SELF, "data:", "https:"],
    "font-src": [CSP.SELF, "https:"],
    "connect-src": [CSP.SELF, "https://api.example.com"],
    "object-src": [CSP.NONE],
    "base-uri": [CSP.NONE],
    "frame-ancestors": [CSP.NONE],
    "form-action": [CSP.SELF],
}
```

This baseline immediately stops the browser from loading unauthorized external scripts, neutralizes clickjacking (`frame-ancestors: 'none'`), and blocks rogue form actions.

### Best Practice: Report-Only Mode Before Enforcing

On mature websites, applying a strict CSP immediately can inadvertently break analytics, fonts, or tracking scripts. Always test policies using **Report-Only mode** first:

```python
# settings.py
from django.utils.csp import CSP

# Emits Content-Security-Policy-Report-Only headers
SECURE_CSP_REPORT_ONLY = {
    "default-src": [CSP.SELF],
    "script-src": [CSP.SELF, CSP.NONCE],
    "style-src": [CSP.SELF],
    "img-src": [CSP.SELF, "data:", "https:"],
    "report-uri": "/csp-violations-collector/",
}
```

In this mode, the browser allows all assets to execute normally while sending violation telemetry to your collector endpoint. Once all legitimate assets are identified, migrate to `SECURE_CSP`.

### Securing Inline JavaScript with Nonces and `json_script`

When inline scripts fail under CSP, developers often cave in and add `'unsafe-inline'`. This completely undermines XSS protection.

The robust approach is using cryptographic **Nonces** (numbers used once). Django generates a unique pseudorandom token per HTTP request that must match across both the CSP header and script tags:

Enable the context processor in your settings:

```python
TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "OPTIONS": {
            "context_processors": [
                # ...
                "django.template.context_processors.csp",  # Exposes csp_nonce
            ],
        },
    },
]
```

In your templates:

```html
<!-- Safely serialize structured JSON data without inline script injection -->
{{ app_config|json_script:"app-config-data" }}

<!-- Execute script securely with cryptographic nonce -->
<script nonce="{{ csp_nonce }}">
  const appConfig = JSON.parse(
    document.getElementById("app-config-data").textContent
  );
  console.log("App initialized with nonce protection");
</script>
```

> **Pro Tip:** Always serialize variables into frontend templates using Django's built-in `json_script` filter rather than manual string interpolation. This completely neutralizes script escape vulnerabilities.

### CSP in Django 5.2 LTS and Earlier with `django-csp` 4.0

For projects running on Django 5.2 LTS or Django 4.2, `django-csp` remains the premier tool. Note that `django-csp` 4.0 adopted modern dictionary configuration syntax:

```python
# settings.py (for django-csp 4.0+)
from csp.constants import SELF, NONCE, NONE

MIDDLEWARE = [
    # ...
    "csp.middleware.CSPMiddleware",
]

CONTENT_SECURITY_POLICY = {
    "DIRECTIVES": {
        "default-src": [SELF],
        "script-src": [SELF, NONCE],
        "style-src": [SELF],
        "object-src": [NONE],
        "base-uri": [NONE],
    },
}
```

Legacy settings such as `CSP_DEFAULT_SRC = ("'self'",)` are deprecated in 4.0 and should be refactored.

---

## Architecture Guide: How CSRF, CORS, and CSP Interact in Real Systems

Reviewing common application architectures highlights when and where each security layer applies:

### Scenario 1: Classic Server-Rendered Django Monolith

- **CSRF:** **Mandatory.** Every state-changing form must include `{% csrf_token %}`.
- **CORS:** **Not required.** Because HTML forms and views originate from the identical domain (`same-origin`), CORS never triggers.
- **CSP:** **Highly recommended.** Essential for hardening admin interfaces, user-generated content, and form handling against stored XSS.

### Scenario 2: Decoupled SPA (React / Vue / Nuxt) with Session Cookies

- **CSRF:** **Mandatory.** The client must supply the token in the `X-CSRFToken` request header. `CSRF_TRUSTED_ORIGINS` must include the SPA origin.
- **CORS:** **Mandatory.** `CORS_ALLOWED_ORIGINS` must authorize the frontend origin, and `CORS_ALLOW_CREDENTIALS = True` must be enabled.
- **CSP:** **Mandatory.** Restrictive CSP policies should be served on both the backend API domain and the SPA host.

### Scenario 3: Public REST API with Token or Bearer Authentication

- **CSRF:** **Not required.** Without ambient cookie authentication, CSRF cannot occur.
- **CORS:** **Required**, if web browser applications are expected to consume the API directly via Fetch or XMLHttpRequest.
- **CSP:** Not applicable to pure JSON responses, but recommended on interactive API documentation routes (Swagger UI / ReDoc).

---

## Comparison Table: CSRF vs. CORS vs. CSP at a Glance

Use this matrix during security audits and architectural design sessions:

<table>
<thead>
  <tr>
    <th align="left">
      Criterion
    </th>
    
    <th align="left">
      CSRF (Cross-Site Request Forgery)
    </th>
    
    <th align="left">
      CORS (Cross-Origin Resource Sharing)
    </th>
    
    <th align="left">
      CSP (Content Security Policy)
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td align="left">
      <strong>
        Primary Objective
      </strong>
    </td>
    
    <td align="left">
      Prevents unauthorized actions executed under authenticated user sessions
    </td>
    
    <td align="left">
      Controls cross-domain read access to HTTP responses in client scripts
    </td>
    
    <td align="left">
      Prevents XSS, clickjacking, and malicious code injection in the browser
    </td>
  </tr>
  
  <tr>
    <td align="left">
      <strong>
        Protection Vector
      </strong>
    </td>
    
    <td align="left">
      Inbound (write requests)
    </td>
    
    <td align="left">
      Outbound (read responses)
    </td>
    
    <td align="left">
      Client Execution (browser runtime)
    </td>
  </tr>
  
  <tr>
    <td align="left">
      <strong>
        Enforcing Agent
      </strong>
    </td>
    
    <td align="left">
      <strong>
        Server & Middleware
      </strong>
      
       (validates token & origin)
    </td>
    
    <td align="left">
      <strong>
        Web Browser
      </strong>
      
       (validates Access-Control headers)
    </td>
    
    <td align="left">
      <strong>
        Web Browser
      </strong>
      
       (blocks undeclared resources)
    </td>
  </tr>
  
  <tr>
    <td align="left">
      <strong>
        Typical Attack
      </strong>
    </td>
    
    <td align="left">
      Phishing site triggers funds transfer or profile deletion in active session
    </td>
    
    <td align="left">
      Rogue domain silently reads confidential API data via client-side fetch
    </td>
    
    <td align="left">
      Attacker injects malicious JavaScript (session hijacking, keylogging)
    </td>
  </tr>
  
  <tr>
    <td align="left">
      <strong>
        Relevant Django Tools
      </strong>
    </td>
    
    <td align="left">
      <code>
        CsrfViewMiddleware
      </code>
      
      , <code>
        CSRF_TRUSTED_ORIGINS
      </code>
      
      , <code>
        {% csrf_token %}
      </code>
    </td>
    
    <td align="left">
      Package <code>
        django-cors-headers
      </code>
      
      , <code>
        CORS_ALLOWED_ORIGINS
      </code>
    </td>
    
    <td align="left">
      Django 6.0 <code>
        SECURE_CSP
      </code>
      
       or package <code>
        django-csp
      </code>
    </td>
  </tr>
  
  <tr>
    <td align="left">
      <strong>
        Common Misconception
      </strong>
    </td>
    
    <td align="left">
      <em>
        "My REST API never needs CSRF protection"
      </em>
    </td>
    
    <td align="left">
      <em>
        "CORS protects my server from hacker attacks"
      </em>
    </td>
    
    <td align="left">
      <em>
        "CSP is optional since template escaping is enough"
      </em>
    </td>
  </tr>
</tbody>
</table>

---

## The 5 Most Common Security Pitfalls in Django Projects

### 1. Using `@csrf_exempt` as a Quick Fix

When hitting `403 Forbidden (CSRF verification failed)`, engineers often sprinkle `@csrf_exempt` across views. This is an invitation to exploit your users. The proper fix: verify your authentication architecture, supply the `X-CSRFToken` header, or configure `CSRF_TRUSTED_ORIGINS` accurately.

### 2. Leaving `CORS_ALLOW_ALL_ORIGINS = True` in Production

Developers frequently activate wildcard origins locally and forget to revert them before deployment. In production, only explicit origins should appear in `CORS_ALLOWED_ORIGINS`.

### 3. Enabling CSP and Promptly Giving Up with `unsafe-inline`

When scripts fail, teams often resort to `'unsafe-inline'` and `'unsafe-eval'`. Doing so discards roughly 90% of your XSS protection. Transition to cryptographic nonces (`CSP.NONCE`) and Django's `json_script`.

### 4. Specifying `CSRF_TRUSTED_ORIGINS` Without the Protocol Scheme

Since Django 4.0, configuring `CSRF_TRUSTED_ORIGINS = ["example.com"]` fails validation. Always provide the protocol scheme: `["https://example.com"]`.

### 5. Skipping `python manage.py check --deploy` in CI/CD Pipelines

Django includes a built-in automated check designed specifically for production deployments. Running this command should be a non-negotiable step in every CI/CD deployment pipeline.

---

## Production-Ready Checklist: Security Settings for `settings.py`

Here is a hardened baseline configuration for production Django deployments:

```python
# settings.py - Production Security Baseline

DEBUG = False

# HTTPS & Cookie Security
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
CSRF_COOKIE_HTTPONLY = False  # Must be False if client JS needs to read the token
CSRF_COOKIE_SAMESITE = "Lax"
SESSION_COOKIE_SAMESITE = "Lax"

# Trusted Origins for Cross-Origin CSRF
CSRF_TRUSTED_ORIGINS = [
    "https://app.example.com",
    "https://admin.example.com",
]

# HTTP Strict Transport Security (HSTS)
SECURE_HSTS_SECONDS = 31536000  # 1 year
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True

# Browser Defense Headers
SECURE_CONTENT_TYPE_NOSNIFF = True
X_FRAME_OPTIONS = "DENY"

# Reverse Proxy Configuration (e.g., behind NGINX or Kubernetes Ingress)
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
```

Before deploying, run the automated security audit in your shell:

```bash
python manage.py check --deploy
```

This command flags unencrypted cookies, missing security headers, and active debug flags before your code reaches production. Looking for specialized support in auditing or designing enterprise-grade software? Discover our [Cloud Security Services](https://www.blueshoe.io/our-services/cloud-security-services/) and bespoke [Cloud Application Development](https://www.blueshoe.io/our-services/cloud-application-development/).

---

## Conclusion: Defense-in-Depth for Modern Django Applications

CSRF, CORS, and CSP may sound like an interchangeable alphabet soup of web security, but they address three fundamentally distinct threat boundaries:

- **CSRF asks:** *Was this state-changing write request intentionally initiated by an authenticated user of my application?*
- **CORS asks:** *Is third-party browser JavaScript authorized to parse and process this API response?*
- **CSP asks:** *Which scripts, styles, and external connections is the browser permitted to execute on this web page?*

Understanding these three controls empowers you to make informed architectural decisions and deploy resilient, enterprise-ready web applications. With native CSP support built into Django 6.0, the framework offers a complete, self-contained security toolkit that eliminates the need for legacy workarounds.

---

## Frequently Asked Questions (FAQ)

### What is the difference between CSRF, CORS, and CSP in Django?

CSRF prevents unauthorized state-changing write requests on behalf of an authenticated user. CORS is a browser security policy governing whether client-side JavaScript can read HTTP responses from another origin. CSP is an HTTP header restricting which scripts, styles, images, and resources the browser is permitted to load and execute.

### What is the difference between CSRF_TRUSTED_ORIGINS and CORS_ALLOWED_ORIGINS?

`CORS_ALLOWED_ORIGINS` tells the browser who is allowed to read API responses (`Access-Control-Allow-Origin`). `CSRF_TRUSTED_ORIGINS` instructs Django on the server side to accept unsafe write requests (`POST`, `PUT`, `DELETE`) originating from that cross-origin domain. Both settings serve distinct security functions and cannot replace one another.

### How does the built-in Content Security Policy (CSP) work in Django 6.0?

Starting with Django 6.0, CSP is natively integrated into the framework via `django.middleware.csp.ContentSecurityPolicyMiddleware` and configured through the `SECURE_CSP` (or `SECURE_CSP_REPORT_ONLY`) setting. External packages like `django-csp` are no longer strictly required for standard setups.

### When does a Django REST Framework (DRF) API require CSRF protection?

CSRF protection is required whenever DRF uses `SessionAuthentication` with browser session cookies. If your API is purely stateless and relies on token or Bearer authentication passed in the HTTP `Authorization` header, CSRF protection is generally unnecessary.

### Why is CORS_ALLOW_ALL_ORIGINS = True dangerous in production?

Setting `CORS_ALLOW_ALL_ORIGINS = True` allows any arbitrary third-party website to read sensitive data from your API via client-side JavaScript. When combined with credentials like cookies or auth headers, wildcard CORS completely undermines fundamental browser isolation mechanisms and exposes sensitive data.

### How do I pass the Django CSRF token in AJAX and Fetch requests?

Extract the token from the cookie (by default `csrftoken`) via JavaScript and include it in the `X-CSRFToken` request header. Django's `CsrfViewMiddleware` automatically checks this header for non-safe HTTP methods.

## Sitemap

See the full [sitemap](https://www.blueshoe.io/sitemap.md) for all pages.
