Choosing Where to Drop Sentry Events: SDK Sampling vs. Server-Side Filters
Decide where to drop Sentry events — SDK sampling, beforeSend, ignoreErrors, or server-side filters — with a comparison table, trade-offs, and a validated Node/browser config example.
01 Oct 2025, 23:30 UTC

Your Sentry quota is finite, and most JavaScript apps generate far more events than they need to keep: health-check transactions, browser-extension errors, and the same benign exception fired thousands of times. The decision is not whether to drop events but where — in the SDK before anything leaves the process, or server-side in Sentry's project settings. Each location has different costs, reversibility, and blind spots.
The decision and its constraints
Three constraints drive the choice:
- Cost of capture. SDK-side options stop events before they hit the network, saving bandwidth and quota. Server-side filters save quota but the client still pays to capture and transmit the event.
- Speed of change. Server-side settings (inbound filters, spike protection) take effect immediately without a deploy. SDK options ship with your code.
- Visibility. Anything dropped server-side is invisible — you cannot inspect what a filter threw away. SDK-side drops can at least be logged locally before they're discarded.
Comparing the supported options
| Option | Where it runs | Applies to | Reduces client overhead? | Change without deploy? |
|---|---|---|---|---|
tracesSampleRate | SDK | Transactions | Yes | No |
tracesSampler | SDK | Transactions | Yes | No |
ignoreErrors | SDK | Errors | Yes (skipped early) | No |
beforeSend | SDK | Errors (per event) | Partially (runs after capture) | No |
| Inbound filters | Sentry server | Errors | No | Yes |
| Spike protection | Sentry server | All event types | No | Yes |
Exact option names and available filters vary by SDK major version and Sentry plan, so confirm against your installed SDK's documentation. The behavior below reflects long-standing JavaScript SDK conventions.
Trade-offs that actually matter
tracesSampleRate vs. tracesSampler
tracesSampleRate: 0.1 keeps a flat 10% of all transactions. That is fine for uniform traffic, but most apps are heterogeneous: you want 0% of /healthz and 100% of checkout. tracesSampler receives a samplingContext (transaction name, URL, and the parent sampling decision) so you can decide per transaction. Prefer it whenever route value differs.
One critical detail: respect samplingContext.parentSampled. In distributed tracing, an upstream service may have already decided to keep a trace. If your sampler ignores that and returns 0, you get broken, partial traces across services.
ignoreErrors vs. beforeSend
ignoreErrors takes strings or regexes matched against the exception message. It is the cheap, easy-to-audit way to permanently exclude noise like ResizeObserver loop limit exceeded. Use beforeSend when you need logic: scrubbing PII, dropping errors only on certain pages, or mutating tags. The risk is that beforeSend returning null drops the event silently — a bug in that callback can suppress all errors, so keep it minimal and test it.
Server-side filters: fast but blind
Inbound filters (browser-extension errors, localhost, legacy browsers) and spike protection are reversible and instant, which makes them ideal as a first response to a quota emergency. The downsides: discarded events are unrecoverable, they don't reduce client-side work, and spike protection can drop events during a genuine incident — exactly when you need visibility most. Treat spike protection as a quota guardrail, not a noise filter.
Note also that error and transaction quotas are managed separately; sampling transactions does not proportionally reduce the errors attached to them.
Concrete implementation: route-aware sampling plus a tested drop rule
In your Node or browser app's Sentry init (runs at process start; no special permissions needed, but it must execute before other imports that you want instrumented):
Sentry.init({
dsn: process.env.SENTRY_DSN,
tracesSampler(samplingContext) {
// Respect upstream decisions to keep traces whole
if (samplingContext.parentSampled !== undefined) {
return samplingContext.parentSampled;
}
const name = samplingContext.name || "";
if (name.includes("/healthz")) return 0;
if (name.includes("/checkout")) return 1.0;
return 0.1;
},
ignoreErrors: [/^ResizeObserver loop/],
beforeSend(event, hint) {
const err = hint.originalException;
if (err && err.isBenign === true) return null; // known-noise marker
return event;
},
});Placeholders to replace: your DSN, the route names, and whatever marker your codebase uses for known-benign errors. Risk: a typo in the beforeSend condition can drop everything — keep it to one narrow condition.
Validate before production
- In staging, temporarily add
console.log(samplingContext.name, decision)insidetracesSampler, generate traffic across several routes, and confirm logged decisions match intent. - Trigger a test error matching your drop rule and a control error that should be kept. Confirm the dropped one never appears in the Sentry issues stream while the control does.
- After a day of production traffic, check the project's stats/usage view in Sentry and confirm dropped-event counts roughly align with your configured rates and filters.
A sensible default rollout: enable server-side inbound filters first (instant, reversible), then ship SDK-side sampling for the high-volume routes, and only add beforeSend logic where a static filter genuinely can't express the rule.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.