2026年09月16日 10:40 / - PV

GA4の参照元レポートを見ていて、見てはいけないものを見つけました。
localhost:3000 / referral
自分のローカル開発のアクセスが、本番のGA4に流れ込んでいました。
「数セッションくらい誤差では」と思うかもしれません。私もそう思いました。ですが判断を誤らせるには十分でした。この記事は、その実例と対処の話です。
やっていたことは単純です。
.envに置いていた_app.jsで無条件にタグを読み込んでいたnext devでもタグが動いていたローカルで画面を確認するたび、本番のGA4にヒットが飛んでいたわけです。
さらに、自前のヒートマップ計測タグのほうは対策したつもりでした。
{process.env.NODE_ENV === 'production' && (
<Script src="https://heatmap.example.com/tag.js" />
)}
これが効いていませんでした。
NODE_ENV === 'production'NODE_ENVは「開発サーバーか、ビルドされたものか」を表します。デプロイ先が本番かどうかは表しません。
Vercelのプレビューデプロイは、本番と同じようにビルドされます。つまりNODE_ENVはproductionです。
プレビューはプルリクエストのたびに作られ、そのたびに自分で開いて確認します。ローカルより頻度が低いとも限りません。
VercelはNEXT_PUBLIC_VERCEL_ENVという環境変数を自動で入れてくれます。値はproduction / preview / developmentのいずれかです。
これを併用して、「計測タグを読み込んでよい環境か」という判定を1つ作ります。
/*
計測タグを読み込んでよい環境か。ローカル開発と Vercel のプレビューでは読み込まない。 本番の GA4 に参照元 localhost:3000 が記録されていたため、 自分の開発アクセスが本番データに混ざっていた。*/
const isProductionSite =
process.env.NODE_ENV === 'production' &&
process.env.NEXT_PUBLIC_VERCEL_ENV !== 'preview'
export default function App({ Component, pageProps }) {
return (
<>
{isProductionSite && (
<GoogleTagManager googleTagManagerId={googleTagManagerId} />
)}
{isProductionSite && (
<Script src="https://heatmap.example.com/tag.js" data-site="example" />
)}
<Component {...pageProps} />
</>
)
}
ポイントは判定を1箇所に切り出して、全部の計測タグをそれで囲むことです。
タグごとに条件を書いていると、今回のように片方だけ対策済みという状態が生まれます。GTMは無条件、ヒートマップはNODE_ENVだけ、という中途半端さがまさにそれでした。
NEXT_PUBLIC_の接頭辞が必要な点にも注意してください。クライアント側で参照する値なので、これが無いとundefinedになり、条件が意図せず通ります。
ここからが本題です。汚れたデータは、「なんとなく多い」では済みません。
うちのサイトで、前年同期と比べた数字がこうなっていました。
合計だけを見れば「増えている、順調だ」と読めます。実際には検索流入が4割減っていました。
差を埋めていたのはDirectでした。そしてそのDirectの多くが、自分の本番動作確認のアクセスです。デプロイのたびに開いて確認していたぶんが、そのまま積み上がっていました。
答え合わせもできました。Google Search Consoleのクリック数は同期間で**-45%**。GA4のOrganic Searchの-42.7%とほぼ一致します。 つまり実態は「減っている」で確定です。
もしGA4の合計だけを見て「増えているから今のやり方でいい」と判断していたら、まったく逆の方向へ舵を切っていました。
計測が壊れているケースそのものについては、別記事に書きました。
取り除けません。 送ってしまったヒットは消せませんし、後から「これは自分のアクセス」と判別する手段もありません。
できるのは2つです。
GA4には内部トラフィックの除外設定(IPアドレスによるフィルタ)もありますが、自宅と外出先で回線が変われば抜けます。そもそも送らないほうが確実です。
「あとで除外設定を入れよう」と思っているうちに1年分溜まる、というのが現実的な失敗のしかたです。私はそれをやりました。
自前のヒートマップ計測を入れているなら、プライバシーポリシーに書いておくのも一緒にやってしまうのがおすすめです。
うちは以下を追記しました。
「入れたついでに書く」を逃すと、たいてい永久に書きません。
NEXT_PUBLIC_VERCEL_ENVはVercel以外でも使えますか?Vercel固有の変数です。他のホスティングなら、同じ役割の変数を自分で定義してください。
大事なのは変数名ではなく、「ビルドの種類」と「デプロイ先の種類」を別の軸として持つことです。NODE_ENVだけでは後者を表現できません。
その場合は、プレビュー用に別の計測先を用意するのが筋です。GA4なら別プロパティ、GTMなら別コンテナを作って、環境に応じて向き先を変えます。
「本番の計測先に、プレビューから送って確認する」は、確認と引き換えにデータを汚しています。
プレビューのアクセスも同じように混ざります。そしてプレビューはステージング用のURLとして人に共有することもあるので、他人のアクセスまで混ざる可能性があります。
「本番サイトかどうか」で切るのがいちばんシンプルです。
参照元として記録されているだけで、外部に何かが公開されているわけではありません。自分のGA4プロパティの中の話です。
とはいえ「開発中のURLがレポートに残る」のは気持ちのいいものではありません。
NODE_ENV === 'production'だけではVercelのプレビューが素通りするNEXT_PUBLIC_VERCEL_ENV !== 'preview'を併用する数字を見て判断するつもりなら、まずその数字が自分の足跡でできていないかを疑ってみてください。うちは「増えている」と「減っている」が同時に成立していました。
この記事が何かのお役に立てれば幸いです。
最後までお読みいただきありがとうございました!
Share Me!