# Sentry beforeSend: scrub PII without silently dropping everything
## The callback
```js
Sentry.init({
dsn: "___PUBLIC_DSN___",
beforeSend(event) {
if (event.user) {
delete event.user.email; // keep the id, drop the address
}
return event; // ALWAYS return the event, or return null to drop deliberately
},
});
```
The classic bug: a `beforeSend` that mutates the event but forgets to return it. In JS that returns `undefined`, which the SDK treats as drop. Events vanish with no error. If volume craters after adding `beforeSend`, read the function first.
## Prefer category control over hand-scrubbing (v11)
`sendDefaultPii` is replaced by `dataCollection`, with per-category switches:
```js
dataCollection: {
userInfo: false,
cookies: false,
httpBodies: [],
urlQueryParams: { deny: ["forwarded", "-ip"] },
}
```
Warning from the migration guide: the v11 default collects MORE than v10 did (user info, cookies, request/response bodies, genAI inputs/outputs). If you relied on the v10 restrictive default, set the baseline explicitly or you start shipping data you never approved.
## The built-in denylist
The SDK always scrubs values whose keys look sensitive (auth, token, secret, password) and sends `[Filtered]`. Treat it as best effort: it matches on the key name, so a credential in a field named `customer_ref` sails through. Name sensitive fields like they are sensitive, or scrub them in `beforeSend`.
## Layer the defenses
1. `dataCollection` categories: collect only what you need.
2. `beforeSend`: surgical redaction of the rest.
3. Server-side scrubbing in project settings: backstop for anything the SDK missed.
Cheaper filters first: `ignoreErrors` for known noise and `denyUrls`/`allowUrls` for third-party script errors cost less than a `beforeSend` that parses every event.