← back to Prestige Car Wash
Analytics: GA4 gtag injection on customer pages, gated on GA_MEASUREMENT_ID (inert until set, format-validated, admin untracked)
10e7bf17ce3fed0181ee80d481e857625ebe4bec · 2026-07-26 16:38:48 -0700 · steve@designerwallcoverings.com
Files touched
Diff
commit 10e7bf17ce3fed0181ee80d481e857625ebe4bec
Author: steve@designerwallcoverings.com <steve@designerwallcoverings.com>
Date: Sun Jul 26 16:38:48 2026 -0700
Analytics: GA4 gtag injection on customer pages, gated on GA_MEASUREMENT_ID (inert until set, format-validated, admin untracked)
---
server.js | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/server.js b/server.js
index 4963f5a..fc44e82 100644
--- a/server.js
+++ b/server.js
@@ -473,11 +473,21 @@ function buildSocialTags(html, pagePath) {
// Built fresh per request (sync file read + one string replace = microseconds) so the
// injected JSON-LD/OG NEVER drift from live places.json — a stale NAP is the exact thing
// Google penalizes, which would defeat the point of injecting it. (contrarian gate, 2026-07-26)
+// GA4 gtag snippet — injected on the customer pages ONLY when GA_MEASUREMENT_ID is set
+// (a valid G-XXXX id). No-op until then, so this is safe to ship before the id exists.
+// Admin (internal) is intentionally NOT tracked as customer traffic.
+function gaSnippet() {
+ const id = process.env.GA_MEASUREMENT_ID;
+ if (!id || !/^G-[A-Z0-9]{6,}$/.test(id)) return '';
+ return `\n<script async src="https://www.googletagmanager.com/gtag/js?id=${id}"></script>` +
+ `\n<script>window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}gtag('js',new Date());gtag('config','${id}');</script>`;
+}
function pageHtml(file, pagePath, ld) {
const html = fs.readFileSync(path.join(__dirname, 'public', file), 'utf8');
let inject = buildSocialTags(html, pagePath);
// escape "<" so the JSON can't break out of the <script> tag
if (ld) inject += `\n<script type="application/ld+json">${JSON.stringify(ld).replace(/</g, '\\u003c')}</script>`;
+ inject += gaSnippet();
return html.replace('</head>', inject + '\n</head>');
}
const sendPage = (res, file, pagePath, ld) => {
← aa47ccf SEO: broaden OG/Twitter + per-page canonical to /services +
·
back to Prestige Car Wash
·
chore: v0.3.1 (session close — LHF/SWOT/Slideshow) 39653bd →