Skip to main content
Record what people do with your extension, from inside your own extension.
This page is about analytics that your extension sends. For the data that the Extension.js command line tool itself reports, and how to turn it off, read Telemetry and privacy controls.

An analytics snippet from a CDN does not work

Manifest V3 blocks remotely hosted code. The vendor snippet that loads a tag manager or an analytics SDK from a content delivery network is exactly the pattern that the policy targets. Extension.js warns when an HTML page in your project references a remote script:
The build still succeeds and the tag stays in the output, so nothing stops you from shipping it. The browser then refuses to run the script, and no event is ever sent. Treat the warning as a defect. Two routes remain. Bundle an analytics library that ships as a package and works without loading more code, or call an HTTP endpoint yourself. The rest of this page covers the second route, which is the one that needs no third-party runtime at all.

Send events over the Measurement Protocol

Google Analytics 4 accepts events over plain HTTP. A request looks like this:
The client_id is yours to generate. Create one identifier per installation, store it, and reuse it:
While you are building the payload, send it to https://www.google-analytics.com/debug/mp/collect instead. That endpoint returns the validation messages for your request rather than recording it.

Send from the service worker

Run the request in the background service worker, or in an extension page. Those contexts carry the extension origin, and a host permission covers the call:
A content script runs under the host page’s origin instead, where host permissions do not apply and the page’s own rules do. Send a message to the background and let it make the call. Cross-origin requests has the full table and the message-passing pattern. If you declare your own content_security_policy, then connect-src has to list the analytics endpoint. A development session appends its own loopback entries to that directive, and never your endpoint. A missing entry therefore fails the same way in development and in production.

A bundled key is not a secret

An API secret that ships inside the extension is readable by anyone who installs it. Environment variables do not change this. Extension.js inlines every EXTENSION_PUBLIC_ value into the bundle at build time. That keeps the value out of your repository, and not out of your users’ hands.
That is acceptable for a write-only analytics key that you can rotate. When a credential must stay private, send the event to a small backend that you control, and keep the credential there. Environment variables covers the prefix and the file order.

Declare what you collect

Every store asks you to disclose data collection, and a Firefox build says so during the build:
Keep the declaration honest, and keep the payload small enough to match it. Page URLs, form values, and anything that identifies a person raise the review bar on every store.

Next steps