Webhook Configuration
This guide explains how to configure webhooks in your Cloudflare dashboard
to automatically update image status when uploads complete, and documents
the request/response contract that WebhookView enforces on the
Django side.
Note
The signature-validation gate is enforced when
CLOUDFLARE_IMAGES["WEBHOOK_SECRET"] is set: requests without a
valid signature header are rejected before the body is parsed. See
Response Codes below for the full status-code matrix.
What are Webhooks?
Webhooks allow Cloudflare to automatically notify your Django application when image uploads are completed or failed.
Note
Webhooks are currently only supported for direct creator uploads.
Step-by-Step Webhook Configuration
1. Access Cloudflare Dashboard
Log in to your Cloudflare account
Select your account
3. Create Webhook Destination
From the Webhooks card, select “Create”
Fill in the webhook details:
Name: Give your webhook a descriptive name (e.g., “Django Images Webhook”)
URL:
https://yourdomain.com/cloudflare-images/api/webhook/Secret (Optional but recommended): Enter your webhook secret if configured
Click “Save and Test”
The new webhook will appear in the Webhooks card
4. Create Notification
Go to “Notifications” > “All Notifications”
Click “Add”
Under the list of products, locate “Images” and select “Select”
Configure the notification:
Name: Give your notification a descriptive name
Description: Optional description
Webhooks: Select the webhook you created in step 3
Click “Save”
5. Webhook Events
The webhook will be triggered for these events:
✅ Image Upload Complete - When a direct creator upload succeeds
✅ Image Upload Failed - When a direct creator upload fails
Important
Webhooks are only triggered for direct creator uploads, not for regular API uploads.
Django Configuration
End-to-end Django setup is five steps. Run them in order on a fresh project; each step is independent on a project that already has the toolkit installed.
Step 1: Install the package
pip install django-cloudflareimages-toolkit
Then add "django_cloudflareimages_toolkit" to INSTALLED_APPS
and run python manage.py migrate so the CloudflareImage and
ImageUploadLog tables are created. The webhook view writes status
changes into these tables.
Step 2: Configure the webhook secret
Generate a high-entropy secret (e.g. python -c 'import secrets;
print(secrets.token_urlsafe(32))') and store it in your settings.
The same value must be entered in the Cloudflare dashboard in step 5.
# settings.py
import os
CLOUDFLARE_IMAGES = {
"ACCOUNT_ID": os.environ["CLOUDFLARE_ACCOUNT_ID"],
"API_TOKEN": os.environ["CLOUDFLARE_API_TOKEN"],
"WEBHOOK_SECRET": os.environ["CLOUDFLARE_WEBHOOK_SECRET"],
}
Important
Setting WEBHOOK_SECRET is what enforces signature validation
on inbound webhook requests. Without it, every well-formed payload
is accepted. See Response Codes for the gate contract.
Step 3: Mount the URLs
# urls.py
from django.urls import include, path
urlpatterns = [
# ... your other URLs ...
path("cloudflare-images/", include("django_cloudflareimages_toolkit.urls")),
]
This exposes the webhook at https://yourdomain.com/cloudflare-images/api/webhook/
along with the rest of the toolkit’s REST endpoints. The view is
CSRF-exempt by default, so you do not need to add it to
CSRF_EXEMPT_PATHS or wrap it.
Step 4: React to status changes
The bundled WebhookView updates the CloudflareImage row’s
status field whenever Cloudflare reports a transition. To react to
those changes in your own code, hook Django’s post_save signal on
CloudflareImage and branch on the new status:
# apps.py
from django.apps import AppConfig
class MyAppConfig(AppConfig):
name = "myapp"
def ready(self):
from django.db.models.signals import post_save
from django_cloudflareimages_toolkit.models import CloudflareImage
from . import handlers
post_save.connect(handlers.on_image_status_change, sender=CloudflareImage)
# handlers.py
def on_image_status_change(sender, instance, created, **kwargs):
if instance.status == "uploaded":
# Image is live — generate thumbnails, send notifications, etc.
notify_owner(instance)
elif instance.status == "failed":
# Cloudflare rejected the upload — reset the user's UI state
mark_upload_failed(instance)
If you’d rather process events without going through the database row,
subclass WebhookView and override post; see
apps/api/v1/images/cloudflare_views.py in
pacficient-labs/django-cloudflareimages-toolkit
for a working example.
Step 5: Configure Cloudflare to deliver
Follow the Step-by-Step Webhook Configuration above to register
your public URL (https://yourdomain.com/cloudflare-images/api/webhook/)
and the same secret you stored in step 2. Cloudflare will start
delivering events on the next direct-creator upload.
Alternative: Using Cloudflare API
You can also configure webhooks programmatically using the Cloudflare API. This involves two steps:
Step 1: Create Webhook Destination
curl -X POST "https://api.cloudflare.com/client/v4/accounts/{account_id}/notification_destinations" \
-H "Authorization: Bearer {api_token}" \
-H "Content-Type: application/json" \
--data '{
"name": "Django Images Webhook",
"type": "webhook",
"webhook": {
"url": "https://yourdomain.com/cloudflare-images/api/webhook/",
"secret": "your-webhook-secret"
}
}'
Step 2: Create Notification Policy
curl -X POST "https://api.cloudflare.com/client/v4/accounts/{account_id}/alerting/v3/policies" \
-H "Authorization: Bearer {api_token}" \
-H "Content-Type: application/json" \
--data '{
"name": "Images Upload Notifications",
"description": "Notifications for Cloudflare Images uploads",
"enabled": true,
"alert_type": "images_upload_complete",
"mechanisms": {
"webhooks": ["webhook-destination-id-from-step-1"]
}
}'
Note
Replace webhook-destination-id-from-step-1 with the ID returned from the first API call.
Response Codes
WebhookView.post returns the following statuses. Configure your
upstream monitoring on the 4xx path for caller errors and the
5xx path for genuine outages; that split is meaningful here.
Status |
When |
|---|---|
|
Payload validated, processed, image found and updated. |
|
Body wasn’t valid JSON, or body parsed but failed
|
|
|
|
Payload was valid but referenced a |
|
Reserved for genuinely unexpected failures inside
|
Changed in version 1.0.11: The signature gate is now enforced when WEBHOOK_SECRET is set
(was previously bypassed when the header was absent), and malformed
payloads now return 400 instead of being misclassified as 500.
See the v1.0.11 release notes for the underlying bug analysis.
Webhook Payload Examples
Upload Complete
{
"id": "2cdc28f0-017a-49c4-9ed7-87056c83901",
"uploaded": "2024-01-01T12:00:00.000Z",
"variants": [
"https://imagedelivery.net/Vi7wi5KSItxGFsWRG2Us6Q/2cdc28f0-017a-49c4-9ed7-87056c83901/public",
"https://imagedelivery.net/Vi7wi5KSItxGFsWRG2Us6Q/2cdc28f0-017a-49c4-9ed7-87056c83901/thumbnail"
],
"metadata": {
"key": "value"
},
"requireSignedURLs": true
}
Upload Failed
{
"id": "2cdc28f0-017a-49c4-9ed7-87056c83901",
"error": "Image processing failed",
"timestamp": "2024-01-01T12:00:00.000Z"
}
Troubleshooting
Common Issues
Webhook not receiving requests
Check that your Django server is accessible from the internet
Verify the webhook URL is correct
Check firewall settings
Authentication errors
Verify your webhook secret matches in both Cloudflare and Django
Check that the secret is properly configured
SSL/TLS errors
Ensure your webhook URL uses HTTPS
Check that your SSL certificate is valid
Testing Webhooks Locally
For local development, you can use tools like ngrok to expose your local server:
# Install ngrok
npm install -g ngrok
# Expose your local Django server
ngrok http 8000
# Use the ngrok URL in your webhook configuration
# Example: https://abc123.ngrok.io/cloudflare-images/api/webhook/
Webhook Logs
Check your Django logs for webhook activity:
# In your Django settings.py
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'file': {
'level': 'INFO',
'class': 'logging.FileHandler',
'filename': 'cloudflare_webhooks.log',
},
},
'loggers': {
'django_cloudflareimages_toolkit': {
'handlers': ['file'],
'level': 'INFO',
'propagate': True,
},
},
}
Security Considerations
Always use HTTPS for webhook URLs
Configure webhook secrets to verify request authenticity
Validate payload structure before processing
Rate limit webhook endpoints if necessary
Log webhook activity for monitoring and debugging
Monitoring Webhook Health
You can monitor webhook health through:
Django Admin: View webhook logs in the admin interface
Cloudflare Dashboard: Check webhook delivery status
Application Logs: Monitor webhook processing in your logs
Custom Metrics: Track webhook success/failure rates
Next Steps
After configuring webhooks:
Test with a sample image upload
Monitor the Django admin for automatic status updates
Check logs to ensure webhooks are being processed correctly
Set up monitoring and alerting for webhook failures
For more information, see the Cloudflare Images API documentation.