リファラーが取得できない理由と対策|(direct)/(none)の原因からPHP・JSの実装・リダイレクト問題まで

referrer-error その他

「Google Analyticsの参照元が“(direct)”ばかりで分析が進まない…」

「PHPやJavaScriptでリファラーを取得しようとしても、なぜか空になる…」

そんな“リファラーが取得できない”問題に、頭を悩ませてはいませんか?

Web担当者や開発者にとって、リファラー(参照元)情報はユーザー行動を分析し、広告効果や流入経路を正しく把握するために欠かせないデータです。ですが、実際にはリファラーが正しく取得できない場面が多く、Google Analytics上でも「(none)」「(direct)」と表示されてしまうケースが増えています。

その原因は、単なる設定ミスだけでなく、リダイレクトの処理方法、HTTPSとHTTPの混在、ブラウザのセキュリティ仕様、さらにはmetaタグやサーバー設定の影響など、多岐にわたります。

この記事では、「リファラーが取得できない」ことで困っている方に向けて、考えられる原因を一つひとつ丁寧に解説し、状況に応じた具体的な対策までご紹介します。

この記事でわかること

  • Google Analyticsでリファラーが「(direct)」「(none)」になる代表的な原因
  • HTTPS→HTTP遷移やリダイレクト処理でリファラーが失われる仕組みと対応策
  • JavaScriptやPHPを使ったリファラー取得の方法と注意点
  • iframeやクロスドメイン環境でリファラーが取得できないときの対処法
  • metaタグ、.htaccess、Nginx設定によるReferrer-Policyの活用方法
  • 技術的制限を乗り越えてリファラー情報を安定的に取得する実践的な方法

リファラー取得の問題を放置していると、CVR分析や広告施策の精度が落ちてしまいます。この記事を通じて、原因を正確に把握し、最適な対策を講じていきましょう。

なぜ「リファラーが取得できない」? 原因とWebサイトへの影響を徹底解説

Webサイトを運営していると、「どこからユーザーが来たのか?」という情報は非常に重要です。この「どこから来たか」を示すのがリファラー(Referer)情報。しかし、多くのサイト運営者が「リファラーが取得できない」という問題に頭を悩ませています。

Referer - HTTP | MDN
Referer リクエストヘッダーには、現在リクエストされているページへのリンク先を持った直前のウェブページのアドレスが含まれています。 Referer ヘッダーにより、サーバーは人々がどこから訪問しに来たかを識別し、分析、ログ、キャッシュの最適化などに利用することができます。

なぜリファラーが取得できないのか?その原因と影響について、技術的な観点から詳しく解説していきます。

Google Analyticsで「(direct)」や「(none)」が多い…その理由とは?

Google Analyticsでトラフィックソースを確認すると、「(direct)/(none)」と表示されるケースがあります。これは「直接アクセス」と解釈されますが、実際には様々な理由でリファラー情報が失われているケースが含まれています。

「(direct)/(none)」が発生する主な理由

  1. ブラウザのアドレスバーから直接URLを入力した場合
    • 本当の意味での直接アクセスです
  2. ブックマークからアクセスした場合
    • ブックマークからのアクセスもリファラー情報を持ちません
  3. HTTPS→HTTPの遷移
    • セキュリティ保護のため、HTTPSサイトからHTTPサイトへのリンクではリファラーが送信されません
  4. プライバシー設定によるブロック
    • ブラウザの設定や拡張機能、セキュリティソフトがリファラー情報の送信をブロック
    • Firefox、Safari、Braveなど多くのブラウザでは、プライバシー保護機能としてリファラー情報を制限する設定があります
  5. メールクライアントからのクリック
    • Outlook、Thunderbirdなどのメールクライアントからのリンククリックではリファラーが送信されないことが多い

Google Analyticsでの確認方法

Google Analyticsで「(direct)/(none)」の割合が高すぎないか確認するには:

  1. 「集客」→「全てのトラフィック」→「チャネル」と進む
  2. 「Direct」の割合をチェック
  3. 一般的に20〜30%以上であれば、リファラー情報損失の可能性を疑う
// Google Analytics 4でデータレイヤーを使用してリファラー情報を補完する例
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}

// カスタムディメンションにリファラー情報を追加
gtag('event', 'page_view', {
  'custom_referer': document.referrer || '(not available)',
  'page_location': window.location.href
});

HTTPSからHTTPへの遷移でリファラーが消える理由と対応策

特に重要なのが「HTTPSからHTTPへの遷移」の問題です。これは技術的なセキュリティ対策としてブラウザが実装している仕様によるものです。

なぜHTTPSからHTTPへの遷移でリファラーが消えるのか?

ブラウザは暗号化された通信(HTTPS)から暗号化されていない通信(HTTP)へ移動する際、セキュリティ上の理由からリファラー情報を送信しません。これは「ダウングレード」と呼ばれる遷移で、情報漏洩を防ぐための仕様です。

