Audit Log
Record all create, update, and delete operations on user assets for compliance, security auditing, and debugging. Every mutation is logged with metadata about what changed, when, and whether it succeeded or failed.
Setup
Audit logging is enabled by default. To disable it:
AUDIT_LOG_ENABLED='false'That's all. No additional configuration needed - the middleware automatically captures all mutations to the database.
How It Works
The audit log middleware intercepts every API request that modifies data:
sequenceDiagram
participant User
participant API as API Route
participant Middleware as Audit Middleware
participant Handler as Route Handler
participant DB as D1 Database
User->>API: POST /v1/images
API->>Middleware: Intercept request
Middleware->>Middleware: Parse action + resource type
Middleware->>Handler: Execute request
Handler->>DB: Create image
DB-->>Handler: Success
Handler-->>Middleware: Response
Middleware->>Middleware: Extract resource ID<br/>Build metadata
Middleware->>DB: Insert audit log entry<br/>(via waitUntil)
Middleware-->>User: Return response
Note over Middleware,DB: Fire-and-forget pattern<br/>Never blocks the responseWhat Gets Logged
Every mutation generates an audit entry with:
- Action - create, update, delete, or restore
- Resource Type - image, album, tag, api_key, backup_config, favourite, user, encryption
- Resource ID - Specific item affected (null for failed operations)
- Status - success or failed
- Affected Count - Number of items modified (for bulk operations)
- Metadata - Additional context:
- Changed fields (for updates)
- Operation type (replace_file, move_album, etc.)
- Error message (for failures)
- IP address (from CF-Connecting-IP header)
Action Detection
Actions are automatically inferred from HTTP methods:
POST→ create (or restore if path contains/restore)PUT/PATCH→ updateDELETE→ delete
Resource types are extracted from the URL path (e.g., /v1/images → image).
Non-Blocking Design
Audit logging uses Cloudflare Workers' waitUntil() to ensure database writes complete after the response is sent. This means:
- Zero performance impact on API requests
- Audit failures never break the main flow
- Logs are written asynchronously but reliably
If audit logging fails (database error, etc.), a warning is logged but the user's request succeeds normally.
API Endpoints
List Audit Logs
GET /v1/audit-logs?limit=50&action=delete&resource_type=imageQuery parameters:
limit- Results per page (default: 50, max: 100)cursor- Pagination cursoraction- Filter by action typeresource_type- Filter by resourcestatus- Filter by success/faileddate_from- Start date (YYYY-MM-DD)date_to- End date (YYYY-MM-DD)
Export Audit Logs
GET /v1/audit-logs/export?date_from=2025-01-01&date_to=2025-01-31Downloads CSV file with all audit entries for the specified date range.
Statistics
GET /v1/audit-logs/statsReturns aggregated statistics:
- Total entries
- Additions, updates, deletions, restores
- Failed operations count
Data Retention
Audit logs are retained for 30 days by default, after which they're automatically deleted by the cleanup cron job (runs at 1 AM UTC daily).
This retention period is configured in the source code (AUDIT_LOG.RETENTION_DAYS). For compliance purposes requiring longer retention, you should export logs periodically using the export endpoint.
Metadata Examples
Image Update
{
"action": "update",
"resource_type": "image",
"resource_id": "abc123",
"status": "success",
"metadata": {
"fields": ["alt", "tags"],
"ip": "203.0.113.42"
}
}Failed Delete
{
"action": "delete",
"resource_type": "album",
"resource_id": null,
"status": "failed",
"metadata": {
"error": "Cannot delete album with images",
"ip": "203.0.113.42"
}
}Bulk Operation
{
"action": "delete",
"resource_type": "image",
"resource_id": null,
"status": "success",
"affected_count": 15,
"metadata": {
"operation": "bulk_delete",
"bulkIds": ["id1", "id2", "..."]
}
}Performance Considerations
Audit logging adds minimal overhead:
- Database writes: One row per mutation (~50-100 bytes)
- Processing time: ~1-2ms to parse and log (non-blocking)
- Storage growth: ~1KB per 10-20 operations
For high-traffic deployments with thousands of mutations per day, audit logs will grow steadily. The 30-day retention keeps storage in check, but you should monitor D1 usage if you're doing bulk operations frequently.
Privacy & Security
What's Included
- Hashed user identifiers (owner field)
- IP addresses (optional, can be disabled in code)
- Resource IDs and changed fields
What's NOT Included
- Request/response bodies
- Authentication tokens
- File contents
- Personal data beyond what's in resource IDs
The audit log is designed for compliance and debugging, not forensic analysis. If you need full request logging, use Cloudflare Logpush instead.
Disabling
To completely disable audit logging:
AUDIT_LOG_ENABLED='false'Existing audit logs remain in the database until the retention period expires. No new entries will be created.