Add setting to strip tracking parameters from links - #14970
Open
kees wants to merge 1 commit into
Open
Conversation
kees
force-pushed
the
feature/strip-tracking-params
branch
from
September 1, 2026 17:57
1f87be0 to
5cca6c5
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
First time contributor checklist
Contributor checklist
Fixes #1234syntaxDescription
Links shared from Facebook, Instagram, YouTube and similar platforms carry per-share tracking
tokens: e.g.
fbclid,igshid, YouTube'ssi, X'ss/t, the wholeutm_*family. These are notgeneric analytics: a
siorttoken is minted for a specific share by a specific account, sowhen the recipient taps the link, the destination site learns that those two people are in contact
and that the link travelled between them. Signal protects the message but clicking the link exposes
the relationship to a third party anyway.
This adds a Privacy setting (on by default) that removes known tracking parameters from a link
at the moment it is opened, copied, or fetched for a preview.
Message content is never modified
This is the central design constraint, and it is what distinguishes this from
#9125 (see below).
Text that was sent to you is rendered exactly as the sender wrote it. Nothing rewrites a
message body, inbound or outbound. What changes is only:
The reasoning:
would make quoting, search, and "what did they actually send me?" unreliable, and would mean the
rendered text no longer matches what was received.
LinkPreview.urlmust appear verbatim in themessage body or every recipient discards the preview
(
DataMessageProcessor.getLinkPreviews→presentInBody). Cleaning the attached preview URLtherefore requires also editing the body the user typed. This PR sidesteps that entirely: only
the sender's own preview fetch is cleaned, so the preview still renders for recipients.
Because the visible text is left alone, the displayed URL and the tap target can differ. That
is safe here: the cleaned URL is rebuilt with
HttpUrl.newBuilder(), so scheme, host, port,path and fragment are carried over untouched and only query parameters can change. A
cleaned link is always the same destination with less attached, never a different one. There
is a test asserting exactly that (
never changes anything but the query), because it isthe property the whole design rests on.
Parameter selection
Two tiers:
utm_*prefix family,fbclid,gclid/gclsrc/dclid/gbraid/wbraid,msclkid,twclid,ttclid,yclid,_openstat,igshid,mc_cid/mc_eid,mkt_tok,_hsencand similar. Every one is a click/campaign identifier thatattributes a visit; none select what the URL resolves to.
covered:
is/si/featureon YouTube,sion Spotify,mibextidon Facebook,s/tontwitter.com and x.com.
tis a legitimate timestamp parameter on plenty of other sites, soit is never touched off those hosts.
A URL that parses to nothing strippable is returned as the same instance, so links that need no
cleaning are never re-encoded or normalised as a side effect.
Prior art
There is no open issues or PRs for this. There are two relevant discussions:
open since 2020, 22 posts / 125 likes. Post No max. thread size setting #23 independently identifies the
siandtparameters as "an extremely invasive and powerful way to map an individual's network of
contacts".
closed by the stale bot in 2022 without a review on the merits. It cleaned links arriving through
ShareActivity, i.e. it rewrote the text being shared. This new PR is deliberately the complement:it never touches text, and instead cleans at the point of use, which also covers links that were
typed, pasted, or received rather than shared in.
@cody-signal noted on that PR in 2023 that "this sort of thing has been brought up internally a
couple times and is gaining popularity in other products", and pointed the discussion to the forum.
On "The answer is not more options"
The contributing guide's first principle argues against adding new settings.
The principle's neighbours seem to explain the rationale: "The user doesn't know what a key is",
"There are no power users", etc. It is aimed at options that expose people to
cryptographic or protocol concepts they should never have to reason about, and at capability handed
to self-identified experts. "Remove link tracking" is not that. It is a phrase users already meet
in Firefox, Safari and Brave, all of which ship this behaviour, and understanding it requires no
model of anything Signal does internally.
There is also direct precedent in the same screen area: Generate link previews is already a
user-facing toggle, and it exists for the same reason: the feature discloses something to a third
party, and some users want to decide about that disclosure. This setting is the same category of
control over the same category of disclosure.
There is a failure mode where some site treats a listed global parameter for another purpose.
The host-scoped tier exists to make that unlikely, but a user-recoverable solution (the setting)
provides a way around this in a pinch.
That said, if folks disagree, the setting is trivial to remove. I am happy to push that version instead,
and would rather land the protection without the toggle than not land it.
The setting defaults to on for new and existing installs. This too, I'm fine
switching this to "off by default"; I'd rather have the feature available than not. :)
Scope
Stripping happens for:
Deliberately excluded:
StoryContextMenu/MediaOverviewContextMenu, which handtext/plainto another app rather than explicitly opening a link.Testing
TrackingParametersTest, covering each parameter tier, case-insensitivematching, valueless and repeated parameters, parameter order preservation, fragment/port/path
preservation, non-HTTP and unparseable input, and the "only the query changes" invariant.
(
./gradlew :Signal-Android:testPlayProdDebugUnitTest)../gradlew :Signal-Android:ktlintCheck :fast-lint:fastLint checkStopshipclean../gradlew formatproduces no diff.playStagingDebugbuild against thestaging environment: the setting is present and on for a fresh install, and links behave as
described above when enabled.