具体的には:

  1. HTTPS(暗号化)サイトでユーザーがHTTP(非暗号化)サイトへのリンクをクリック
  2. ブラウザは「暗号化されたサイトの情報を非暗号化通信で送ると危険」と判断
  3. セキュリティリスクを避けるため、リファラー情報を削除してリクエストを送信

この仕組みにより、例えば「https://secure-site.com」から「http://your-site.com」へのアクセスではリファラー情報が消失します。

対応策:Referrer-Policyの活用

この問題に対応するには、Referrer-PolicyというHTTPヘッダーやメタタグを使用します:

<!-- サイト全体に適用するメタタグ -->
<meta name="referrer" content="no-referrer-when-downgrade">

または、サーバー側で以下のようなHTTPヘッダーを設定します:

Referrer-Policy: no-referrer-when-downgrade

このポリシーでは、同じセキュリティレベルまたはセキュリティレベルが上がる場合はリファラーを送信し、セキュリティレベルが下がる場合(HTTPSからHTTPへ)のみリファラーを送信しないという設定になります。

リダイレクト処理でリファラーが失われる原因と対策

リダイレクト(特に302や307などの一時的リダイレクト)を使用すると、リファラー情報が失われるケースがあります。

リダイレクトでリファラーが失われる仕組み

  1. ユーザーがサイトAからサイトBへのリンクをクリック
  2. サイトBがサイトCへリダイレクト
  3. この過程で、最終的なサイトCには「サイトAからのアクセス」という情報が失われ、「サイトBからのアクセス」として記録される

特に複数回のリダイレクトが発生すると、元のリファラー情報は完全に失われてしまいます。

リダイレクト時のリファラー保持方法

  1. URLパラメータによる受け渡し 最も一般的な方法は、リダイレクト時にURLパラメータでリファラー情報を引き継ぐ方法です:
<?php
// 元のリファラーを取得
$original_referer = $_SERVER['HTTP_REFERER'] ?? '';

// リダイレクト先URLにリファラー情報をパラメータとして付加
$redirect_url = '<https://final-destination.com/?original_ref=>' . urlencode($original_referer);

// リダイレクト実行
header('Location: ' . $redirect_url);
exit;
?>

  1. 301リダイレクト(永続的リダイレクト)の使用 一時的リダイレクト(302)よりも、可能であれば301リダイレクトを使用することで、多くのブラウザでリファラー情報が保持されやすくなります:
<?php
header('HTTP/1.1 301 Moved Permanently');
header('Location: <https://new-location.com/>');
exit;
?>

  1. JavaScriptを使ったリファラー情報の保存 Cookieやローカルストレージを使用して、リダイレクト前にリファラー情報を保存する方法も効果的です:
// リダイレクト前にリファラー情報を保存
localStorage.setItem('original_referer', document.referrer);

// その後リダイレクト
window.location.href = '<https://destination.com/>';

リダイレクト先では、保存したリファラー情報を読み取ります:

// 保存されたリファラー情報を読み取る
const originalReferer = localStorage.getItem('original_referer');
console.log('元のリファラー:', originalReferer);

リファラー情報の損失は、アクセス解析やマーケティング効果測定において重大な問題となります。特にコンバージョン測定や広告効果分析において、「どこからユーザーが来たのか」という情報は非常に価値があります。リファラー損失の原因を理解し、適切な対策を講じることで、より正確なデータ分析が可能になります。

リファラー取得方法と技術的解決策

リファラー情報の取得は、効果的なWebサイト分析の基盤となります。その取得と正確な解析には技術的な理解が必要です。ここでは、主要なプログラミング言語でのリファラー取得方法と、発生しがちな問題の解決策について解説します。

JavaScriptやPHPでのリファラー取得方法

JavaScriptでのリファラー取得

JavaScriptでは、document.referrerプロパティを使用して現在のページへの遷移元URL(リファラー)を取得できます。

// リファラー情報を取得
const referer = document.referrer;

// コンソールに出力
console.log('このページへのリファラー:', referer);

// リファラーの有無を確認
if (referer) {
  console.log('リファラーあり:', referer);
} else {
  console.log('リファラーなし(直接アクセスまたはリファラー情報が送信されていない)');
}

JavaScriptでのリファラー情報活用例

以下は、リファラー情報に基づいて表示内容を変更する簡単な例です:

// リファラーのURLオブジェクトを作成
const referer = document.referrer ? new URL(document.referrer) : null;

// リファラードメインに基づいた処理
if (referer) {
  // ドメイン名を取得
  const refererDomain = referer.hostname;

  // Google検索からの訪問者向けメッセージ
  if (refererDomain.includes('google.com')) {
    document.getElementById('welcome-message').innerHTML =
      'Google検索からようこそ!お探しの情報は見つかりましたか?';
  }
  // Twitter経由の訪問者向けメッセージ
  else if (refererDomain.includes('twitter.com') || refererDomain.includes('t.co')) {
    document.getElementById('welcome-message').innerHTML =
      'Twitterからのご訪問ありがとうございます!';
  }
}

この例では、Google検索やTwitterからの訪問者に対して異なるウェルカムメッセージを表示しています。

PHPでのリファラー取得

PHPでは、$_SERVER['HTTP_REFERER']スーパーグローバル変数を使用してリファラー情報にアクセスできます。

<?php
// リファラー情報を取得
$referer = $_SERVER['HTTP_REFERER'] ?? 'リファラーなし';

// 出力
echo "このページへのリファラー: " . htmlspecialchars($referer);

// リファラードメインに基づいた処理
if (isset($_SERVER['HTTP_REFERER'])) {
  $referer_domain = parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST);

  if (strpos($referer_domain, 'google.com') !== false) {
    echo "<p>Google検索からのアクセスありがとうございます!</p>";
  }
}
?>

注意点として、$_SERVER['HTTP_REFERER']は必ず存在するとは限らないため、NULL合体演算子(??)やisset()でのチェックが重要です。

リファラー情報の信頼性を高める複合的なアプローチ

実際の現場では、単一の方法だけでなく、複数のアプローチを組み合わせることが推奨されます:

<?php
function getReferer() {
  // まずHTTP_REFERERを確認
  if (isset($_SERVER['HTTP_REFERER'])) {
    return $_SERVER['HTTP_REFERER'];
  }

  // URLパラメータからの取得(UTMパラメータなど)
  if (isset($_GET['utm_source'])) {
    return 'utm_source: ' . $_GET['utm_source'];
  }

  // Cookieからの取得(以前保存した場合)
  if (isset($_COOKIE['original_referer'])) {
    return $_COOKIE['original_referer'];
  }

  return 'unknown';
}

$referer = getReferer();
?>

metaタグやサーバー設定でリファラー情報を制御する方法

リファラー情報の送信は、サイト運営者側でも制御できます。特に、プライバシーやセキュリティの観点から、送信するリファラー情報の範囲を調整したいケースが増えています。

Referrer-Policy metaタグによる制御

HTMLのmetaタグを使用して、ブラウザに対してリファラー情報の送信ポリシーを指示できます:

<!-- 完全にリファラー情報を送信しない -->
<meta name="referrer" content="no-referrer">

<!-- 同一オリジン(同じドメイン)内のみリファラーを送信 -->
<meta name="referrer" content="same-origin">

<!-- デフォルト設定:ダウングレード(HTTPSからHTTP)時のみリファラーを送信しない -->
<meta name="referrer" content="no-referrer-when-downgrade">

<!-- 常にオリジン(ドメイン)のみを送信(パスを含まない) -->
<meta name="referrer" content="origin">

<!-- 同一オリジン内では完全なリファラー、クロスオリジンではオリジンのみ -->
<meta name="referrer" content="strict-origin">

これらの設定は、サイト全体のプライバシーポリシーに合わせて選択します。例えば医療情報サイトやプライバシーを重視するサービスでは、strict-originsame-originを選択するケースが多いです。

.htaccessやサーバー設定によるHTTPヘッダー制御

Apache Webサーバーでは、.htaccessファイルを使用してReferrer-Policyヘッダーを設定できます:

# .htaccessファイル内での設定
<IfModule mod_headers.c>
  Header set Referrer-Policy "no-referrer-when-downgrade"
</IfModule>

Nginxの場合は、server設定ブロック内に以下を追加します:

# Nginx設定ファイル内
server {
  # 他の設定

  # リファラーポリシーヘッダーを追加
  add_header Referrer-Policy "no-referrer-when-downgrade";
}

リンクごとのリファラー制御

個別のリンクに対しても、rel属性を使用してリファラーポリシーを指定できます:

<!-- このリンクではリファラー情報を送信しない -->
<a href="<https://example.com>" rel="noreferrer">外部サイト</a>

<!-- リファラー情報を送信しないと同時に、window.opener参照も防止 -->
<a href="<https://example.com>" rel="noreferrer noopener">安全な外部リンク</a>

特に、target="_blank"を使用する外部リンクでは、セキュリティ対策としてrel="noopener"の追加が推奨されています。

iframeやクロスドメイン環境でのリファラー取得問題の解決策

異なるドメイン間での連携や、iframe内でのリファラー情報の扱いは特に複雑です。多くの企業がマイクロサービスアーキテクチャやサブドメインを活用する現代のWeb開発では、クロスドメイン環境は珍しくありません。

iframeとリファラー問題

iframeでは、親ページから子ページへのリファラー情報が正しく伝わらないことがあります:

