Django Privacy and GDPR Production Checklist
A technical privacy checklist for Django in production: personal data inventory, logs and error tracking, cookies, retention and deletion including backups, exports, encryption, access control, and processors.
Problem statement
A Django app that handles users' names, emails, addresses, or payment details holds personal data, and in the EU and many other jurisdictions that brings obligations: collect only what you need, protect it, keep it only as long as necessary, let people access and delete it, and know who else processes it. Most of the effort is technical, and much of it lives in deployment and operations rather than models: logs, error trackers, backups, analytics, and third-party services.
This checklist covers the technical side for a production Django deployment. It is not legal advice; your privacy policy, legal bases, and contracts need review by someone qualified. It helps you find where personal data actually flows so those decisions are based on facts.
- Inventory personal data: models and fields, uploaded files, logs, error reports, analytics, backups, and third-party services.
- Keep personal data out of logs and error trackers where possible (no request bodies, no passwords, minimal identifiers).
- Use only essential cookies without consent; gate analytics and marketing cookies behind consent.
- Define retention per data type and enforce it with scheduled jobs, including sessions, logs, and backups.
- Implement data export and deletion as tested code paths, and document how deletion interacts with backups.
- Encrypt in transit everywhere and at rest where the platform allows; restrict and audit access.
- List your processors (hosting, email, error tracking), their regions, and data processing agreements.
Checklist
1. Data inventory
- Models and fields containing personal data are listed, with the purpose of each (for example "email: login and transactional mail").
- Free-text fields that may contain personal data (notes, messages, support tickets) are included.
- Uploaded files (avatars, documents, ID scans) are listed with their storage location.
- Data you do not need is not collected, or is removed.
A quick way to start the list:
from django.apps import apps
for model in apps.get_models():
fields = [f.name for f in model._meta.get_fields() if getattr(f, "concrete", False)]
print(model._meta.label, fields)
2. Logs
- Access logs: decide whether full IP addresses are needed; shorten or anonymise them in Nginx logs if not, and set retention.
- Application logs do not contain passwords, tokens, full request bodies, or payment data; log user IDs rather than emails where possible.
django.requesterror logs andAdminEmailHandleremails can include request data; error emails are often better replaced by an error tracker with scrubbing.- Log retention is defined (for example 14 to 30 days) and enforced by rotation and in central log storage.
3. Error tracking and monitoring
- Error tracker SDKs do not send personal data by default; for Sentry, keep
send_default_pii=Falseunless you have a reason and a legal basis. - Server-side scrubbing rules remove sensitive fields (passwords, tokens, card data, emails) from events.
- Retention in the error tracker and APM is set to what you need.
4. Cookies and tracking
- Django's session and CSRF cookies are strictly necessary for the site to work; document them.
- Analytics, advertising, and third-party embed cookies are only set after consent where required, and the consent mechanism actually blocks scripts before consent.
- Cookie flags are set:
SESSION_COOKIE_SECURE,CSRF_COOKIE_SECURE,HttpOnly, andSameSite.
5. Retention and deletion
- Retention periods exist per data type (accounts, orders, logs, support messages, backups) and match your privacy policy.
- Expired sessions are cleared (
clearsessionson a schedule). - Inactive accounts and old records are deleted or anonymised by scheduled jobs, not by hand.
- Account deletion removes or anonymises the user's data across related models, uploaded files, and caches.
- Deletion requests are propagated to processors that hold copies (email service contact lists, CRM, analytics).
- Backups: you have a documented approach, typically letting backups expire on their normal schedule and re-applying deletions if a backup is ever restored.
6. Access and export
- Users can obtain a copy of their data, or staff can produce it with a tested management command in a machine-readable format (JSON or CSV).
- Staff access to personal data in the admin follows least privilege: permissions and groups limit who sees what.
- Admin access is logged (Django's
LogEntryrecords changes; consider logging views of sensitive records too). - Production database access for developers is limited, audited, and not the default way of working.
7. Security of processing
- HTTPS everywhere, with HSTS; internal connections to the database and cache use TLS when they cross networks you do not control.
- Disks, databases, and object storage are encrypted at rest where the platform offers it.
- Passwords use Django's default hashers; no custom or weakened hashing.
- Secrets are stored outside the repository; access to them is limited.
- Non-production environments do not contain real personal data, or use sanitised copies.
8. Processors and locations
- Every service receiving personal data is listed: hosting, database, backups, email delivery, error tracking, analytics, support tools.
- For each: data processed, region, data processing agreement, and transfer mechanism if data leaves your required region.
- Hosting regions are chosen deliberately (for example EU regions for an EU user base, if required).
9. Incidents
- There is a documented process for personal data breaches: detection, assessment, who decides on notification, and time limits.
- Logs and audit trails are sufficient to determine what data was affected.
Explanation
Privacy regulation asks what you store, why, for how long, who can see it, and whether you can hand it over or delete it on request. In a Django deployment, the database answers only part of that. Personal data also flows into access logs, error reports, caches, backups, search indexes, and third-party services, each with its own retention and access rules. A technical inventory plus enforced retention and tested export and deletion paths make the legal commitments something the system actually does.
Edge cases / notes
Anonymisation versus deletion
Keeping order records for accounting while removing personal details is common: replace names and addresses with placeholders and unlink the user, but keep amounts and dates. Make sure anonymised records cannot be re-identified through other fields.
Search indexes and caches
External search indexes (Elasticsearch, OpenSearch) and caches hold copies. Deletion code should remove documents from indexes and invalidate cached pages or fragments containing the data.
When to automate this
Turn retention into scheduled management commands, deletion and export into tested code paths with a single entry point, and the inventory into a checked-in document updated in code review when models change. Automation is what keeps privacy promises true after the team changes.
Next steps
- Keep personal data out of logs with Django Logging Setup for Production.
- Configure scrubbing in Set Up Sentry Error Monitoring for Django.
- Align backup retention with the Django Backup and Recovery Checklist.
- Protect credentials with Manage Django Secrets in Production.
FAQ
Is this checklist enough to be GDPR compliant?
No. It covers technical measures. Legal bases, privacy notices, contracts, and records of processing need review by someone qualified.
Do Django's session and CSRF cookies need consent?
They are generally considered strictly necessary for the site to function. Analytics and marketing cookies usually require consent in the EU.
How do I handle deletion requests with backups?
Common practice is to let backups expire on their normal schedule and re-apply deletions if a backup is restored. Document the approach.
Should error trackers receive user emails and IP addresses?
Only if needed and justified. Default to sending no personal data and scrub sensitive fields.