Prefer an allow list. It logs only the fields you named, so a new field added next month stays out of the logs until someone decides it belongs. A deny list masks fields you remembered to list, and anything you forgot is written in clear text.
The two approaches
A deny list says: log everything, but mask password, token, card_number and a few others. It is quick to add and keeps logs rich.
An allow list says: log only user_id, route, status and duration. Everything else is dropped or replaced with a placeholder. It takes more thought up front and yields thinner logs.
The OWASP Logging Cheat Sheet leans toward removal. It says sensitive data such as access tokens, passwords, keys and personal data should be removed, masked, sanitised, hashed or encrypted rather than logged directly.
Side by side
| Question | Allow list | Deny list |
|---|---|---|
| What happens to a new field? | Dropped until approved | Logged in clear text |
| Failure mode | Missing context during debugging | Leaked secret in logs |
| Setup effort | Higher | Lower |
| Maintenance | Update when logs need a field | Update whenever a sensitive field appears |
| Review friction | Small, visible diff | Easy to forget |
| Best for | Request logs, audit events, third party sinks | Local debugging, short lived diagnostics |
The failure modes are not symmetrical. Missing context is annoying and fixable. A leaked secret must be rotated and may already be copied into backups and search indexes.
A hybrid that works
GDPR data minimisation in Article 5(1)(c), which asks for data limited to what is necessary, points the same way. Most teams end up with a layered approach:
- Allow list at the edge for structured fields in request and audit logs.
- Free text such as error messages passes through pattern based masking for things that look like tokens, keys and email addresses. Treat this as a safety net, not the main control, because patterns miss things.
- A hard rule that certain headers, such as Authorization and Cookie, are never logged at all.
- A review step: adding a new logged field requires a short note on why it is needed and how long it is kept.
Where possible, filter inside the application or in your log pipeline before data reaches storage. Filtering at query time leaves the raw data stored.
Make it testable
Write a unit test that logs an object holding known fake secrets and asserts that none appear in the output. Put it in CI. When someone adds a logger or changes the formatter, the test fails if the safeguard was bypassed.
Also look at third party code. A library that logs full request objects at debug level can bypass your own filters, so set its level explicitly and check what it prints.
Pseudonymising rather than dropping is an option for identifiers you need for correlation. See personal data in logs for how.
Quick check
Is regex masking enough on its own?
No. Patterns catch common shapes but miss unusual tokens and free text. Use it as a backstop behind an allow list.
Will an allow list make debugging harder?
Slightly, at first. Add fields when a real need appears, and use a time limited debug mode for one tenant when you need more.
Safeguard policies and gates follow the same principle in the pipeline: define what is allowed and block the rest. Run a free scan at gold.safeguard.sh/scan.
Sources
Last reviewed: 19 October 2026.