<!-- 親ページ(<https://main-site.com/)> -->
<iframe src="<https://sub-service.com/embedded-page.html>" width="100%" height="400"></iframe>

この場合、embedded-page.htmlでのリファラーはhttps://main-site.com/になり、実際の外部からのリファラーは失われます。

解決策:URLパラメータの活用

<!-- 親ページでJavaScriptを使用してiframeのsrc属性を動的に設定 -->
<script>
  window.onload = function() {
    const originalReferer = document.referrer;
    const iframe = document.getElementById('my-iframe');

    // 既存のURLにパラメータを追加
    let iframeSrc = iframe.src;
    const separator = iframeSrc.includes('?') ? '&' : '?';
    iframe.src = iframeSrc + separator + 'parent_referer=' + encodeURIComponent(originalReferer);
  };
</script>

<iframe id="my-iframe" src="<https://sub-service.com/embedded-page.html>" width="100%" height="400"></iframe>

子ページ(embedded-page.html)では、以下のようにしてパラメータを取得します:

// iframeの子ページ内のJavaScript
function getParentReferer() {
  const urlParams = new URLSearchParams(window.location.search);
  return urlParams.get('parent_referer') || document.referrer;
}

const originalReferer = getParentReferer();
console.log('元のリファラー:', originalReferer);

クロスドメイン環境でのリファラー管理

複数のサブドメインやドメインを持つサービスでは、一貫したユーザー行動追跡が課題になります。

クロスドメイントラッキングの設定例(Google Analytics)

Google Analyticsでクロスドメイントラッキングを設定する例:

// メインドメインのGA設定
gtag('config', 'UA-XXXXXXXX-X', {
  'linker': {
    'domains': ['main-site.com', 'sub-service.com', 'other-service.net']
  }
});

postMessageを使ったクロスドメイン通信

異なるドメイン間でリファラー情報を含む情報を共有する場合は、postMessageAPIが有用です:

// 親ドメインのスクリプト
const iframe = document.getElementById('cross-domain-iframe');
iframe.onload = function() {
  const refererData = {
    type: 'referer-info',
    referer: document.referrer,
    currentUrl: window.location.href
  };

  // iframeのコンテンツウィンドウにメッセージを送信
  iframe.contentWindow.postMessage(JSON.stringify(refererData), '<https://sub-service.com>');
};

子ドメイン(iframe内)のスクリプト:

// 子ドメインのスクリプト
window.addEventListener('message', function(event) {
  // 送信元オリジンを検証(セキュリティ上重要)
  if (event.origin !== '<https://main-site.com>') return;

  try {
    const data = JSON.parse(event.data);

    if (data.type === 'referer-info') {
      console.log('親ページのリファラー:', data.referer);
      console.log('親ページのURL:', data.currentUrl);

      // この情報を使って処理を行う...
    }
  } catch (e) {
    console.error('メッセージの解析に失敗:', e);
  }
});

リファラー情報の取得と制御は、Webサイトの分析精度やプライバシー設定において重要な役割を果たします。特に複雑なシステム構成や多様なブラウザ環境を考慮すると、単一のアプローチだけでなく、複数の手法を組み合わせた堅牢な実装が求められます。次のセクションでは、より高度な問題と具体的な解決策について掘り下げていきます。

リファラー取得を妨げる技術的要因とその回避策

リファラー情報の取得は、一見シンプルに思えますが、実際の開発現場では様々な技術的要因によって阻害されることがあります。このセクションでは、特に多く見られる具体的な問題と、それらを効果的に回避するための実践的な解決策を紹介します。

PHPの$_SERVER[‘HTTP_REFERER’]が空になるケースと信頼できるコード例

PHPでリファラー情報を取得する際によく使用される$_SERVER['HTTP_REFERER']変数ですが、これが空になるケースは多岐にわたります。開発者がこの変数を過信すると、予期せぬバグや機能不全を引き起こす可能性があります。

$_SERVER[‘HTTP_REFERER’]が空になる主な原因

  1. ブラウザによるリファラー情報の非送信
    • プライバシー設定によりリファラー情報の送信が無効化されている
    • HTTPSからHTTPへのリクエストでリファラーが削除される
  2. ユーザーによる直接アクセス
    • URLの直接入力
    • ブックマークからのアクセス
    • デスクトップアプリケーションからのリンク
  3. プロキシやファイアウォールによるフィルタリング
    • 企業のネットワークでは、セキュリティ対策としてリファラー情報を削除することがある
  4. メールクライアントからのアクセス
    • 多くのメールクライアントはプライバシー保護のためリファラー情報を送信しない

信頼できる$_SERVER[‘HTTP_REFERER’]の処理コード例

以下は、リファラー情報の欠如に対応する堅牢なPHPコードの例です:

<?php
/**
 * 信頼性の高いリファラー情報を取得する関数
 *
 * @return array リファラー情報の詳細(存在する場合)
 */
function getReliableReferer() {
    // 結果を格納する配列
    $result = [
        'raw' => null,
        'exists' => false,
        'domain' => null,
        'path' => null,
        'query' => null,
        'source_type' => 'unknown'
    ];

    // 1. 標準的なリファラー情報の確認
    if (isset($_SERVER['HTTP_REFERER']) && !empty($_SERVER['HTTP_REFERER'])) {
        $result['raw'] = $_SERVER['HTTP_REFERER'];
        $result['exists'] = true;

        // リファラーURLの解析
        $parsed = parse_url($_SERVER['HTTP_REFERER']);

        if ($parsed !== false) {
            $result['domain'] = $parsed['host'] ?? null;
            $result['path'] = $parsed['path'] ?? null;
            $result['query'] = $parsed['query'] ?? null;

            // ソースタイプの判定
            if (isset($parsed['host'])) {
                // 検索エンジンからのトラフィックを識別
                if (strpos($parsed['host'], 'google.') !== false) {
                    $result['source_type'] = 'search_engine';
                    $result['search_engine'] = 'google';
                } elseif (strpos($parsed['host'], 'bing.') !== false) {
                    $result['source_type'] = 'search_engine';
                    $result['search_engine'] = 'bing';
                } elseif (strpos($parsed['host'], 'yahoo.') !== false) {
                    $result['source_type'] = 'search_engine';
                    $result['search_engine'] = 'yahoo';
                }
                // ソーシャルメディアからのトラフィックを識別
                elseif (strpos($parsed['host'], 'facebook.') !== false || strpos($parsed['host'], 'fb.') !== false) {
                    $result['source_type'] = 'social_media';
                    $result['social_platform'] = 'facebook';
                } elseif (strpos($parsed['host'], 'twitter.') !== false || strpos($parsed['host'], 't.co') !== false) {
                    $result['source_type'] = 'social_media';
                    $result['social_platform'] = 'twitter';
                } elseif (strpos($parsed['host'], 'instagram.') !== false) {
                    $result['source_type'] = 'social_media';
                    $result['social_platform'] = 'instagram';
                } elseif (strpos($parsed['host'], 'linkedin.') !== false) {
                    $result['source_type'] = 'social_media';
                    $result['social_platform'] = 'linkedin';
                }
                // 内部リンクかどうかを確認
                elseif (isset($_SERVER['HTTP_HOST']) && $parsed['host'] === $_SERVER['HTTP_HOST']) {
                    $result['source_type'] = 'internal';
                } else {
                    $result['source_type'] = 'external';
                }
            }
        }
    }

    // 2. UTMパラメータを確認(Googleアナリティクスのキャンペーンパラメータ)
    if (isset($_GET['utm_source'])) {
        $result['utm_source'] = $_GET['utm_source'];
        $result['utm_medium'] = $_GET['utm_medium'] ?? null;
        $result['utm_campaign'] = $_GET['utm_campaign'] ?? null;

        // UTMソースに基づいてソースタイプを補完
        if (empty($result['source_type']) || $result['source_type'] === 'unknown') {
            $utm_source = strtolower($_GET['utm_source']);

            if (in_array($utm_source, ['google', 'bing', 'yahoo'])) {
                $result['source_type'] = 'search_engine';
                $result['search_engine'] = $utm_source;
            } elseif (in_array($utm_source, ['facebook', 'twitter', 'instagram', 'linkedin'])) {
                $result['source_type'] = 'social_media';
                $result['social_platform'] = $utm_source;
            } elseif ($utm_source === 'newsletter' || $utm_source === 'email') {
                $result['source_type'] = 'email';
            }
        }
    }

    // 3. Cookieから以前保存したリファラー情報を確認(セッション持続用)
    if ((!$result['exists']) && isset($_COOKIE['original_referer'])) {
        $result['raw'] = $_COOKIE['original_referer'];
        $result['exists'] = true;
        $result['from_cookie'] = true;

        // Cookieから取得したURLも解析
        $parsed = parse_url($_COOKIE['original_referer']);
        if ($parsed !== false) {
            $result['domain'] = $parsed['host'] ?? null;
            $result['path'] = $parsed['path'] ?? null;
        }
    }

    // 初回アクセス時にリファラー情報をCookieに保存(30日間有効)
    if ($result['exists'] && !isset($result['from_cookie'])) {
        setcookie('original_referer', $result['raw'], time() + 30 * 24 * 60 * 60, '/', '', true, true);
    }

    return $result;
}

// 使用例
$referer_data = getReliableReferer();

// 使用例: リファラー情報に基づいた処理
if ($referer_data['exists']) {
    echo "リファラー: " . htmlspecialchars($referer_data['raw']) . "<br>";
    echo "ドメイン: " . htmlspecialchars($referer_data['domain']) . "<br>";
    echo "ソースタイプ: " . $referer_data['source_type'] . "<br>";

    // 検索エンジンからの訪問の場合
    if ($referer_data['source_type'] === 'search_engine') {
        echo "検索エンジン: " . $referer_data['search_engine'] . "<br>";
        // 検索エンジン固有のウェルカムメッセージなど
    }
} else {
    echo "リファラー情報がありません(直接アクセスまたはリファラーブロック)";
}
?>

このコードの優れた点は以下の通りです:

  1. 単にリファラーの有無だけでなく、詳細な情報を構造化して取得
  2. UTMパラメータを活用した代替情報の取得
  3. Cookieを利用した訪問履歴の維持
  4. トラフィックソースの分類(検索エンジン、ソーシャルメディアなど)
  5. XSS対策としてのエスケープ処理の適用

JavaScriptのdocument.referrerで取得できない場合の回避策と代替手法

JavaScriptでリファラー情報を取得するdocument.referrerも、PHPの$_SERVER['HTTP_REFERER']と同様の制限があります。しかし、フロントエンドではより柔軟な対応が可能です。

document.referrerが空になる主な状況

  1. プライバシー設定によるブロック
  2. HTTPSからHTTPへの遷移
  3. 直接アクセス(URLバー入力、ブックマーク等)
  4. 一部のモバイルアプリ内ブラウザからのアクセス

代替手法とフォールバックメカニズム

/**
 * 拡張リファラー情報取得関数
 * - 標準のdocument.referrer
 * - URLパラメータ
 * - セッションストレージ
 * - ローカルストレージ
 * を組み合わせて信頼性の高いリファラー情報を提供
 */
function getEnhancedReferrer() {
  // 結果オブジェクト
  const result = {
    referrer: '',
    source: 'unknown',
    isFirstVisit: false,
    landingPage: window.location.href
  };

  // URLパラメータからリファラー関連情報を取得
  const urlParams = new URLSearchParams(window.location.search);

  // 1. 標準のリファラー確認
  if (document.referrer && document.referrer.length > 0) {
    result.referrer = document.referrer;
    result.source = 'document.referrer';

    // 内部リファラー(同一ドメイン)かどうか判定
    const currentDomain = window.location.hostname;
    const referrerUrl = new URL(document.referrer);
    result.isInternalReferrer = referrerUrl.hostname === currentDomain;

    // 初回訪問時、ランディングページとリファラーを保存
    if (!sessionStorage.getItem('original_landing_page')) {
      sessionStorage.setItem('original_landing_page', window.location.href);
      sessionStorage.setItem('original_referrer', document.referrer);
      result.isFirstVisit = true;
    }
  }
  // 2. ref/source URLパラメータを確認
  else if (urlParams.has('ref') || urlParams.has('source') || urlParams.has('utm_source')) {
    if (urlParams.has('ref')) {
      result.referrer = urlParams.get('ref');
      result.source = 'url_param_ref';
    } else if (urlParams.has('source')) {
      result.referrer = urlParams.get('source');
      result.source = 'url_param_source';
    } else if (urlParams.has('utm_source')) {
      result.referrer = urlParams.get('utm_source');
      result.campaign = urlParams.get('utm_campaign') || '';
      result.medium = urlParams.get('utm_medium') || '';
      result.source = 'utm_parameters';
    }

    // 初回訪問としてマーク
    if (!sessionStorage.getItem('original_landing_page')) {
      sessionStorage.setItem('original_landing_page', window.location.href);
      sessionStorage.setItem('original_referrer', result.referrer);
      result.isFirstVisit = true;
    }
  }
  // 3. セッションストレージから以前の訪問情報を取得
  else if (sessionStorage.getItem('original_referrer')) {
    result.referrer = sessionStorage.getItem('original_referrer');
    result.landingPage = sessionStorage.getItem('original_landing_page') || result.landingPage;
    result.source = 'session_storage';
    result.isFirstVisit = false;
  }
  // 4. ローカルストレージから長期保存情報を取得
  else if (localStorage.getItem('first_visit_referrer')) {
    result.referrer = localStorage.getItem('first_visit_referrer');
    result.landingPage = localStorage.getItem('first_visit_landing_page');
    result.firstVisitDate = localStorage.getItem('first_visit_date');
    result.source = 'local_storage';
    result.isFirstVisit = false;
  }
  // 5. それでも情報がない場合は直接アクセスと判断
  else {
    result.source = 'direct';
    result.isFirstVisit = !localStorage.getItem('first_visit_date');

    // 初回訪問情報を保存
    if (result.isFirstVisit) {
      localStorage.setItem('first_visit_landing_page', window.location.href);
      localStorage.setItem('first_visit_date', new Date().toISOString());
      localStorage.setItem('first_visit_referrer', 'direct');

      sessionStorage.setItem('original_landing_page', window.location.href);
      sessionStorage.setItem('original_referrer', 'direct');
    }
  }

  return result;
}

// 使用例
const referrerInfo = getEnhancedReferrer();
console.log('リファラー情報:', referrerInfo);

// リファラー情報に基づいた動的コンテンツ
if (referrerInfo.isFirstVisit) {
  // 初回訪問者向けのウェルカムメッセージを表示
  document.getElementById('welcome-message').textContent =
    'サイトへようこそ!初めてのご訪問ありがとうございます。';

  // 初回訪問者向けにチュートリアルやガイダンスを表示する要素を表示
  document.getElementById('first-visit-guide').style.display = 'block';
}

// トラフィックソースに基づいたカスタムコンテンツ
if (referrerInfo.referrer.includes('google.com')) {
  document.getElementById('search-visitors').style.display = 'block';
} else if (referrerInfo.referrer.includes('facebook.com') ||
           referrerInfo.referrer.includes('twitter.com')) {
  document.getElementById('social-visitors').style.display = 'block';
}

この例では、以下の手法を組み合わせています:

  1. 標準のdocument.referrerの確認
  2. URLパラメータ(refsource、UTMパラメータ)の活用
  3. セッションストレージによるサイト内ナビゲーション中のリファラー情報保持
  4. ローカルストレージによる長期的な初回訪問情報の記録

SPAフレームワークでのリファラー情報の保持

シングルページアプリケーション(SPA)では、ページ遷移時にリファラー情報が失われるという追加の課題があります。例えばReactアプリケーションでの対応策:

// React Router v6 でのリファラー情報保持の例
import { useEffect, useState } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';

// カスタムフック: ルート変更時にリファラー情報を管理
function useReferrerTracking() {
  const location = useLocation();
  const navigate = useNavigate();
  const [pageTransitions, setPageTransitions] = useState([]);

  useEffect(() => {
    // ルート変更検出時
    const currentPath = location.pathname;
    const timestamp = new Date().toISOString();

    // 新しいページ遷移を記録
    setPageTransitions(prev => {
      const newTransitions = [...prev, {
        from: prev.length > 0 ? prev[prev.length - 1].to : document.referrer,
        to: currentPath,
        timestamp
      }];

      // 最大10回分の遷移履歴を保持
      if (newTransitions.length > 10) {
        return newTransitions.slice(-10);
      }
      return newTransitions;
    });

    // 遷移履歴をセッションストレージに保存
    sessionStorage.setItem('page_transitions', JSON.stringify(pageTransitions));

    // 外部リファラーがある場合は保存(初回のみ)
    if (document.referrer && document.referrer.length > 0 &&
        !document.referrer.includes(window.location.hostname) &&
        !sessionStorage.getItem('external_referrer')) {
      sessionStorage.setItem('external_referrer', document.referrer);
    }
  }, [location.pathname]);

  // 前のページへの参照を取得する関数
  const getPreviousPage = () => {
    if (pageTransitions.length > 1) {
      return pageTransitions[pageTransitions.length - 2].to;
    }
    return document.referrer;
  };

  // 元のリファラー(サイト訪問時)を取得
  const getOriginalReferrer = () => {
    return sessionStorage.getItem('external_referrer') || document.referrer || 'direct';
  };

  return {
    pageTransitions,
    getPreviousPage,
    getOriginalReferrer
  };
}

// 使用例
function MyComponent() {
  const { getPreviousPage, getOriginalReferrer } = useReferrerTracking();

  return (
    <div>
      <p>前のページ: {getPreviousPage()}</p>
      <p>元のリファラー: {getOriginalReferrer()}</p>
    </div>
  );
}

.htaccessやNginxでリファラー保持を保証する設定例と注意点

サーバー側の設定でリファラー情報の取り扱いを制御することも重要です。特に異なるプロトコル間(HTTPSとHTTP)やサブドメイン間での移動時にリファラー情報を保持するためには、適切なヘッダー設定が必要です。

Apacheサーバー(.htaccess)での設定例

# .htaccessファイル内

# リファラーポリシーの設定
<IfModule mod_headers.c>
    # デフォルトポリシー: ダウングレード時(HTTPSからHTTP)のみリファラーを送信しない
    Header set Referrer-Policy "no-referrer-when-downgrade"

    # 特定のディレクトリでは完全なリファラー情報を送信
    <Directory "/var/www/html/public-content">
        Header set Referrer-Policy "unsafe-url"
    </Directory>

    # プライバシーが重要なセクションではリファラーを完全に隠す
    <Directory "/var/www/html/private-section">
        Header set Referrer-Policy "no-referrer"
    </Directory>
</IfModule>

# 内部リダイレクト時のリファラー情報保持
<IfModule mod_rewrite.c>
    RewriteEngine On

    # 古いURLから新URLへのリダイレクト時にリファラー情報を保持
    # ?ref=external-source などのクエリパラメータを追加
    RewriteCond %{HTTP_REFERER} !^$
    RewriteCond %{HTTP_REFERER} !^https?://([^/]+\\.)?example\\.com/ [NC]
    RewriteRule ^old-page\\.php$ new-page.php?ref=%{HTTP_REFERER} [QSA,L]
</IfModule>

Nginxでの設定例

# Nginxの設定ファイル内

server {
    listen 80;
    server_name example.com;

    # デフォルトのリファラーポリシー
    add_header Referrer-Policy "no-referrer-when-downgrade";

    # 特定のロケーションでリファラーポリシーをカスタマイズ
    location /public/ {
        add_header Referrer-Policy "origin-when-cross-origin";
        # その他の設定...
    }

    location /private/ {
        add_header Referrer-Policy "no-referrer";
        # その他の設定...
    }

    # リダイレクト時のリファラー保持
    location /old-section/ {
        # リファラーが存在し、かつ内部リファラーでない場合にクエリパラメータを追加
        if ($http_referer !~ "^https?://([^/]+\\.)?example\\.com/") {
            rewrite ^/old-section/(.*)$ /new-section/\?ref=$http_referer redirect;
        }
        # 通常のリダイレクト
        rewrite ^/old-section/(.*)$ /new-section/\ redirect;
    }
}

サーバー設定における注意点

  1. 複数のポリシー競合 同一ページで異なる方法(HTTPヘッダーとmetaタグ)でReferrer-Policyを設定した場合、最も制限的なポリシーが適用されます。
  2. プロキシサーバーの影響 CDNやリバースプロキシを使用している場合、リファラー情報が変更される可能性があります。Cloudflareなどのサービスを使用している場合は、その設定も確認しましょう。
  3. セキュリティとのバランス リファラー情報の保持を優先するあまり、unsafe-urlなどの設定を無分別に使用すると、セキュリティリスクが増大します。機密情報を含むURLからのリファラー送信には注意が必要です。
  4. モバイルアプリからのトラフィック モバイルアプリからWebビューを通じてアクセスする場合、リファラー情報が全く異なる挙動を示すことがあります。
  5. パフォーマンスへの影響 複雑なリライトルールやリファラー処理はサーバーの負荷につながる可能性があります。特に高トラフィックサイトでは注意が必要です。

実装と検証の重要性

リファラー情報の処理においては、理論だけでなく実際の環境での検証が非常に重要です。特に以下のポイントに注意しましょう:

  1. 複数のブラウザでのテスト
    Chrome、Firefox、Safari、Edgeなど主要ブラウザでの挙動の違いを把握する
  2. モバイルデバイスでの確認
    モバイルブラウザはデスクトップとは異なるリファラー処理をすることがある
  3. プライバシーモードでのテスト
    ブラウザのプライバシーモードではリファラー送信が制限されることが多い
  4. 実際のユーザー行動データの分析
    実際のトラフィックにおいて、どの程度リファラー情報が取得できているかを定期的に分析する

リファラー情報の取得と活用は、Web解析やマーケティングの基本となるものですが、技術的な複雑さと変化するブラウザポリシーのため、定期的な見直しと更新が必要な領域です。適切な実装によって、より正確なデータ取得と効果的なサイト運営が可能になります。

まとめ:リファラー問題を解決して正確なアクセス解析を実現しよう

ここまで「リファラーが取得できない問題」について詳しく見てきましたが、いかがでしたか?リファラー情報はウェブサイトの分析において非常に重要な要素であり、これが正しく取得できないと適切な改善策を講じることが難しくなってしまいます。

記事のポイント

  • リファラー情報が「(direct)」や「(none)」と表示される主な原因は、HTTPSからHTTPへの移動やリダイレクト設定の問題、ブラウザのプライバシー設定などがあります
  • JavaScriptやPHPでリファラーを取得する際は、それぞれdocument.referrer$_SERVER['HTTP_REFERER']を使いますが、取得できないケースも多いため注意が必要です
  • クロスドメイン環境やiframe内では特に慎重な対応が求められ、適切なメタタグ設定やサーバー側の構成が重要になります
  • リファラー情報保持のためには、適切なリダイレクト方法の選択や.htaccessファイルの設定が効果的です
  • どうしてもリファラーが取得できない環境では、UTMパラメータやカスタムデータレイヤーなどの代替手段を検討しましょう

リファラー情報の問題は一見すると技術的に複雑に感じるかもしれませんが、原因を理解して適切な対策を講じることで、多くの場合は改善が可能です。特にGoogleアナリティクスでの分析精度を高めたい場合は、今回ご紹介した方法を組み合わせて活用してみてください。

最後に、ウェブの技術は日々進化しており、プライバシー保護の流れからリファラー情報の制限も厳しくなっていく傾向にあります。常に最新の情報をキャッチアップして、変化に対応できる準備をしておくことが大切です。

JavaScriptでブラウザバックを正確に判定する方法|よくある失敗と対処法もセットで解説
JavaScriptでブラウザバックを判定する方法を基礎から解説。popstate・pageshowイベントの違いや実装例、戻るボタンによる誤操作の防止方法、SPA(React・Vue・Next.js)やSafari特有の挙動への対応、Google Analyticsとの連携方法まで幅広く紹介しています。
タイトルとURLをコピーしました