ドキュメント自分のドメインから配信する

04 · 計測

スクリプトを自分のドメインから配信する

サイト上の 2 つのパスを Tally に転送すれば、スクリプトとそのイベントはファーストパーティのままになります。

01自分で配信する理由

広告ブロッカーは、アナリティクスを読み込み元のアドレスでブロックします。自分のドメインから配信すれば、スクリプトとそのイベントはブロックリストに載りません。訪問者の Cookie はこれに左右されず、どちらの場合もスクリプトがページのドメインに設定します。

02転送する 2 つのパス

サイト側転送先
/js/script.jshttps://t.tallybyoutpost.com/script.js
/api/eventhttps://t.tallybyoutpost.com/api/event

そのうえで、スクリプトを自分のサイトから読み込みます。スクリプトは自分と同じアドレスの /api/event にイベントを送るので、ほかに変更はありません。

HTML
<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 までお知らせください。