seo/analytics: real GA attribution, tight robots.txt, no build-date lastmod, GA only on velxio.dev
- trackClickCTA sent the originating page as the event parameter `source`, a reserved traffic-source name in GA4: every landing CTA click rewrote the session's acquisition to landing / arduino-simulator / rpi-simulator with medium (not set), which put 16% of sessions in the Unassigned channel. Renamed to cta_source (register it as a custom dimension in GA4 if the click_cta report should keep breaking down by page). - robots.txt is one group with no blank lines (some parsers end a group at a blank line, orphaning the Disallow rules); /login, /register and /admin are no longer disallowed — they carry noindex, and a page must be crawlable for that tag to take effect. /project/ and /api/ stay out. - The static sitemap no longer stamps every URL with the build date as <lastmod>; an all-identical, ever-changing lastmod is discarded as unreliable, so omitting it is the honest option. - The GA4 tag loads only on velxio.dev / www.velxio.dev / tauri.localhost: self-hosted copies of the app (there are >180 hostnames reporting into the property) no longer pollute velxio.dev's analytics.
This commit is contained in:
parent
189ba355a9
commit
1b735c9c30
|
|
@ -20,15 +20,26 @@
|
|||
|
||||
<!-- ═══════════════════════════════════════════════
|
||||
GOOGLE ANALYTICS 4
|
||||
Replace G-XXXXXXXXXX with your real Measurement ID
|
||||
(analytics.google.com → Admin → Data Streams → Measurement ID)
|
||||
The Measurement ID below is velxio.dev's, so the tag only loads on
|
||||
velxio.dev hostnames (plus the Velxio Desktop shell). Self-hosted
|
||||
copies of the app send nothing — swap in your own ID AND hostname
|
||||
(analytics.google.com → Admin → Data Streams → Measurement ID) if you
|
||||
want analytics on your deployment.
|
||||
═══════════════════════════════════════════════ -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-HPD8HTV3TG"></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
gtag('config', 'G-HPD8HTV3TG');
|
||||
(function () {
|
||||
var h = location.hostname;
|
||||
if (h !== 'velxio.dev' && h !== 'www.velxio.dev' && h !== 'tauri.localhost') return;
|
||||
var s = document.createElement('script');
|
||||
s.async = true;
|
||||
s.src = 'https://www.googletagmanager.com/gtag/js?id=G-HPD8HTV3TG';
|
||||
document.head.appendChild(s);
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
window.gtag = gtag;
|
||||
gtag('js', new Date());
|
||||
gtag('config', 'G-HPD8HTV3TG');
|
||||
})();
|
||||
</script>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════
|
||||
|
|
|
|||
|
|
@ -1,17 +1,15 @@
|
|||
# One group, no blank lines inside it: some crawlers treat a blank line as
|
||||
# the end of a group, which left the Disallow rules below orphaned.
|
||||
User-agent: *
|
||||
Allow: /
|
||||
|
||||
# Auth pages — no useful content for search engines
|
||||
Disallow: /login
|
||||
Disallow: /register
|
||||
|
||||
# UUID-based duplicate of /:username/:slug — keep crawlers on the canonical
|
||||
# user-friendly URL instead of the opaque /project/<uuid> alternate.
|
||||
# UUID-based duplicate of /:username/:slug (the canonical URL); crawling it
|
||||
# is wasted budget.
|
||||
Disallow: /project/
|
||||
Disallow: /admin
|
||||
|
||||
# Prevent crawling of API endpoints
|
||||
# API endpoints are not pages.
|
||||
Disallow: /api/
|
||||
# Auth and admin pages are deliberately NOT disallowed here: they carry a
|
||||
# noindex meta tag, and a page has to be crawlable for that tag to be seen —
|
||||
# a disallowed URL can still be indexed from links, title-less.
|
||||
|
||||
# Sitemaps (static build-time routes + dynamic public projects + the docs
|
||||
# portal, which is a separate Astro build shipping its own sitemap index)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -76,11 +76,16 @@ if (process.env.VITE_PRO_BUILD && process.env.PRO_OVERLAY_PATH) {
|
|||
|
||||
const exampleUrls = exampleIds.map((id) => ({
|
||||
loc: `${DOMAIN}/examples/${id}`,
|
||||
lastmod: TODAY,
|
||||
changefreq: 'monthly',
|
||||
priority: 0.6,
|
||||
}));
|
||||
|
||||
// No <lastmod>: this runs at build time with no per-page history to draw
|
||||
// on, so every URL used to carry the build date. A lastmod that changes on
|
||||
// every deploy for every page is provably unreliable and search engines
|
||||
// discard the whole signal; omitting it is the honest option (the projects
|
||||
// sitemap, which has real updated_at timestamps, keeps its lastmod).
|
||||
|
||||
// Every <loc> carries a trailing slash. nginx serves the prerendered route
|
||||
// files as `<route>/index.html` and 301-redirects the slash-less form to add
|
||||
// the slash, so a slash-less sitemap URL is fetched as a redirect and filed
|
||||
|
|
@ -94,7 +99,6 @@ ${indexable
|
|||
(r) => `
|
||||
<url>
|
||||
<loc>${DOMAIN}${r.path}${r.path.endsWith('/') ? '' : '/'}</loc>
|
||||
<lastmod>${TODAY}</lastmod>
|
||||
<changefreq>${r.changefreq ?? 'monthly'}</changefreq>
|
||||
<priority>${r.priority ?? 0.5}</priority>
|
||||
</url>`
|
||||
|
|
@ -105,7 +109,6 @@ ${exampleUrls
|
|||
(u) => `
|
||||
<url>
|
||||
<loc>${u.loc}/</loc>
|
||||
<lastmod>${u.lastmod}</lastmod>
|
||||
<changefreq>${u.changefreq}</changefreq>
|
||||
<priority>${u.priority}</priority>
|
||||
</url>`
|
||||
|
|
|
|||
|
|
@ -130,9 +130,17 @@ export function trackProductPageClick(componentId: string, brand?: string, href?
|
|||
|
||||
// ── CTA / Conversion ────────────────────────────────────────────────────────
|
||||
|
||||
/** Fired when a user clicks a CTA button on a landing/SEO page. */
|
||||
/**
|
||||
* Fired when a user clicks a CTA button on a landing/SEO page.
|
||||
*
|
||||
* The originating page is sent as `cta_source`, NOT `source`: `source`
|
||||
* (like `medium` and `campaign`) is a reserved traffic-source parameter in
|
||||
* GA4, so every CTA click was rewriting the session's acquisition source to
|
||||
* "landing" / "arduino-simulator" / "rpi-simulator" with medium "(not set)"
|
||||
* — 16% of sessions landed in the Unassigned channel with no real origin.
|
||||
*/
|
||||
export function trackClickCTA(source: string, destination: string): void {
|
||||
fireEvent('click_cta', { event_category: 'conversion', source, destination });
|
||||
fireEvent('click_cta', { event_category: 'conversion', cta_source: source, destination });
|
||||
}
|
||||
|
||||
// ── Library Manager ─────────────────────────────────────────────────────────
|
||||
|
|
|
|||
Loading…
Reference in New Issue