Django / Django REST FrameworkPython
Django Webhook Guide: @csrf_exempt & HMAC Signature Verification
Learn how to build secure webhook receivers in Django. Exempt webhook routes from CSRF protection and access `request.body` for HMAC validation.
Direct Answer / Quick Implementation Guide
In Django, apply the `@csrf_exempt` decorator to your webhook view function to prevent 403 Forbidden CSRF errors. Access `request.body` (raw bytes) to compute HMAC-SHA256 signatures before parsing with `json.loads()`.
Essential Implementation Takeaways
- •Always add `@csrf_exempt` to webhook views.
- •Use `request.body` for raw payload bytes.
- •Validate signatures with `hmac.compare_digest()`.
- •Use Celery tasks to process heavy webhook workflows asynchronously.
Common Gotchas in Django / Django REST Framework & How to Fix Them
HTTP 403 Forbidden CSRF error
Fix:Add `@csrf_exempt` decorator above the view function.
Complete Production Boilerplate
Python# views.py (Django Webhook View)
from django.views.decorators.csrf import csrf_exempt
from django.http import JsonResponse, HttpResponseBadRequest, HttpResponseForbidden
import hmac
import hashlib
import json
import os
WEBHOOK_SECRET = os.environ.get("WEBHOOK_SECRET", "secret").encode('utf-8')
@csrf_exempt
def webhook_receiver(request):
if request.method != 'POST':
return HttpResponseBadRequest("Only POST method allowed")
raw_body = request.body
signature = request.headers.get('X-Signature')
if not signature:
return HttpResponseBadRequest("Missing signature")
computed = hmac.new(WEBHOOK_SECRET, raw_body, hashlib.sha256).hexdigest()
if not hmac.compare_digest(signature, computed):
return HttpResponseForbidden("Invalid signature")
data = json.loads(raw_body.decode('utf-8'))
# Trigger Celery task: process_event.delay(data)
return JsonResponse({"status": "received"}, status=200)Step-by-Step Setup Guide
1
Import `@csrf_exempt` from `django.views.decorators.csrf`.
2
Decorate your view with `@csrf_exempt`.
3
Read raw bytes from `request.body`.
4
Verify signature and return `JsonResponse({"status": "received"}, status=200)`.
How to Test Locally on Your Machine
- Run `python manage.py runserver 8000`.
- Forward webhooks from SafeWebhook to `http://localhost:8000/api/webhook/`.
Frequently Asked Questions: Django / Django REST Framework Webhooks
Why does Django require @csrf_exempt on webhook views?
Django enforces CSRF token validation on all POST requests by default. Webhooks originate from external servers without session cookies, so they must be exempted.
Test Your Django / Django REST Framework Webhook Handler Live
Capture payloads, simulate errors, and replay directly to your localhost server.