Introduction
Content Security Policy's frame-ancestors directive is the modern replacement for X-Frame-Options. It offers more flexibility, including allowing specific domains to frame your content.
Key Concepts
frame-ancestors: CSP directive controlling who can embed your page.
More Flexible: Unlike X-Frame-Options, allows specific domains.
Supersedes X-Frame-Options: Browsers prefer CSP when both are set.
Real World Context
X-Frame-Options only supports DENY and SAMEORIGIN—it cannot allowlist specific partner domains. Organizations that need to embed content for trusted partners (widget marketplaces, embedded dashboards) must use CSP frame-ancestors for the flexibility to specify exact allowed domains.
Deep Dive
Django 6.0+ Native CSP (Recommended)
python# settings.py from django.utils.csp import CSP MIDDLEWARE = [ 'django.middleware.csp.ContentSecurityPolicyMiddleware', ... ] SECURE_CSP = { 'frame-ancestors': [CSP.SELF], # Replaces X-Frame-Options } # Or deny all framing SECURE_CSP = { 'frame-ancestors': [CSP.NONE], } # Allow specific trusted domains SECURE_CSP = { 'frame-ancestors': [CSP.SELF, 'https://trusted-partner.com', 'https://app.example.com'], }
Legacy: Using django-csp (Django < 6.0)
For Django < 6.0, use the third-party django-csp package:
python# pip install django-csp # settings.py MIDDLEWARE = [ 'csp.middleware.CSPMiddleware', ... ] # No framing allowed (like DENY) CSP_FRAME_ANCESTORS = ("'none'",) # Same origin only (like SAMEORIGIN) CSP_FRAME_ANCESTORS = ("'self'",) # Specific trusted domains CSP_FRAME_ANCESTORS = ( "'self'", 'https://trusted-partner.com', 'https://app.example.com', )
Per-View CSP
pythonfrom csp.decorators import csp_update @csp_update(FRAME_ANCESTORS="'self' https://partner.com") def partnered_widget(request): pass
Migration from X-Frame-Options
python# Keep both during transition X_FRAME_OPTIONS = 'DENY' # Use SECURE_CSP (Django 6.0+) or CSP_FRAME_ANCESTORS (django-csp) # Modern browsers use CSP, older use X-Frame-Options
Common Pitfalls
- Setting frame-ancestors without keeping X-Frame-Options — Older browsers (IE 11) don't support CSP frame-ancestors; keep both headers during the transition period.
- Using frame-src instead of frame-ancestors — frame-src controls what YOUR page can frame (children); frame-ancestors controls who can frame YOU (parents). Confusing these leaves you unprotected.
Best Practices
- Use CSP for flexibility: When you need specific domain allowlists.
- Keep X-Frame-Options: For older browser support.
- Use 'none' by default: More secure than 'self'.
Summary
frame-ancestors provides fine-grained control over framing. Use django-csp to set it globally, and per-view decorators for exceptions. Keep X-Frame-Options alongside CSP for maximum browser compatibility.