Is Similarweb - Website Traffic, AI Traffic & SEO Checker safe?

High risk

Similarweb is high risk. We observed a Mixpanel event from the Similarweb extension via mpps.similarweb.com/track. The decoded event held the embedded Mixpanel token, a persistent device ID, the current welcome-page URL, and context for a button interaction.…

Similarwebv6.12.22Chrome Web Store
75Risk

AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.

Publishers can request a review.

Findings

SeverityMEDIUM
ClassUNWANTED
TypeUnexpected
CWECWE-359
SourceAI SANDBOX

Similarweb sends extension usage events to Mixpanel

We observed a Mixpanel event from the Similarweb extension via mpps.similarweb.com/track.

The decoded event held the embedded Mixpanel token, a persistent device ID, the current welcome-page URL, and context for a button interaction.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You install or use the Similarweb extension.

The observed traffic came from an extension button interaction during the extension flow.

The extension did this

The extension sends a Mixpanel analytics event with a persistent device identifier.

The observed event was routed through mpps.similarweb.com/track and included the current welcome-page URL.

02EvidenceNETWORK CAPTURE
Captured request
GEThttps://mpps.similarweb.com/track/
Observed request decoded to event 'button' with token 7ccb86f5c2939026a4b5de83b5971ed9, distinct_id '$device:61b6a35c-f2b2-46e5-a11c-074a4a6b28b9', and current_url 'https://www.similarweb.com/corp/extension-welcome-chrome/'. The stored capture did not retain a request body.
03EvidenceFIELD TABLE
Decoded fields from the observed Mixpanel event
FieldValueWhy it matters
Event name
buttonShows the type of extension interaction that was recorded.
Persistent device ID
$device:61b6a35c-f2b2-46e5-a11c-074a4a6b28b9Lets analytics connect extension events from the same browser across sessions.
Current page URL
https://www.similarweb.com/corp/extension-welcome-chrome/Shows which page was open when the extension recorded the event.
Mixpanel project token
7ccb86f5c2939026a4b5de83b5971ed9Identifies the analytics project that received the event.
04EvidenceCODE COMPARE
The code that does this

The shipped background script defines the Mixpanel endpoint and token

What it actually does
const M = {
    FETCH_DATA: "fetchData",
    FETCH_IDENTITY: "fetchIdentity",
    IS_INSTALLED: "isInstalled",
    OPEN_LINK: "openLink",
    OPEN_OPTIONS_PAGE: "openOptionsPage",
    OPT_IN: "enableAutoIcon",
    OPT_OUT: "disableAutoIcon",
    TOGGLE_POPUP: "togglePopup",
    TOGGLE_POPUP_FROM_IFRAME: "togglePopupFromIframe"
},
F = {
    ENDPOINTS: {
        TRACK: "https://api.mixpanel.com/track"
    },
    TOKENS: {
        SANDBOX: "2a36d6f836516f4677bde7726425a84d",
        PRODUCTION: "7ccb86f5c2939026a4b5de83b5971ed9"
    },
    SITE_TYPE: "similarweb extension"
};
05EvidenceCODE COMPARE
The code that does this

Install and usage events are sent through Mixpanel tracking helpers

