Skip to content

Stats: click/download tracking misses multisite /files/ and custom uploads paths (only matches /wp-content/uploads) #49478

@dd32

Description

@dd32

Impacted plugin

Jetpack

Quick summary

Note

AI-assisted investigation, reviewed before posting. Findings below were produced by an AI agent (Claude) querying a live Jetpack-connected site's Stats and reading the automattic/jetpack-stats tracker source. Reviewed before filing; please sanity-check the specifics.

Summary

The Stats client-side click tracker only treats same-host links as trackable file/download clicks when the URL contains the literal string /wp-content/uploads. Sites that relocate their uploads directory — most notably WordPress multisite installs that serve uploads from /files/ (the ms_files_rewriting mechanism), and any site using a custom UPLOADS constant or upload_path / upload_url_path option — therefore get no click/download tracking for their media files at all. Internal navigation is (correctly) ignored, but so are genuine file downloads, because they don't match the hardcoded path.

Where the behavior lives

Tracking_Pixel::enqueue_stats_script() (package automattic/jetpack-stats) enqueues the WP.com-served tracker https://stats.wp.com/e-{YEARWEEK}.js and bootstraps it with _stq.push(["clickTrackerInit", blog, post]). The tracker's click handler contains:

// a(e) === true when the link is same-host
if ( a(e) && !e.href.includes("/wp-content/uploads") ) return; // not tracked
...
window._stq.push(["click", { s:"2", u:e.href, r:..., b:blog, p:post }]); // c.gif beacon

So the heuristic is: track same-host links only if they look like a default-location upload. The path is hardcoded to /wp-content/uploads and does not account for relocated uploads.

Steps to reproduce

Steps to reproduce

  1. Use a Jetpack-connected multisite install (or any site with relocated uploads), where attachment URLs resolve to https://example.org/files/… rather than …/wp-content/uploads/….
  2. Add a normal download link to a media file, e.g. <a href="https://example.org/files/2024/01/kit.zip">Download</a>.
  3. Click it repeatedly from the front end (logged out).
  4. Check Stats → Clicks (or query WPCOM_Stats::get_clicks()).

What I expected

The /files/…/kit.zip clicks to be recorded, the same way /wp-content/uploads/…/kit.zip clicks are on a single-site default install.

What actually happens

Nothing is recorded for the /files/ link — the same-host guard short-circuits because the URL doesn't contain /wp-content/uploads. The download is invisible to Stats.

Site owner impact

Fewer than 20% of the total website/platform users

Severity

Minor

What other impact(s) does this issue have?

No revenue impact

If a workaround is available, please outline it here.

Real-world evidence

On a live Jetpack-connected multisite (blog serves uploads from /files/):

  • 7 existing PDF attachments (/files/2020/…/files/2022/…) appear in no Clicks report across day/week/month/year windows.
  • Confirmed same-host non-file URLs do otherwise surface in the Clicks report, so the report itself works — it's specifically the upload-path match that fails.
  • (Separately, the dedicated File Downloads report is unavailable on non-Simple sites — see Related — so Clicks is the only available channel, which makes this gap more impactful.)

Impact

Any multisite or relocated-uploads Jetpack site silently loses all automatic download/media-click tracking. WordPress multisite (/files/) is a first-class, officially supported configuration, so this is not an exotic edge case. There is no front-end workaround via the documented API; the only mitigation is manually pushing _stq.push(["click", {…}]) per link, which bypasses the guard but requires custom JS on every download link.

Suggested fix

Don't hardcode /wp-content/uploads. Derive the site's actual uploads path server-side (e.g. the path component of wp_get_upload_dir()['baseurl']) and pass it into the tracker config so the click handler can match the real upload location. Concretely, clickTrackerInit (or the stats array) could carry an uploads_path (or a small list, to cover both the default and a relocated path), and the guard would test against that instead of a fixed string. This keeps internal-navigation links untracked while correctly capturing file downloads regardless of where uploads live.

Related

Environment

  • Jetpack Stats package: automattic/jetpack-stats (Tracking_Pixel), tracker stats.wp.com/e-*.js.
  • Site type: Jetpack-connected WordPress multisite, uploads served from /files/.

Platform (Simple and/or Atomic)

Self-hosted

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugWhen a feature is broken and / or not performing as intendedNeeds triageTicket needs to be triaged

    Type

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions