Safari ITP のトラッキング問題の修正

Safari ITP のトラッキング問題の修正

お知らせ:当社は、お客様により充実したサポート情報を迅速に提供するため、本ページのコンテンツは機械翻訳を用いて日本語に翻訳しています。正確かつ最新のサポート情報をご覧いただくには、本内容の英語版を参照してください。

SafariのIntelligent Tracking Prevention(ITP)は、JavaScriptで書き込まれたCookieを訪問者のブラウザーに保持できる期間を制限します。Zoho PageSenseでA/Bテスト、ヒートマップ、アンケート、ファネルを実行している場合、訪問者が早期に新規ユーザーとして扱われ、テスト結果が歪んだり、複数ステップのファネルが正しく機能しなくなったりする可能性があります。このガイドでは、ITPの仕組み、PageSenseのトラッキングへの影響、Cookie同期エンドポイントを使用して10分以内に問題を解決する方法について説明します。

Safari ITPとは  

Appleは、サイトをまたいだユーザーのトラッキングを防止するためにITPを導入しました。バージョンアップのたびに制限が強化されています。

ITPのバージョン
機能

ITP 2.1
JavaScript(document.cookie)で書き込まれたCookieは、7日後に期限切れになります
ITP 2.2
訪問者が装飾リンク(トラッキングドメインのクエリー文字列やフラグメントを含むURL)からアクセスした場合、JavaScriptのCookieは24時間後に期限切れになります

ITP 2.3
24時間ルールがWebサイトのCookieにも適用されます。また、Cookie以外のローカルストレージも、操作がない状態で7日経過すると期限切れになります