What it actually does
Background tracking helperbackground/background.js
xe = function(e, t, r) {
    var n = chrome.runtime.getManifest(),
        i = n.version,
        a = ye(),
        o = Se(t, r);
    fetch(F.ENDPOINTS.TRACK, {
        method: "POST",
        headers: {
            "Content-Type": "application/json"
        },
        body: JSON.stringify([{
            event: e,
            properties: ke(ke({}, o), {}, {
                browser: a,
                version: i,
                token: b ? F.TOKENS.SANDBOX : F.TOKENS.PRODUCTION
            })
        }])
    }).then((function() {
        X("Mixpanel event:", ke({ category: e }, o));
    })).catch(Y);
};
Install event triggerbackground/background.js
Ce = function() {
    var e = t(i().mark((function e(t) {
        var r, n;
        return i().wrap((function(e) {
            for (;;) switch (e.prev = e.next) {
                case 0:
                    r = t.reason, e.t0 = r, e.next = "install" === e.t0 ? 4 : "update" === e.t0 ? 8 : 15;
                    break;
                case 4:
                    return e.next = 6, chrome.tabs.create({ url: Ee() });
                case 6:
                    return xe("background", "install"), e.abrupt("break", 16);
                case 8:
                    return e.next = 10, N(R.IS_LIMIT_ANNOUNCED);
                case 10:
                    if (n = e.sent, n) { e.next = 14; break; }
                    return e.next = 14, B(R.IS_LIMIT_ANNOUNCED, !1);
                case 14:
                case 15:
                    return e.abrupt("break", 16);
                case 16:
                case "end":
                    return e.stop();
            }
        }), e);
    })));
    return function(t) { return e.apply(this, arguments); };
}();
Panel Mixpanel event helperpanel/panel.js
Ig = function(t, e, i) {
    var n = jg(e, i);
    Tg().track(t, n), Vp("Mixpanel event:", Pg({
        category: t
    }, n));
};
06EvidenceTHIRD PARTY LIST
Analytics destinations in code and traffic
  • api.mixpanel.com

    Endpoint configured in the shipped background tracking helper for Mixpanel events.

  • mpps.similarweb.com

    Observed proxy endpoint that received the decoded Mixpanel event during traffic capture.

  • api-js.mixpanel.com

    Default API host inside the bundled Mixpanel JavaScript SDK used by the panel.

SeverityMEDIUM
ClassUNWANTED
TypeUnexpected
CWECWE-829
SourceAI SANDBOX

Similarweb loads a remote configuration file from an S3 bucket at startup

On startup Similarweb's worker fetches JSON from an S3 bucket, not similarweb.com, with no signature/schema check, parsed into feature toggles and rewrite rules applied before URLs reach Similarweb.

Sessions returned only analytics toggles.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You start Chrome, or install the extension, with Similarweb enabled.

No page visit, click, or interaction with the extension is needed.

The extension did this

The extension downloads a settings file from an Amazon S3 bucket and saves it into your synced Chrome storage.

The file it receives decides which analytics systems run and, through a second code path, how the URLs of pages you visit are rewritten before being sent to Similarweb.

02EvidenceNETWORK CAPTURE
Captured request
GEThttps://sw-extension.s3.amazonaws.com/config.json?v=1788051448413
HTTP 200. Response body captured verbatim: { "mixpanel": { "enabled": true, "config": { "persistence": "localStorage" } }, "matomo": { "enabled": true } }
03EvidenceSTORAGE DUMP
What's stored on your device

The S3 file is kept as received. It's written to synced storage, not local, so it travels to every Chrome profile you're signed into.

Locationchrome.storage.sync, key 'config'
Contents
{
  "matomo": {
    "enabled": true
  },
  "mixpanel": {
    "config": {
      "persistence": "localStorage"
    },
    "enabled": true
  }
}
04EvidenceCODE COMPARE
The code that does this

Startup fetch and persistence, from the extension's shipping background bundle.

What it actually does
The config endpoint constantbackground/background.js (deobfuscated)
const ENDPOINTS = {
  CONFIG:      "https://sw-extension.s3.amazonaws.com/config.json",
  DATA:        "https://data.similarweb.com/api/v1/data",
  GLOBAL_RANK: "https://rank.similarweb.com/api/v1/global",
  IDENTITY:    "https://data.similarweb.com/api/v1/identity"
};

