Skip to main content
Browser monitoring is a beta AppSignal Labs feature. The @appsignal/browser package is published under the beta tag on npm, and its configuration and API may still change between beta releases. Share feedback in our Discord community.
Front-end telemetry sits closer to personal data than back-end telemetry does. URLs carry tokens, forms carry card numbers, and the text on a button can be an order total. AppSignal for Browser is conservative by default, and every remaining decision is yours to configure.

What is protected by default

Without any configuration:
  • Every query parameter is removed from every URL AppSignal records. ?token=abc never leaves the browser.
  • Request and response bodies are never recorded. There is no option to turn that on.
  • No cookies are read or written. The SDK stores a few appsignal_* values in localStorage and sessionStorage: random IDs for the session, the tab, and the visitor, the time of the last activity, and any tags you set. The visitor ID is a random value with nothing personal in it. It does stay on the device between visits, though, so treat it as an identifier and mention it in your privacy notice.
  • Console output is cut off after the first 200 characters.
  • Nothing is sent when active is false. The SDK does not change anything on the page, start any timers, or make any requests.

URLs and query parameters

privacy.queryParamsAllowlist lists the query parameters worth keeping. Everything else is removed. You can use * to match several names at once, so utm_* keeps every UTM parameter. The default list is empty, which removes all of them. This applies to every URL AppSignal records: the URLs in network and navigation breadcrumbs, the page an error happened on, the page the user came from, and the page a web vital belongs to.
With that setting: The last two rows are the important pair. Both URLs have something after a #, and they are treated differently on purpose. The third one looks like a query string after the #, so the same rules apply and the login token is removed. This is the case that matters: a token like that should never reach AppSignal. The fourth one looks like a route, which is how many single-page applications do their routing, so it is kept exactly as it is. A plain link to a section of a page, such as #pricing, is kept too.

Excluding network requests

privacy.networkBlocklist lists requests that should leave no breadcrumb. The request still happens as normal. It is only the record of it that is skipped. Patterns are matched against the host and the path of the URL. A single * matches one part of the path, and ** matches any number of parts.

Masking and blocking page elements

Two options control what gets recorded when a user selects something. Both take CSS selectors, and both apply to everything inside the element you name, so listing a container covers all of its contents. privacy.dom.maskText replaces the element’s text with [masked]. The breadcrumb is still recorded, so you keep the fact that someone selected it:
privacy.dom.blockElement records nothing at all. Use it for card forms, identity numbers, and anything that should never surface:
These two options only affect what is recorded when a user selects something. They do not touch error messages, network URLs, or console output. Use queryParamsAllowlist, networkBlocklist, and the two functions in the following section for those.

Filtering errors and breadcrumbs

Two functions let you inspect each error and each breadcrumb before AppSignal stores or sends anything. Neither can be async. beforeError runs once for every error. Return null to throw the error away, or change its fields to remove sensitive text from them. It is the one place to do both:
Returning null gets rid of the error completely. No breadcrumb is recorded for it, and it does not count towards the rate limits. beforeBreadcrumb runs once for every breadcrumb, as it is recorded. Return null to throw it away, or change its message and data:
beforeError cannot see breadcrumbs, because they are attached to the error after this function has approved it. Use beforeBreadcrumb to clean up breadcrumb content instead.
Neither function can be async. If beforeError returns a promise, AppSignal logs a console.error and throws the error away. Do any slow work before you call captureError, not inside these functions.
Nothing is collected until init() runs, so asking for consent first is a matter of not calling it until you have it:
destroy() stops collection straight away, sending whatever it had already gathered. It only affects the page the user is on. Store their decision yourself and check it before calling init(), or the next page they open starts collecting again.

What you can tell your users

Here is what AppSignal for Browser records about a visit:
  • The pages they opened, and how far down each one they scrolled.
  • What they selected, and where on the screen they selected it. Elements are recorded by their label, not their contents, unless the label happens to be the contents.
  • The requests the page made, and whether each one succeeded.
  • Warnings and errors the page logged to the console.
  • Timing measurements.
  • On errors, whatever tags you set.
The breadcrumbs page has the full list. Every batch of web vitals also carries some context about the visit: random IDs for the session, the tab, and the visitor, the page URL, the page they came from, the browser’s user agent, the size of their screen and window, their language, and their time zone. Where the browser makes them available, it also carries the connection type and how much memory the device has. It does not record anything typed into a form, the contents of any request or response, or cookies. The visitor ID is the only identifier the SDK makes up: a random value that means nothing on its own, stored on the device between visits. Anything that identifies a person by name, email, or account is there because your application sent it, through setTags() or addBreadcrumb().