装飾リンクとは、Safariによってサイト間トラッカーに分類されたドメインから生成された、クエリー文字列またはフラグメントを含むURL(例:?utm_source=newsletterまたは#section)です。
ITPが有効になる環境:iOS 12.2以降、iPadOS、およびmacOS Mojave/High Sierra/Catalina以降のSafari。

ITPによるZoho PageSenseへの影響  

PageSenseは、訪問者が表示したバリエーション、ファネル内の位置、セッションの進捗状況などの訪問者トラッキングデータを、ブラウザーのCookieとlocalStorageに保存します。ITPによって保存期間が短縮されると、次の問題が発生する可能性があります。
  1. 訪問者がテストに再度振り分けられる。再訪問した訪問者のCookieが期限切れになっていると、PageSenseはその訪問者を新規訪問者として扱います。その結果、以前とは異なるバリエーションに割り当てられ、エクスペリエンスに一貫性がなくなり、重複データによってテスト結果の精度が低下する可能性があります。
  2. ファネルが途中で中断する。コンバージョンファネルが7日を超える場合(装飾リンクからアクセスした訪問者の場合は24時間を超える場合)、PageSenseは訪問者の進捗情報を失います。8日目にコンバージョンした訪問者は、それまでの手順を関連付けるトラッキングデータが期限切れになっているため、集計されません。
  3. ユニーク訪問者数が水増しされる。Cookieの期限切れ後に同じ人物が再訪問すると、新しいユニーク訪問者として登録されます。テスト期間中、この影響によってサンプルサイズやコンバージョン率が大きく歪む可能性があります。
  4. アンケートの途中経過が失われる。訪問者がlocalStorageの7日間の保存期間内にPageSenseのアンケートを完了しなかった場合、回答は失われます。また、Cookieにはアンケートを開始済みであることが記録されているため、その訪問者にアンケートが再表示されることもありません。

Notesメモ:CookieとlocalStorageの期限切れの問題は、Safariにのみ該当します。Chrome、Firefox、Edgeには影響しません。

サーバーがSet-CookieHTTPレスポンスヘッダーを通じて設定したCookieには、ITPの有効期限の上限が適用されません。解決方法は簡単です。ブラウザーのリクエストからPageSenseのCookieを読み取り、有効期間の長い、サーバーによって設定されたCookieとして再発行する軽量なエンドポイントを、自社サーバー上にホストします。
PageSenseのトラッカーは、Safari上でこのエンドポイントを自動的に呼び出します(訪問者1人につき1時間に最大1回)。エンドポイントの場所を指定するだけで利用できます。
Zohoには何も保存または送信されません。このエンドポイントは、PageSenseの既存のCookie値に長い有効期限を設定し、訪問者のブラウザーに返すだけです。すべての処理は自社サーバー上で実行されます。

設定

手順1:同期エンドポイントの展開  
以下のコードスニペットのいずれかを、Webサイトと同じドメイン上にホストします(例:https://yoursite.com/pagesense-sync)。
展開する前に、コードスニペット内のCOOKIE_DOMAINの値を、先頭にドットを付けた登録可能なドメインに更新します(例:.yoursite.com)。サイトがapp.yoursite.comにある場合も、サブドメイン間でCookieにアクセスできるよう、ドメインには.yoursite.comを指定してください。
Notes
ドメイン値に関するメモ:以下のすべてのコードスニペットで、.example.comを自社の登録可能なドメインに置き換えてください。たとえば、Webサイトがstore.acmecorp.comの場合は、.acmecorp.comを使用します。正しい値が不明な場合は、Zoho PageSenseサポートにお問い合わせください。
Python(Flask)
  1. python
  2. '''
  3. PageSenseのCookie同期エンドポイント(Safari ITP対策)— Python/Flask。
  4. PageSenseのファーストパーティCookieをSet-Cookie経由で再発行し、Safariで
  5. 本来の有効期間が維持されるようにします。サイトと同じドメイン上にホストしてください。

  6. from pagesense_sync import pagesense_sync_blueprint
  7. app.register_blueprint(pagesense_sync_blueprint)
  8. '''

  9. fromflask importBlueprint,request,make_response

  10. # --- 設定:先頭にドットを付けた登録可能なドメインに置き換えてください ---
  11. # 例:.acmecorp.com(store.acmecorp.com 上のサイトの場合)
  12. COOKIE_DOMAIN = '.example.com'

  13. MAX_AGE_SECONDS = 34560000  # 400日

  14. pagesense_sync_blueprint = Blueprint('pagesense_sync',__name__)


  15. @pagesense_sync_blueprint.route('/pagesense-sync', methods=['GET'])
  16. def pagesense_sync():
  17. is_https = (
  18. request.is_secure
  19.         orrequest.headers.get('X-Forwarded-Proto', '')== 'https'
  20.     )

  21. response = make_response('', 204)
  22. response.headers['Cache-Control']= 'no-store'

  23.     forname,value inrequest.cookies.items():
  24.         ifname.startswith('zab') orname.startswith('zps'):
  25. response.set_cookie(
  26. name,
  27. value,
  28.                 max_age=MAX_AGE_SECONDS,
  29.                 path='/',
  30.                 domain=COOKIE_DOMAIN,
  31.                 secure=is_https,
  32.                 httponly=False,  # トラッカーがこれらのクッキーを読み取れるようにする必要があります
  33.                 samesite='None' ifis_https else 'Lax',
  34.             )

  35.     returnresponse

Node.js (Express)
  1. javascript
  2. /**
  3. * PageSenseクッキー同期エンドポイント(Safari ITP対策)— Node.js / Express。
  4. * Set-Cookieを使用してPageSenseのファーストパーティクッキーを再発行し、Safariで
  5. * 有効期間全体が維持されるようにします。サイトと同じドメインにマウントしてください。
  6. *
  7. * const pagesenseSync = require('./pagesense-sync.node.js');
  8. * app.get('/pagesense-sync', pagesenseSync);
  9. */

  10. // --- 設定:先頭にドットを付けた登録可能なドメインに置き換えてください ---
  11. // 例:store.acmecorp.com上のサイトの場合は「.acmecorp.com」
  12. constCOOKIE_DOMAIN = '.example.com';

  13. constMAX_AGE_SECONDS = 34560000; // 400日

  14. module.exports= function pagesenseSync(req, res) {
  15.     constisHttps = req.secure || req.headers['x-forwarded-proto']=== 'https';
  16.     constraw = req.headers.cookie || '';

  17.     constsetCookies = raw.split(';').reduce((acc, pair) => {
  18.         constidx = pair.indexOf('=');
  19.         if (idx < 0) returnacc;
  20.         constname = pair.slice(0,idx).trim();
  21.         constvalue = pair.slice(idx + 1).trim();
  22.         if (name.indexOf('zab')=== 0|| name.indexOf('zps')=== 0) {
  23. acc.push(
  24. name + '='+ value +
  25.                 '; Max-Age='+ MAX_AGE_SECONDS +
  26.                 '; Path=/'+
  27.                 '; Domain='+ COOKIE_DOMAIN +
  28.                 (isHttps ? '; Secure; SameSite=None': '; SameSite=Lax')
  29.                 // 意図的にHttpOnlyにしていません—トラッカーがこれらのCookieを読み取る必要があるためです
  30.             );
  31.         }
  32.         returnacc;
  33.     }, []);

  34.     if (setCookies.length) {
  35. res.setHeader('Set-Cookie',setCookies);
  36.     }
  37. res.setHeader('Cache-Control', 'no-store');
  38. res.status(204).end();
  39. };
Java(サーブレット)
  1. java
  2. import java.io.IOException;
  3. import javax.servlet.annotation.WebServlet;
  4. import javax.servlet.http.Cookie;
  5. import javax.servlet.http.HttpServlet;
  6. import javax.servlet.http.HttpServletRequest;
  7. import javax.servlet.http.HttpServletResponse;

  8. /**
  9. * PageSenseのCookie同期エンドポイント(Safari ITP対策)— Javaサーブレット。
  10. * Set-Cookieを使用してPageSenseのファーストパーティCookieを再発行し、Safariで
  11. * 有効期間全体が適用されるようにします。サイトと同じドメインにデプロイしてください。
  12. */
  13. @WebServlet('/pagesense-sync')
  14. public class PageSenseSyncServlet extends HttpServlet {

  15.     // --- 設定:先頭にドットを付けた登録可能なドメインに置き換えてください ---
  16.     // 例:「.acmecorp.com」(store.acmecorp.com上のサイトの場合)
  17.     private static final StringCOOKIE_DOMAIN = '.example.com';

  18.     private static final intMAX_AGE_SECONDS = 34560000; // 400日

  19.     @Override
  20.     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
  21.         booleanisHttps = request.isSecure()|| 'https'.equalsIgnoreCase(request.getHeader('X-Forwarded-Proto'));

  22.         Cookie[]cookies = request.getCookies();
  23.         if (cookies != null) {
  24.             for (Cookiecookie :cookies) {
  25.                 Stringname = cookie.getName();
  26.                 if (name.startsWith('zab')|| name.startsWith('zps')) {
  27.                     // SameSiteを設定するため、ヘッダーを手動で作成
  28.                     //(javax.servlet CookieはSameSiteをサポートしていません)
  29.                     StringBuilderheader = new StringBuilder();
  30. header.append(name).append('=').append(cookie.getValue())
  31.                           .append('; Max-Age=').append(MAX_AGE_SECONDS)
  32.                           .append('; Path=/')
  33.                           .append('; Domain=').append(COOKIE_DOMAIN);
  34.                     if (isHttps) {
  35. header.append('; Secure; SameSite=None');
  36.                     } else {
  37. header.append('; SameSite=Lax');
  38.                     }
  39.                     // 意図的にHttpOnlyにしていません — トラッカーがこれらのCookieを読み取る必要があります
  40. response.addHeader('Set-Cookie',header.toString());
  41.                 }
  42.             }
  43.         }

  44. response.setHeader('Cache-Control', 'no-store');
  45. response.setStatus(HttpServletResponse.SC_NO_CONTENT);
  46.     }
  47. }
.NET (ASP.NET Core)
  1. csharp
  2. using Microsoft.AspNetCore.Http;
  3. using Microsoft.AspNetCore.Mvc;
  4. using System;

  5. /// <summary>
  6. /// PageSenseのCookie同期エンドポイント(Safari ITP対策)— ASP.NET Core。
  7. /// Safariで有効期間全体が維持されるように、Set-Cookieを介して
  8. /// PageSenseのファーストパーティCookieを再発行します。サイトと同じドメインでホストしてください。
  9. /// </summary>
  10. [ApiController]
  11. public class PageSenseSyncController : ControllerBase
  12. {
  13.     // --- 設定:先頭にドットを付けた登録可能なドメインに置き換えてください ---
  14.     // 例:store.acmecorp.com上のサイトの場合は「.acmecorp.com」
  15.     private const stringCookieDomain = '.example.com';

  16.     private const intMaxAgeSeconds = 34560000; // 400日

  17.     [HttpGet('/pagesense-sync')]
  18.     public IActionResult Sync()
  19.     {
  20.         boolisHttps = Request.IsHttps
  21. || string.Equals(Request.Headers['X-Forwarded-Proto'], 'https',
  22. StringComparison.OrdinalIgnoreCase);

  23.         foreach (varcookie inRequest.Cookies)
  24.         {
  25.             if (cookie.Key.StartsWith('zab',StringComparison.Ordinal)
  26. || cookie.Key.StartsWith('zps',StringComparison.Ordinal))
  27.             {
  28. Response.Cookies.Append(cookie.Key,cookie.Value, new CookieOptions
  29.                 {
  30. MaxAge = TimeSpan.FromSeconds(MaxAgeSeconds),
  31. Path = '/',
  32. Domain = CookieDomain,
  33. Secure = isHttps,
  34. HttpOnly = false, // トラッカーがこれらのCookieを読み取れるようにする必要があります
  35. SameSite = isHttps ? SameSiteMode.None : SameSiteMode.Lax
  36.                 });
  37.             }
  38.         }

  39. Response.Headers['Cache-Control']= 'no-store';
  40.         return NoContent();
  41.     }
  42. }
手順2:エンドポイントの場所をPageSenseに指定  
次の行を、サイトの<head>内にあるPageSense SmartCodeの前に追加します。
  1. html
  2. <script>window.zpsITPSyncUrl = '/pagesense-sync';</script>
  3. <!-- 以下にPageSense SmartCode -->
以上で完了です。PageSenseは、SafariおよびiOSブラウザーで、訪問者1人につき1時間に最大1回、自動的にエンドポイントを呼び出します。

仕組み  

エンドポイントは、ブラウザーが各要求で送信するPageSenseのCookie(zabまたはzpsで始まるCookie)を読み取り、有効期限を400日に設定したSet-Cookieレスポンスヘッダーを使用して再発行します。サーバー側で設定されたCookieには、ITPの7日間の上限は適用されません。
エンドポイントは「Cache-Control: no-store」を指定して「204 No Content」を返すため、キャッシュされることなく、毎回新たに実行されます。Cookieでは「HttpOnly: false」が設定されます。これは、PageSenseのトラッカーがJavaScriptからCookieを読み取る必要があるため、意図的な設定です。

エンドポイントでできることとできないこと  

Cookieの有効期間をITPの7日間の上限より長くする

✅

再訪問時のA/Bテストにおけるバリエーションの一貫性を確保する

✅

複数のセッションにまたがる複数ステップのファネル追跡を修正する

✅

Safariで過大に計上されるユニーク訪問者数を修正する

✅

同じ登録可能ドメインのサブドメイン間で動作する

✅

クロスドメイントラッキングには対応しない(Safariによって完全にブロックされます)

❌


スプリットURLテストにおけるクロスドメイントラッキングの問題の解決

異なるドメイン間でスプリットURLテストを実行すると、クロスドメイントラッキングに必要なCookieがブラウザーによってブロックされる場合があります。その結果、バリエーションページへのリダイレクト後にPageSenseが訪問者を認識できず、テストの追跡が不完全または不正確になる可能性があります。この問題に対応するため、PageSenseではCookieだけに依存せず、テストに必要な情報を一時的にバリエーションURL経由で渡します。これにより、ブラウザーのプライバシー制限がクロスドメイントラッキングに影響する場合でも、訪問者を正確に追跡し続けることができます。
バリエーションページが情報を読み取ると、PageSenseはページを再読み込みすることなく、ブラウザーのアドレスバーから一時的な追跡パラメーターを自動的に削除します。訪問者には簡潔なURLが引き続き表示され、テストも正確に追跡されます。
例:次のページを使用してスプリットURLテストを実行するとします。
元のページ: https://www.example.com/pricing
バリエーションページ: https://www.newsite.com/pricing
訪問者が元のページにアクセスすると、PageSenseは訪問者をバリエーションに割り当て、一時的に次のURLへリダイレクトします。
各パラメーターの意味は次のとおりです。
  • ps_exp:テストを識別します。
  • ps_var:割り当てられたバリエーションを識別します。
  • ps_visitor:訪問者を識別します。
バリエーションページがこの情報を受信すると、PageSenseは一時的なパラメーターを削除し、訪問者には次のURLが表示されます。

仕組み  
  1. 訪問者が元のページにアクセスします。
  2. PageSenseが訪問者をバリエーションに割り当てます。
  3. 訪問者が、一時的なトラッキングパラメーターが付加されたバリエーションページにリダイレクトされます。
  4. バリエーションページが情報を読み取り、訪問者のトラッキングを継続します。
  5. PageSenseが、ページを再読み込みせずにURLから一時的なパラメーターを削除します。
メモ:この方法により、ブラウザーのプライバシー設定でクロスドメインCookieが制限されている場合でも、異なるドメイン間でスプリットURLテストのトラッキングを確実に行えます。

既知の制限事項:サーベイとアンケートの進捗状況  

Cookie同期エンドポイントではCookieベースのトラッキングを修正できますが、localStorageには対応できません。訪問者がPageSenseのサーベイまたはアンケートへの回答途中で、7日以内に完了しなかった場合、その進捗状況はlocalStorageに保存されますが、ITPによって期限切れになる可能性があります。
この場合、訪問者のCookieはサーバー側で設定され、有効期間も長いため、その訪問者がサーベイを表示済みであることを引き続き識別でき、PageSenseで再表示されることはありません。ただし、回答途中の内容は失われます。現在、この制限を回避する方法はありません。これはSafariによってlocalStorageに課される制約であり、Cookieの処理方法にかかわらず適用されます。

よくある質問  

1. これらのCookieは本当に400日間保持されますか?
Cookieは、Max-Age=34560000(400日。ブラウザーで許可される最大期間)として発行されます。ただし、PageSenseでは独自の論理的な有効期限が管理されます。各Cookieの想定有効期間は、メタデータCookie(zpsCookieMeta)に保存されます。PageSenseはこの情報を読み取り、想定有効期限を過ぎたCookieを存在しないものとして扱い、クライアント側で削除します。そのため、サーバー側の400日という有効期限は上限であり、データが実際に保持される期間を保証するものではありません。

2. このエンドポイントをホストすることによるセキュリティ上のリスクはありますか?
いいえ。このエンドポイントは、訪問者のブラウザーにすでに保存されているCookieの値を再送信するだけです。情報が保存されたり、記録されたり、Zohoに転送されたりすることはありません。同期スクリプトはすべてお客さま自身のサーバー上で実行され、Cookieが第三者に送信されることはありません。

3. Safari以外のブラウザーにも影響しますか?
いいえ。PageSenseのトラッカーが同期エンドポイントを呼び出すのは、ITPが有効なSafariとiOSブラウザーのみです。Chrome、Firefox、Edgeには影響しません。

4. サイトが複数のドメインにまたがっている場合でも機能しますか?
いいえ。Cookieのドメインは、PageSenseがインストールされているサイトの登録可能なドメインと一致している必要があります。完全に異なるドメイン間(例:siteA.comとsiteB.com)のトラッキングはSafariではサポートされておらず、この方法でも対応できません。

5. 訪問者がパラメーターの付加されていないリンクからアクセスした場合はどうなりますか?
追加の対応は必要ありません。訪問者のURLにクエリ文字列やフラグメントが含まれていない場合、ITP 2.2の24時間制限は適用されません。PageSenseに組み込まれたlocalStorageへのフォールバックにより、通常の7日間の期間が自動的に処理されます。同期エンドポイントは主に、UTMパラメーターやその他のクエリ文字列が付加されたキャンペーンリンクからアクセスする訪問者に有効です。






このドキュメントは、すべての手順を分かりやすく進められるように作成されています。さらにサポートが必要な場合やご質問がある場合は、support@zohopagesense.com - までお気軽にお問い合わせください。いつでもサポートいたします。