const STORAGE_KEYS = {
  CONFIG: "config",
  // ...
};
The fetch-and-store routinebackground/background.js (deobfuscated)
async function fetchRemoteConfig() {
  try {
    const response = await fetch(`${ENDPOINTS.CONFIG}?v=${Date.now()}`);
    const config   = await response.json();
    await storageSet(STORAGE_KEYS.CONFIG, config);   // chrome.storage.sync.set({ config })
  } catch (err) {
    logError(err);
  }
}
Called from the top-level startup routinebackground/background.js (deobfuscated)
(async function startup() {
  try {
    initA();
    initB();
    installIdentityHeaderListener();
    await fetchRemoteConfig();
  } catch (err) {
    logError(err);
  }
})();
05EvidenceCODE COMPARE
The code that does this

How the stored configuration is consumed: settings loader and URL-rewriting rule builder.

What it actually does
Settings applied from a response header on the data endpointbackground/background.js (deobfuscated)
fetch(this.dataEndpoint, { method, headers, body }).then(response => {
  const sessionHeader = response.headers.get("x-session-id");
  if (sessionHeader) {
    try {
      const settings = JSON.parse(atob(sessionHeader));
      runHook.setSettings(settings);   // replaces the live settings object
    } catch (e) {}
  }
  // ...
});
The rule builder that consumes config.databackground/background.js (deobfuscated)
createGetClasses(pii) {
  if (typeof pii === "undefined") return;

  // config.data is either a base64-encoded JSON string or a plain object
  const rules = typeof pii.data === "string"
    ? JSON.parse(atob(pii.data || "e30="))
    : pii.data;

  // "urlparams" entries are "keyRegex=valueRegex[=type]" triples
  const urlparams_m = {};
  Object.keys(rules.urlparams || {}).forEach(name => {
    const parts = rules.urlparams[name].split("=");
    if (parts.length === 2 || parts.length === 3) {
      urlparams_m[name] = { kr: parts[0], vr: parts[1], t: parts[2] };
    }
  });
  rules.urlparams_m = urlparams_m;

  const handlers = [];
  try {
    if (typeof rules.sitewhitelist === "string")
      handlers.push(new SiteWhitelistHandler(true, rules.sitewhitelist));
    if (typeof rules.blacklist === "object" && Object.keys(rules.blacklist).length)
      handlers.push(new BlacklistHandler(true, rules.blacklist));
    if (typeof rules.paramwhitelist === "string"
        && typeof rules.urlparams_m === "object"
        && typeof rules.paths === "object"
        && Object.keys(rules.paths).length)
      handlers.push(new ParamWhitelistHandler(false, rules.paramwhitelist, rules.urlparams_m, rules.paths));
  } catch (e) {}

  this.holdings_list = handlers;   // applied to URLs before they are sent
}
06EvidenceTHIRD PARTY LIST
Hosts hardcoded in the background bundle's endpoint table.
  • sw-extension.s3.amazonaws.com

    S3 bucket serving config.json, fetched every worker start and applied with no integrity checks. Not similarweb.com; the bucket's own access control governs what the extension gets.

  • data.similarweb.com

    Similarweb's data/identity API. Its x-session-id header decodes to base64 JSON fed into the same settings loader, a second server-controlled config channel.

  • rank.similarweb.com

    Similarweb's global rank API, queried for site metrics after URLs have been processed by the configured rewriting rules.

07EvidencePLAIN NOTE
What we observed, and what we did not

The startup fetch and the storage write are confirmed: two independent dynamic-analysis sessions each captured the background service worker performing `GET https://sw-extension.s3.amazonaws.com/config.json`, and the post-session storage snapshot held the exact response body under `chrome.storage.sync['config']`.

The configuration served at the time of testing contained only Mixpanel and Matomo feature toggles. The `urlparams`, `sitewhitelist`, `blacklist`, `paramwhitelist` and `paths` fields that the rule builder is written to consume were not present in that response, so we did not observe URL-rewriting rules being delivered. Those code paths are present in the shipping bundle and are reached from the same stored `config` object; what they do on a given day depends on the document the bucket returns at that moment.

Our write-ups

Updated 21 September 2026hoklmmgfnpapgjgcpechhaamimifchmp