Skip to content

The Foreign Hostname (Why I Check Where My Analytics Data Came From)

CONVERSION TRACKING
The Foreign Hostname (Why I Check Where My Analytics Data Came From)
Conner Crowe

On 11 August I opened the analytics project behind connercrowe.com and found four page paths that have never existed on this site. They had logged about 136 pageviews over the trailing ninety days, filed in the same project as my blog posts and my case studies. They came from a pitch mockup I had deployed on a separate domain, which shipped carrying the same public analytics key as the site.

Quick Take

An analytics property is scoped to a key, not to a site, and that key is public by design. Any page that carries it writes into the property, and the hostname is recorded on every event without anything in the default view marking it as foreign. So the page list in your analytics is not an inventory of your site. It is an inventory of everywhere your key is installed, and I learned the difference because an audit had already built a recommendation on four pages that were not part of the site it was auditing.

The key is public on purpose

PostHog documents the project key, the one starting phc_, as a public client-side key that is safe to expose in web and mobile code. It cannot reach your stored data; it sends events and evaluates feature flags, and that is the whole of it. GA4’s measurement ID is the same shape of thing, sitting in the page source of every site that uses it. Neither is a secret and neither can be, because the browser has to send the event and the browser holds nothing the reader cannot open and read.

What follows from that is the part I had not thought through. If the key is the credential, the origin is not. The hostname rides along as a property on the event, recorded rather than enforced. A browser collection endpoint holding a valid key has no reason to check whether the page that sent it belongs to you, and does not.

The vendors do gate the server-side path, which makes the contrast worth seeing. GA4’s Measurement Protocol requires a private api_secret alongside the measurement ID, and Google’s own documentation says to keep it out of client-side code because exposing it lets outside parties post spam into the property. The server door has a lock on it. The browser door is the one standing open, and it has to be, because that is where the measurement happens.

How it reached my own reports

I build navigable mockups to pitch website rebuilds. Each goes up as its own Cloudflare Pages project on its own domain, and one of them started life as a copy of a layout that already carried my analytics snippet. Five of its pages shipped with the key. Four show up in reporting.

Then I ran a content audit on my own site, and the audit read the analytics project as the record of what exists on connercrowe.com. It found four thin pages with low engagement, correctly noted they were weak, and produced a consolidation recommendation for them. Every step of that reasoning was sound and the premise underneath it was false. Those pages were never part of the site the audit was about.

The 136 pageviews are the trivial part. The audit conclusion is the expensive part, and I found it only because I went looking for the pages in my own repository and could not find them.

Where this hides on a Shopify brand

Pitch mockups are my version of the problem. They are not the common one. A brand accumulates the same foreign hostnames through ordinary work: a preview theme shared for review, a staging store, a myshopify.com domain still serving the live theme alongside the custom domain, a wholesale or trade portal on a subdomain, a regional storefront, a landing page a paid-media vendor built and deployed somewhere you never see. Each is a page carrying your key on a hostname that is not the one you think you are measuring, and each contributes sessions to the same property.

The myshopify.com case is the one worth checking first, because it costs nothing to have and it is on by default.

The check, and the fix that follows it

The check is a hostname breakdown, not a code search. Open the property, break pageviews out by hostname, and read the list. That is what surfaced this, and it is the only step that works regardless of where the offending page is deployed or who deployed it. A local code search finds nothing if the project was a direct upload rather than a repository, if it has since been deleted from disk, or if the key sits in a file type outside whatever pattern you searched.

Once the hostname tells you a foreign property exists, the code search tells you which one, and the fix is to strip the snippet from the source and from the built output, redeploy, then fetch each affected page and confirm the string is gone. A stale build directory will republish what you deleted from source.

# Find which local project shipped the key.
grep -rl "phc_" ~/Projects --include="*.html" --include="*.js" --include="*.astro"

# Then confirm every page that carried it, the root included.
for p in / /page-one/ /page-two/; do
  echo -n "$p "
  curl -s "https://your-mockup.pages.dev$p" | grep -c "phc_" || true   # expect 0
done

The reporting side does not clean up after itself. Events already collected stay collected, so every report I run that crosses that ninety-day window carries a hostname filter, and any figure I publish from it says so.

Making that permanent in GA4 is more awkward than it sounds. Google documents three data filter types: developer traffic, internal traffic, and web hostname traffic. The hostname filter excludes domains you name rather than admitting the ones you own, so the job of knowing which hostnames are yours stays with you.

What it costs to skip

The direct cost is an audit that reasons correctly from a false inventory. Consolidation plans and redirect maps both start from a list of pages, and if that list comes from analytics rather than from the repository, it can contain pages that were never on the site.

The quieter cost lands on rates. A conversion rate is a fraction with sessions on the bottom, and so is every engagement benchmark built the same way. Foreign sessions inflate that denominator with visitors who arrived somewhere else with different intent, so the resulting figure is wrong in a direction the figure itself will not reveal. Hand it to a bidding strategy or print it in a report and the error travels.

This belongs in front of the STACK Audit, the five passes I run before letting Smart Bidding optimize against numbers I have not verified. Those passes check whether the signal is emitted, triggered, attributed and valued correctly. None of them ask whether the property is describing the site you think it is, and neither do the eight layers of The Tracking Stack. This is a pre-flight on the data you audit with, ahead of the work of auditing what it says. It is the same family of check as reading the ad destination before the campaign spends: a silent failure with no error state, sitting upstream of everything downstream that trusts it.

The receipts

I found and fixed this on 2026-08-11. The mockup was a direct upload to Cloudflare Pages rather than a git project, which is why a repository sweep alone would have missed it and why the hostname breakdown is the step that matters. I stripped the snippet from the project root and from the built output, redeployed, then fetched the live pages and confirmed them clean. I checked nineteen other mockup and landing-page projects on the same machine. All nineteen were clean, so the leak was confined to the one. The historical events remain in the property and I handle them with a hostname filter rather than deleting them.

What I did not claim

I have not measured what the mispremised audit would have cost, because I found the premise before acting on the recommendation. I am not claiming a ranking or revenue effect, and there is no before-and-after here to show. The pageview figure is approximate and I have deliberately not set it against this site’s own traffic, since that comparison would imply a materiality I did not measure.

I also did not have a stranger write into my property. Both properties were mine, deployed by me, from one machine. That a public key permits an unrelated site to write into a property is documented mechanism rather than something I observed here, and I am reporting it as the former. What this account demonstrates is narrower and more common: a property is scoped to a key, so its page list can include pages that are not on the site you are auditing.

Finally, I have not tested every analytics vendor. What is documented is what is written above, for PostHog and GA4. Treat the rest as unknown until you have read their documentation.

One thing to do next

Break your analytics property out by hostname and read the list. If a domain you do not recognize appears, find the page carrying your key before you trust another report built on that property. That part is free and takes minutes. What it will not tell you is which of your reports, dashboards and bidding decisions have been standing on the polluted number, or for how long. If you want that traced, that is what I do.

Keep going

If this hit, the next two pieces in the same universe:

Free PDF: The 25-page Tracking Stack. No email gate.

Want a review like this on your account?

Want this kind of review
on your account?

Thirty minutes on the phone. Same person on the call as on the work. Walk out with a clear set of next steps.

Book a Call