feat(server-side): serve GA via first-party server - #17323
Merged
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR adds an optional server-side GTM configuration (GTM_SERVER_URL) so Bedrock can load gtm.js from a first-party sGTM tagging server when enabled, while remaining a no-op when unset. (Review performed using the repository’s custom instructions/AGENTS.md guidance.)
Changes:
- Add
GTM_SERVER_URLsetting and plumb it into CSP directives (script-src,connect-src,img-src,frame-src) when configured. - Expose the configured tagging-server origin to the front-end via a
data-gtm-server-urlattribute on<html>(in both relevant base templates). - Update the GTM loader snippet to use the configured base URL, falling back to
https://www.googletagmanager.com.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| media/js/base/gtm/gtm-snippet.es6.js | Load gtm.js from a configurable base URL read from <html data-gtm-server-url>, with Google fallback. |
| bedrock/settings/base.py | Introduces GTM_SERVER_URL env var with basic normalization. |
| bedrock/settings/init.py | Adds GTM_SERVER_URL to CSP directive allowlists when set. |
| bedrock/firefox/templates/firefox/whatsnew/base-new-theme.html | Adds data-gtm-server-url to the page’s <html> tag when configured. |
| bedrock/base/templates/base-protocol.html | Adds data-gtm-server-url to the main base template’s <html> tag when configured. |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #17323 +/- ##
==========================================
- Coverage 82.95% 82.93% -0.02%
==========================================
Files 179 179
Lines 9362 9377 +15
==========================================
+ Hits 7766 7777 +11
- Misses 1596 1600 +4 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
stephendherrera
force-pushed
the
feat/ss-gtm-first-party
branch
from
August 3, 2026 18:25
eacab0f to
044bfef
Compare
stevejalim
reviewed
Aug 4, 2026
Follow-up to review on #17323. The previous guard only added a scheme when none was present, so http:// and protocol-relative values passed through untouched. http:// is mixed content on our https pages, and a protocol-relative value is not a valid CSP source expression, so the directive entry would be discarded and the script blocked. Normalize to an https:// origin unconditionally instead, and treat a scheme with no host as unset rather than letting it through as a truthy nonsense value.
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.
One-line summary
Add optional
GTM_SERVER_URLandGTM_SERVER_PATHsettings that loadgtm.jsfrom our own sGTM tagging server. Inert until the env vars are set per environment.Significant changes and points to review
bedrock/settings/base.py— two new settings, both empty-or-default so nothing changes until they're set:GTM_SERVER_URLvia_normalize_gtm_server_url(), which strips a trailing slash and prependshttps://to a scheme-less value.GTM_SERVER_PATHvia_normalize_gtm_server_path(), which is deliberately not normalized beyond adding a missing leading slash, because of the trailing-slash asymmetry above. Defaults to/gtm.js.bedrock/settings/__init__.py— whenGTM_SERVER_URLis set, the origin is added toscript-src,connect-src,img-srcandframe-src. All four are required.base-protocol.htmlandfirefox/whatsnew/base-new-theme.html, since the latter declares its own<html>tag rather than extending the base. Worth confirming there's no declaration I missed.gtm-snippet.es6.js— the<html>lookup is hoisted to a local since it's now needed three times.GTM_BASE_URLfalls back tohttps://www.googletagmanager.com, andGTM_SCRIPT_PATHis only consulted when a server URL is present, falling back to/gtm.js, because Google's CDN only ever serves from that path. Consent gating (GPC, DNT, EU opt-in, the/thanks/case) is untouched.Issue / Bugzilla link
https://mozilla-hub.atlassian.net/browse/WT-1536
Testing
Confirmed no-op while unset. Rendered
CONTENT_SECURITY_POLICY["DIRECTIVES"]inside thebedrock_testcontainer and diffed:mainwith the vars unset vs this branch with them unset identical, zero CSP change. Which also means no existing test can regress.GTM_SERVER_URL=https://gtm-dev.allizom.org/→ exactly four additions, one origin each intoconnect-src,frame-src,img-src,script-src, and nothing else. Trailing slash stripped as intended.gtm-dev.allizom.organd the fullhttps://gtm-dev.allizom.org/produce identical results, confirming the scheme coercion.With the vars unset, no
data-gtm-server-urlordata-gtm-server-pathattributes render,getAttributereturnsnull, and the snippet loads fromhttps://www.googletagmanager.com/gtm.jsexactly as before.Unit tests:
bedrock/base/tests/test_settings.pycovers both normalizers, including cases pinning the trailing slash so a future refactor can't strip it. 17 passing in that file.ruff check,ruff format --checkandprettier --checkare clean on all changed files.Confirmed against the dev tagging server: the custom path serves the real container (200, ~389 KB, correct container ID inside), and
/gtm.jskeeps serving alongside it, so the GTM-side and bedrock-side changes can land in either order with no breakage window.