サイトA(送客元)からサイトB(送客先)への遷移を計測する汎用的な手法。 CMS や特定のサービスに依存しない、ピュア HTML + JavaScript で実現できるアプローチ。
- サイトA とサイトB は異なるドメインで運用されている
- サイトB には複数の流入経路があり、サイトA 経由のユーザーのみを識別・計測したい
- サイトA 側に計測データを受け取る API エンドポイントが存在する
サイトA からサイトB への遷移リンクに、一意の識別子をパラメータとして付与する。
https://siteb.hoge?id=fuga123
この識別子(以下 click_id)により、サイトB 側で「サイトA 経由の流入である」と判定できる。
サイトB の LP(ランディングページ)で、URL パラメータから click_id を抽出し Cookie に保存する。
Cookie に保存する理由は、LP とサンクスページ(コンバージョン地点)が別ページである場合に click_id を引き継ぐため。
<img> タグの src 属性にサイトA の計測 API の URL を指定し、DOM に挿入する。
ブラウザが画像を読み込む際の GET リクエストが計測 API に到達する。
<img> タグは CORS 制約を受けないため、サイトB のドメインからサイトA のドメインへのリクエストに CORS 設定は不要。
手順 3 の時点でサイトA の計測 API にリクエストが発生する。
API 側で click_id と event の種別(LP 着地 / サンクスページ着地)をログまたは DB に記録することで、サイトA 経由でサイトB に着地した人数を計測できる。
| # | 計測ポイント | event 値 | 発火タイミング |
|---|---|---|---|
| 1 | サイトB LP に着地 | visit |
LP のページ表示時 |
| 2 | サイトB サンクスページ着地 | conversion |
サンクスページのページ表示時 |
https://example.com?click_id=clk_1706500000000_a1b2c3d4e
click_id の形式は任意。タイムスタンプ + ランダム文字列の組み合わせにすると衝突を回避しやすい。
URL パラメータから click_id を取得し、Cookie に保存したうえで計測 API に通知する。
click_id が存在しない場合(= サイトA 経由でない場合)は何も実行しない。
<script>
(function () {
const clickId = new URLSearchParams(window.location.search).get("click_id");
if (!clickId) return;
// Cookie に保存(30日間有効)
const expires = new Date();
expires.setDate(expires.getDate() + 30);
document.cookie =
"_mx_cid=" + clickId + "; expires=" + expires.toUTCString() + "; path=/";
// 計測 API にアクセス通知(img ピクセル)
const img = document.createElement("img");
img.src =
"サイトAのURL/api/track?click_id=" +
encodeURIComponent(clickId) +
"&event=visit";
img.width = 1;
img.height = 1;
img.style.position = "absolute";
img.style.left = "-9999px";
img.alt = "";
document.body.appendChild(img);
})();
</script>Cookie から click_id を取得し、計測 API にコンバージョン通知を送信する。
送信後に Cookie を削除して重複計測を防止する。
<script>
(function () {
// Cookie から click_id を取得
const match = document.cookie.match(/(^|;\s*)_mx_cid=([^;]*)/);
const clickId = match ? match[2] : null;
if (!clickId) return;
// 計測 API にコンバージョン通知(img ピクセル)
const img = document.createElement("img");
img.src =
"サイトAのURL/api/track?click_id=" +
encodeURIComponent(clickId) +
"&event=conversion";
img.width = 1;
img.height = 1;
img.style.position = "absolute";
img.style.left = "-9999px";
img.alt = "";
document.body.appendChild(img);
// 重複計測防止(過去の日時をexpiresに指定して有効期限切れにする)
document.cookie =
"_mx_cid=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
})();
</script>| 項目 | 値 |
|---|---|
| URL | /api/track |
| メソッド | GET |
| パラメータ | click_id (string), event ("visit" | "conversion") |
| レスポンス | 1x1 透明 GIF 画像 |
レスポンスとして 1x1 透明 GIF を返すことで、ブラウザのコンソールに画像読み込みエラーが出ることを防ぐ。
キャッシュヘッダーは Cache-Control: no-store, no-cache, must-revalidate を設定し、毎回リクエストが発生するようにする。
CREATE TABLE tracking_events (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
click_id VARCHAR(255) NOT NULL,
event ENUM('visit', 'conversion') NOT NULL,
ip VARCHAR(45) DEFAULT NULL,
user_agent TEXT DEFAULT NULL,
referer TEXT DEFAULT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
INDEX idx_click_id (click_id),
INDEX idx_event (event)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;/api/track.php として設置する。
click_id と event をバリデーションし、tracking_events テーブルに INSERT したうえで 1x1 透明 GIF を返す。
<?php
declare(strict_types=1);
// DB 接続情報
$dsn = 'mysql:host=localhost;dbname=your_database;charset=utf8mb4';
$dbUser = 'your_user';
$dbPass = 'your_password';
// パラメータ取得
$clickId = $_GET['click_id'] ?? '';
$event = $_GET['event'] ?? '';
// バリデーション
$validEvents = ['visit', 'conversion'];
if ($clickId !== '' && in_array($event, $validEvents, true)) {
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'] ?? null;
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? null;
$referer = $_SERVER['HTTP_REFERER'] ?? null;
try {
$pdo = new PDO($dsn, $dbUser, $dbPass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false,
]);
$stmt = $pdo->prepare(
'INSERT INTO tracking_events (click_id, event, ip, user_agent, referer)
VALUES (:click_id, :event, :ip, :user_agent, :referer)'
);
$stmt->execute([
':click_id' => $clickId,
':event' => $event,
':ip' => $ip,
':user_agent' => $userAgent,
':referer' => $referer,
]);
} catch (PDOException $e) {
error_log('[Track] DB save failed: ' . $e->getMessage());
}
}
// 1x1 透明 GIF を返す
header('Content-Type: image/gif');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Pragma: no-cache');
// GIF89a 1x1 transparent pixel
echo base64_decode('R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7');-- LP 着地数
SELECT COUNT(*) AS visit_count
FROM tracking_events
WHERE event = 'visit';
-- サンクスページ着地数
SELECT COUNT(*) AS conversion_count
FROM tracking_events
WHERE event = 'conversion';
-- click_id ごとの到達状況
SELECT
click_id,
MAX(event = 'visit') AS reached_lp,
MAX(event = 'conversion') AS reached_thanks,
MIN(created_at) AS first_seen
FROM tracking_events
GROUP BY click_id
ORDER BY first_seen DESC;
-- 日別の集計
SELECT
DATE(created_at) AS date,
SUM(event = 'visit') AS visit_count,
SUM(event = 'conversion') AS conversion_count
FROM tracking_events
GROUP BY DATE(created_at)
ORDER BY date DESC;- Safari ITP により、サードパーティ Cookie は最大 7 日間で削除される
- LP 着地からサンクスページ到達までの期間が 7 日を超えると、Cookie が消失し
conversionを計測できない - ブラウザの Cookie ブロック設定が有効な場合、Cookie に
click_idを保存できないため計測不可
- 1x1 ピクセル画像のパターンは広告ブロッカーの検出対象になる場合がある
- ブロックされた場合、
visit/conversionのいずれも計測 API に到達しない
- サンクスページ側のスクリプトで Cookie を削除することで、同一ユーザーの重複コンバージョン計測を防止している
- LP 側の
visitは同一click_idでの再訪問時にも発火する(Cookie の上書きのみで重複制御はしていない)