Skip to main content

CORS Configuration

Django-CFG provides automatic CORS handling for public API endpoints via middleware, plus decorators for custom configurations.

Architecture

Request → PublicAPICORSMiddleware → CorsMiddleware → View
↓ ↓
/cfg/leads/* Other paths
(open CORS) (security_domains)

Key components:

  • PublicAPICORSMiddleware - Handles CORS for public API paths (e.g., /cfg/leads/)
  • CorsMiddleware (django-cors-headers) - Handles CORS for other paths based on security_domains
  • PublicAPIMixin - Removes authentication for public endpoints
  • Decorators - For custom CORS on function-based views

Quick Start

Public endpoints like lead submission work automatically:

from django_cfg.mixins import PublicAPIMixin
from rest_framework import viewsets

class LeadViewSet(PublicAPIMixin, viewsets.ModelViewSet):
"""Public API - no auth required, CORS handled by middleware."""
queryset = Lead.objects.all()
serializer_class = LeadSerializer

CORS is handled automatically by PublicAPICORSMiddleware for paths matching PUBLIC_API_CORS_PATHS.

Function-Based Views

from django_cfg.core.decorators import cors_allow_all
from django.http import JsonResponse

@cors_allow_all
def webhook_handler(request):
return JsonResponse({'received': True})

Middleware Configuration

PUBLIC_API_CORS_PATHS

Configure which paths have open CORS in settings.py:

# Default: ['/cfg/leads/']
PUBLIC_API_CORS_PATHS = [
'/cfg/leads/', # Lead submission API
'/api/webhooks/', # Webhook handlers
'/api/public/', # Other public endpoints
]

PUBLIC_API_CORS_REGEX

For complex path matching:

PUBLIC_API_CORS_REGEX = r'^/(cfg/leads|api/webhooks|api/public)/'

CORS Headers Added

For matching paths, middleware adds:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With, Accept, Origin
Access-Control-Max-Age: 86400

PublicAPIMixin

Mixin for public API endpoints without authentication.

from django_cfg.mixins import PublicAPIMixin
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.response import Response

class ContactFormViewSet(PublicAPIMixin, viewsets.GenericViewSet):
"""
Public contact form API.

- No authentication required (AllowAny)
- CORS handled by PublicAPICORSMiddleware
"""

@action(detail=False, methods=['post'])
def submit(self, request):
serializer = ContactSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response({'success': True}, status=201)
return Response({'errors': serializer.errors}, status=400)

What PublicAPIMixin provides:

  • permission_classes = [AllowAny]
  • authentication_classes = []

What it does NOT provide:

  • CORS headers (handled by middleware)

Decorators Reference

@cors_allow_all

Decorator for open CORS on function-based views.

from django_cfg.core.decorators import cors_allow_all
from rest_framework.decorators import api_view
from rest_framework.response import Response

@cors_allow_all
@api_view(['POST'])
def submit_lead(request):
"""Lead submission endpoint with open CORS."""
return Response({'success': True})

Features:

  • Adds CORS headers to response
  • Handles OPTIONS preflight automatically
  • Disables CSRF protection

@cors_origins

Decorator for restricted CORS origins.

from django_cfg.core.decorators import cors_origins

@cors_origins([
"https://myapp.com",
"https://admin.myapp.com",
])
def restricted_api(request):
"""Only accessible from whitelisted origins."""
return JsonResponse({'data': 'restricted'})

With credentials:

@cors_origins(
allowed_origins=["https://myapp.com"],
credentials=True,
)
def authenticated_api(request):
return JsonResponse({'user': request.user.username})

Parameters:

ParameterTypeDefaultDescription
allowed_originsList[str]requiredAllowed origin URLs
methodsstr"GET, POST, ..."Allowed HTTP methods
headersstr"Content-Type, ..."Allowed headers
max_ageint86400Preflight cache (seconds)
credentialsboolTrueAllow credentials

@cors_exempt

Alias for @cors_allow_all:

from django_cfg.core.decorators import cors_exempt

@cors_exempt
def webhook_handler(request):
return JsonResponse({'received': True})

Usage Patterns

Lead Submission (Built-in)

Django-CFG's lead system works out of the box:

# Already configured in django_cfg.apps.business.leads
class LeadViewSet(PublicAPIMixin, viewsets.ModelViewSet):
queryset = Lead.objects.all()
serializer_class = LeadSubmissionSerializer

@action(detail=False, methods=['post'])
def submit(self, request):
# ... submission logic

Frontend usage:

await fetch('https://api.example.com/cfg/leads/submit/', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: 'John Doe',
email: 'john@example.com',
message: 'Hello!'
})
});

Webhook Handler

from django_cfg.core.decorators import cors_allow_all
from django.http import JsonResponse
import json

@cors_allow_all
def stripe_webhook(request):
payload = json.loads(request.body)
# Process webhook...
return JsonResponse({'status': 'ok'})

Mixed Authentication

from django_cfg.mixins import AdminAPIMixin
from rest_framework import viewsets
from rest_framework.permissions import AllowAny

class ProductViewSet(AdminAPIMixin, viewsets.ModelViewSet):
"""
- List/Retrieve: Public
- Create/Update/Delete: Admin only
"""
queryset = Product.objects.all()

def get_permissions(self):
if self.action in ['list', 'retrieve']:
return [AllowAny()]
return super().get_permissions()

Frontend Integration

ContactPage Component

import { ContactPage } from '@djangocfg/layouts';

function Contact() {
return <ContactPage />; // Uses default API URL
}

Custom API URL

import { ContactPage } from '@djangocfg/layouts';

function Contact() {
return (
<ContactPage
apiUrl="https://api.myapp.com/cfg/leads/submit/"
/>
);
}

Security Best Practices

1. Use middleware for public endpoints

# settings.py
PUBLIC_API_CORS_PATHS = ['/cfg/leads/'] # Only truly public paths

2. Never expose sensitive data without auth

# WRONG
class UserViewSet(PublicAPIMixin, viewsets.ModelViewSet):
queryset = User.objects.all() # Exposes all users!

# CORRECT
class UserViewSet(AdminAPIMixin, viewsets.ModelViewSet):
queryset = User.objects.all()

3. Use security_domains for authenticated APIs

# config.py
config = DjangoConfig(
security_domains=[
"myapp.com",
"admin.myapp.com",
],
# ...
)

Middleware Order

PublicAPICORSMiddleware must be before CorsMiddleware:

MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware',
'django_cfg.core.middleware.PublicAPICORSMiddleware', # Before CorsMiddleware
'corsheaders.middleware.CorsMiddleware',
# ...
]

This is configured automatically by Django-CFG.

TAGS: cors, api, middleware, public-api, security DEPENDS_ON: [rest-framework, django-views] USED_BY: [leads, contact-forms, webhooks]