Taming Sentry Noise with beforeSend: A Practical Filtering Guide
Sentry's beforeSend hook lets you filter, redact, or sample events before they hit your quota. This guide covers practical patterns for JavaScript and Python SDKs, a worked example, and the latency and visibility trade-offs you'll face in production.
24 May 2026, 21:32 UTC

The Problem: Too Much Noise, Too Little Signal
Your Sentry dashboard is flooded. Health-check endpoints throw 500s during deployments. Third-party scripts leak stack traces you can't fix. Passwords and API keys slip into exception contexts. Meanwhile, your quota burns through the roof. You could raise the plan tier, but the real fix is stopping junk at the source.
Sentry's beforeSend hook lets you inspect, rewrite, or drop events before they leave your process. It's the first line of defense for data volume and privacy—but it runs synchronously in most SDKs, and a poorly written filter can add latency or hide real bugs. This post walks through practical patterns, a worked example in JavaScript and Python, and the trade-offs you'll face in production.
How beforeSend Works Across SDKs
The hook signature varies slightly by platform, but the contract is consistent: receive an event object, return it (possibly modified) to send, or return null/None to drop it. Changes apply only to the outgoing payload—your local state stays untouched.
- JavaScript (browser/Node):
beforeSend(event, hint)—hintcarries the original exception or synthetic event. - Python:
before_send(event, hint)— similar hint structure, often includesexc_info. - Other SDKs: Node, React Native, iOS, Android all expose comparable hooks; check the SDK docs for exact types.
Because the callback runs on the error-reporting path, keep it fast. No network calls, no heavy parsing, no synchronous I/O.
Three Patterns That Pay Off
1. Strip Sensitive Fields
Recursively walk event.request.data, event.extra, and event.contexts to redact keys like password, token, secret, authorization. Sentry's built-in sendDefaultPii option handles some cases, but custom logic catches app-specific fields.
2. Drop Health-Check and Expected Errors
If your load balancer pings /health and your framework throws on missing headers, filter by event.request.url or event.exception.values[0].type. Return null for known-benign patterns.
3. Sample Low-Priority Exceptions
For high-volume, low-severity errors (e.g., validation failures on public forms), apply a deterministic sample: Math.random() < 0.01 or hash the error fingerprint. This keeps a representative slice without blowing quota.
Worked Example: JavaScript and Python Filters
Below are minimal, production-style snippets. Adapt the predicates to your routes and error taxonomy.
JavaScript (Node/Browser)
// sentry.client.config.js (browser) or sentry.server.config.js (Node)
import * as Sentry from "@sentry/node"; // or "@sentry/browser"
Sentry.init({
dsn: process.env.SENTRY_DSN,
beforeSend(event, hint) {
// 1. Drop health-check noise
if (event.request?.url?.includes("/health")) {
return null;
}
// 2. Redact sensitive keys in request body
const redacted = redactKeys(event.request?.data, [
"password", "token", "secret", "api_key", "authorization"
]);
if (redacted) event.request.data = redacted;
// 3. Sample validation errors (keep 1%)
const isValidation = event.exception?.values?.[0]?.type === "ValidationError";
if (isValidation && Math.random() > 0.01) {
return null;
}
return event;
}
});
function redactKeys(obj, keys) {
if (!obj || typeof obj !== "object") return obj;
const clone = Array.isArray(obj) ? [...obj] : { ...obj };
for (const k of Object.keys(clone)) {
if (keys.some(s => k.toLowerCase().includes(s))) {
clone[k] = "[REDACTED]";
} else if (typeof clone[k] === "object") {
clone[k] = redactKeys(clone[k], keys);
}
}
return clone;
}
Python (Flask/Django/FastAPI)
# sentry_init.py
import sentry_sdk
import random
def before_send(event, hint):
# 1. Drop health-check noise
request = event.get("request", {})
if request.get("url", "").endswith("/health"):
return None
# 2. Redact sensitive keys
data = request.get("data")
if data:
event["request"]["data"] = _redact_keys(data, [
"password", "token", "secret", "api_key", "authorization"
])
# 3. Sample validation errors (keep 1%)
exc = event.get("exception", {}).get("values", [{}])[0]
if exc.get("type") == "ValidationError" and random.random() > 0.01:
return None
return event
def _redact_keys(obj, keys):
if not isinstance(obj, (dict, list)):
return obj
if isinstance(obj, list):
return [_redact_keys(v, keys) for v in obj]
return {
k: "[REDACTED]" if any(s in k.lower() for s in keys) else _redact_keys(v, keys)
for k, v in obj.items()
}
sentry_sdk.init(
dsn=os.getenv("SENTRY_DSN"),
before_send=before_send,
# other options...
)
Where to run: Place the init code at application startup, before routes are registered. Permissions: Only requires the Sentry DSN environment variable. Expected check: Trigger a test error on a health endpoint and a validation error; verify they don't appear in Sentry (or appear at the sampled rate). Risk: Overly broad URL matching may drop legitimate errors—scope predicates tightly.
Trade-offs and Limitations
- Synchronous execution: In the JavaScript and Python SDKs,
beforeSendblocks the event loop. A 5 ms filter adds 5 ms to every error capture. Profile if you process high error volumes. - Aggressive dropping hides bugs: A blanket
return nullforTypeErrorwill silence real regressions. Prefer sampling or scoped filters (route, error type, user tier). - SDK version drift: Return types and hint shapes change across major versions. Pin your SDK and test the filter after upgrades.
- Client-side only: Browser
beforeSendwon't stop events already captured bywindow.onerroror server middleware unless you also configure those layers.
Practical verification: After deploying, compare the Events Received graph in Sentry for the 24 hours before and after. A drop matching your filter's expected rate (e.g., 99% fewer validation errors) confirms it's working. If counts don't move, add temporary logging inside beforeSend to see which events reach it.
Next Steps
- Audit your top 10 event types in Sentry—identify candidates for dropping or sampling.
- Implement a focused
beforeSendin a staging environment with the redact-and-sample patterns above. - Write a unit test that calls your filter with a synthetic event and asserts the return value (event vs.
null). - Monitor quota usage for two weeks; adjust sampling rates based on actual volume.
The goal isn't zero events—it's high signal, controlled cost, and no secrets in the stream. beforeSend gives you that lever; use it precisely.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.