04 · 計測
スクリプトを自分のドメインから配信する
サイト上の 2 つのパスを Tally に転送すれば、スクリプトとそのイベントはファーストパーティのままになります。
01自分で配信する理由
広告ブロッカーは、アナリティクスを読み込み元のアドレスでブロックします。自分のドメインから配信すれば、スクリプトとそのイベントはブロックリストに載りません。訪問者の Cookie はこれに左右されず、どちらの場合もスクリプトがページのドメインに設定します。
02転送する 2 つのパス
| サイト側 | 転送先 |
|---|---|
/js/script.js | https://t.tallybyoutpost.com/script.js |
/api/event | https://t.tallybyoutpost.com/api/event |
そのうえで、スクリプトを自分のサイトから読み込みます。スクリプトは自分と同じアドレスの /api/event にイベントを送るので、ほかに変更はありません。
<script defer data-site="k3xq2mfa7twp" src="/js/script.js"></script>サイトにすでに /api/event がある場合は、別のパスを転送し、data-api="/your/path" でスクリプトにそのパスを指定します。
03どのプロキシでも必要なこと
- ユーザーエージェントをそのまま渡す:ボットの除外に使います。
- 訪問者の IP アドレスを転送する(
X-Forwarded-For):都市の特定(Cookie なしのサイトでは訪問者の計数にも)に使い、その後破棄します。訪問者のリクエストを転送するサーバーにはプロキシ向けの大きなイベント上限が適用されるため、訪問者が 1 つのブラウザ分の上限を共有することはありません。 - Cookie は転送しない:読み取りません。
- 必要ならブラウザにすぐ応答して構いません(
202)。スクリプトはどのレスポンスも無視します。
04サーバー側
// next.config.js
module.exports = {
async rewrites() {
return [
{ source: '/js/script.js', destination: 'https://t.tallybyoutpost.com/script.js' },
{ source: '/api/event', destination: 'https://t.tallybyoutpost.com/api/event' },
];
},
};
Vercel では、リライトが X-Forwarded-For を引き継ぎます。アプリ自身に /api/event ルートがある場合はそちらが優先されるので、別のパスと data-api を使ってください。
{
"rewrites": [
{ "source": "/js/script.js", "destination": "https://t.tallybyoutpost.com/script.js" },
{ "source": "/api/event", "destination": "https://t.tallybyoutpost.com/api/event" }
]
}
Next.js を使わない Vercel 上のサイト向けの vercel.json。
# netlify.toml
[[redirects]]
from = "/js/script.js"
to = "https://t.tallybyoutpost.com/script.js"
status = 200
force = true
[[redirects]]
from = "/api/event"
to = "https://t.tallybyoutpost.com/api/event"
status = 200
force = true
location = /js/script.js {
proxy_pass https://t.tallybyoutpost.com/script.js;
proxy_set_header Host t.tallybyoutpost.com;
proxy_ssl_server_name on;
}
location = /api/event {
proxy_pass https://t.tallybyoutpost.com/api/event;
proxy_set_header Host t.tallybyoutpost.com;
proxy_ssl_server_name on;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Cookie "";
}
yoursite.com {
handle /js/script.js {
rewrite * /script.js
reverse_proxy https://t.tallybyoutpost.com {
header_up Host {upstream_hostport}
}
}
handle /api/event {
reverse_proxy https://t.tallybyoutpost.com {
header_up Host {upstream_hostport}
header_up -Cookie
}
}
# … the site's own handlers
}
Caddy は X-Forwarded-For を自動で設定します。
// Routed on yoursite.com/js/script.js and yoursite.com/api/event.
const UPSTREAM = {
'/js/script.js': 'https://t.tallybyoutpost.com/script.js',
'/api/event': 'https://t.tallybyoutpost.com/api/event',
};
export default {
async fetch(request) {
const upstream = UPSTREAM[new URL(request.url).pathname];
if (!upstream) return fetch(request);
const headers = new Headers(request.headers);
headers.set('X-Forwarded-For', request.headers.get('CF-Connecting-IP') || '');
headers.delete('Cookie');
return fetch(upstream, { method: request.method, headers, body: request.body });
},
};
// Node 18 or later.
const express = require('express');
const UPSTREAM = 'https://t.tallybyoutpost.com';
const app = express();
app.set('trust proxy', true); // when Express runs behind a proxy, so req.ip is the visitor's
app.get('/js/script.js', async (req, res) => {
const upstream = await fetch(UPSTREAM + '/script.js');
res.set('Content-Type', 'application/javascript; charset=utf-8');
res.set('Cache-Control', upstream.headers.get('cache-control') || 'public, max-age=3600');
res.send(await upstream.text());
});
app.post('/api/event', express.text({ type: '*/*', limit: '8kb' }), (req, res) => {
fetch(UPSTREAM + '/api/event', {
method: 'POST',
headers: {
'Content-Type': 'text/plain',
'User-Agent': req.get('user-agent') || '',
'X-Forwarded-For': req.ip,
},
body: req.body,
}).catch(() => {});
res.status(202).end();
});
05うまくいったか確認する
足りない情報や、実際の画面と違う点はありませんか? どのページかを添えて support@tallybyoutpost.com までお知らせください。