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によってサイト横断トラッカーに分類されたドメインから発信され、クエリー文字列またはフラグメント(例:?utm_source=newsletter#section)を含むURLです。
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,  # トラッカーがこれらのCookieを読み取れるようにする必要があります
  33.                 samesite='None' ifis_https else 'Lax',
  34.             )

  35.     returnresponse

Node.js (Express)
  1. javascript
  2. /**
  3. * PageSenseのCookie同期エンドポイント(Safari ITP対応)— Node.js/Express。
  4. * SafariでCookieの有効期間全体が維持されるように、Set-Cookieを使用して
  5. * PageSenseのファーストパーティCookieを再発行します。サイトと同じドメインにマウントしてください。
  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には設定していません—トラッカーがこれらのクッキーを読み取る必要があります
  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のクッキー同期エンドポイント(Safari ITP対策)— Java Servlet。
  10. * Safariでクッキーの有効期間全体が維持されるよう、PageSenseのファーストパーティクッキーを
  11. * Set-Cookieを介して再発行します。サイトと同じドメインにデプロイしてください。
  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. /// Set-Cookieを使用してPageSenseのファーストパーティCookieを再発行し、Safariで
  8. /// Cookieの有効期間全体が維持されるようにします。サイトと同じドメインでホストしてください。
  9. /// </summary>
  10. [ApiController]
  11. public class PageSenseSyncController : ControllerBase
  12. {
  13.     // --- 設定:先頭にドットを付けた登録可能なドメインに置き換えてください ---
  14.     // 例:'.acmecorp.com'(store.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へのエンドポイントの場所の指定  
次の行をサイトのPageSense SmartCodeの前(<head>内)に追加します。
  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では完全にブロックされます)


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

異なるドメインをまたいでSplit URLテストを実行すると、クロスドメイントラッキングに必要なCookieがブラウザーによってブロックされることがあります。その結果、バリエーションページへのリダイレクト後にPageSenseが訪問者を認識できず、テストの追跡が不完全または不正確になる可能性があります。この問題に対応するため、PageSenseではCookieのみに依存せず、必要なテスト情報をバリエーションURLを通じて一時的に渡します。これにより、ブラウザーのプライバシー制限がクロスドメイントラッキングに影響する場合でも、訪問者を正確に追跡し続けることができます。
バリエーションページが情報を読み取ると、PageSenseはページを再読み込みせずに、ブラウザーのアドレスバーから一時的な追跡パラメーターを自動的に削除します。訪問者には簡潔なURLが引き続き表示され、テストは正確に追跡されます。
例:次のページを使用してSplit URLテストを実行するとします。
バリエーションページ: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.comsiteB.com)のトラッキングはSafariではサポートされておらず、この方法では対応できません。

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






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