<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Meta-Pixel on Tarragon</title><link>https://tarrragon.github.io/blog/tags/meta-pixel/</link><description>Recent content in Meta-Pixel on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Fri, 23 Oct 2026 00:00:00 +0800</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/meta-pixel/index.xml" rel="self" type="application/rss+xml"/><item><title>Meta Pixel 與 Conversions API：廣告平台轉換追蹤的瀏覽器端與伺服器端</title><link>https://tarrragon.github.io/blog/devops/monitoring/vendors/meta-pixel-conversions-api/</link><pubDate>Thu, 22 Oct 2026 00:00:00 +0800</pubDate><guid>https://tarrragon.github.io/blog/devops/monitoring/vendors/meta-pixel-conversions-api/</guid><description>&lt;p>這篇整理 Meta（Facebook / Instagram 廣告）的網站轉換追蹤：瀏覽器端的 Meta Pixel、伺服器端的 Conversions API（常簡稱 CAPI），以及兩條路徑並用時的去重與驗證。範圍是網站事件；App 端的事件走 Meta 的 App SDK，不在這篇。欄位規格以 Meta 開發者文件為準，本篇引用的規則（必填欄位、去重的時間窗、&lt;code>event_time&lt;/code> 的上限）都會隨 Meta 改版調整，接入時回官方文件核對一次。&lt;/p>
&lt;h2 id="廣告平台轉換追蹤的資料消費者">廣告平台轉換追蹤的資料消費者&lt;/h2>
&lt;p>Meta Pixel 送出的事件，主要的消費者是 Meta 的廣告投放系統：投放演算法依轉換事件學習該把廣告給誰看，廣告後台依它計算每一檔廣告帶來幾筆轉換。這一點讓它跟 &lt;a href="https://tarrragon.github.io/blog/devops/monitoring/06-commercial-comparison/mixpanel-amplitude/" data-link-title="Mixpanel / Amplitude" data-link-desc="行為分析專用方案 vs 通用監控的差異 — Mixpanel 和 Amplitude 的 funnel / cohort / retention 分析能力">Mixpanel / Amplitude&lt;/a> 這類行為分析工具分開——行為分析的消費者是自己的產品團隊，事件怎麼命名、帶什麼屬性由自己決定；廣告平台的事件要讓平台的演算法讀得懂，所以命名照平台的規定。&lt;/p>
&lt;p>Meta 定義了一組標準事件（standard events）：&lt;code>PageView&lt;/code>、&lt;code>ViewContent&lt;/code>、&lt;code>AddToCart&lt;/code>、&lt;code>InitiateCheckout&lt;/code>、&lt;code>Purchase&lt;/code>、&lt;code>Lead&lt;/code>、&lt;code>CompleteRegistration&lt;/code> 等。廣告活動的最佳化目標綁在這些事件名稱上，例如以「購買」為目標的廣告活動讀的是 &lt;code>Purchase&lt;/code>；&lt;code>Purchase&lt;/code> 另外要帶 &lt;code>value&lt;/code> 與 &lt;code>currency&lt;/code>，平台才算得出廣告投資報酬。自己的事件命名（&lt;a href="https://tarrragon.github.io/blog/devops/monitoring/01-mental-model/event-naming-convention/" data-link-title="事件命名規範" data-link-desc="namespace.action 格式的事件命名、命名一致性的工程價值、和商業方案命名慣例的對應">事件命名規範&lt;/a> 的 &lt;code>namespace.action&lt;/code>）因此需要一層對映：&lt;code>checkout.complete&lt;/code> 送到自己的 collector，同一筆在送往 Meta 時改名成 &lt;code>Purchase&lt;/code>。對映表寫在一個地方、由工程維護，比散在各個埋點呼叫裡改名好查。&lt;/p>
&lt;p>廣告平台各自用自己看得到的事件做歸因，同一筆訂單可能同時被 Meta 與 Google Ads 算成自己帶來的轉換，兩個後台的轉換數加總會大於實際訂單數。跨渠道的歸因模型見 &lt;a href="https://tarrragon.github.io/blog/devops/monitoring/08-business-analytics/attribution/" data-link-title="Attribution" data-link-desc="使用者從哪來、哪個渠道帶來轉換 — last-touch / first-touch / multi-touch 歸因模型的差異和選擇">Attribution&lt;/a>。&lt;/p>
&lt;h2 id="meta-pixel-在瀏覽器裡做的事">Meta Pixel 在瀏覽器裡做的事&lt;/h2>
&lt;p>Pixel 的基本碼從 &lt;code>connect.facebook.net&lt;/code> 載入 &lt;code>fbevents.js&lt;/code>，再用 &lt;code>fbq()&lt;/code> 這個全域函式初始化並送事件。&lt;code>fbevents.js&lt;/code> 載入後會再向 Meta 取得這個 Pixel 的設定檔，設定檔載入的模組（plugin，&lt;code>fbevents.js&lt;/code> 依 Pixel 設定檔載入的程式片段）會在網站沒有呼叫 &lt;code>fbq()&lt;/code> 的情況下&lt;a href="https://tarrragon.github.io/blog/devops/monitoring/knowledge-cards/autocapture/" data-link-title="Autocapture（自動事件收集）" data-link-desc="盤點網站實際送出哪些事件與內容、報表裡出現網站沒有呼叫過的事件，或在手動埋點與自動收集之間取捨時">自己送事件&lt;/a>，例如依按鈕點擊送出的 &lt;code>SubscribedButtonClick&lt;/code>，內容包含按鈕文字；實測見 &lt;a href="https://tarrragon.github.io/blog/devops/monitoring/vendors/tracking-request-capture/" data-link-title="用 Playwright 擷取追蹤碼請求：擷取方式、記錄欄位與各次結果" data-link-desc="以 Playwright 操作 Meta Pixel 與 GTM 實測頁、記錄各家 script 實際送出的請求與 cookie 的方式，結果檔記錄的欄位與它們的用途，擷取用的測試帳號，以及每一次擷取觀察到的事件請求形式、cookie 與請求參數的對應、無頭瀏覽器下的差異與 script 自動送出的事件">用 Playwright 擷取追蹤碼請求&lt;/a> 的 Meta Pixel 有視窗那一次。&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl"> &lt;span class="c1">// Meta 提供的載入片段（略）：定義 fbq 並從 connect.facebook.net 載入 fbevents.js
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">fbq&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;init&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;PIXEL_ID&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// PIXEL_ID 換成 Events Manager 裡那個 Pixel 的 ID
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">fbq&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;track&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;PageView&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// 每一頁載入時送一次
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>fbq('init', ...)&lt;/code> 的第二個引數是 Pixel ID，每個 Pixel 在 Meta Events Manager 裡有自己的一串數字，一個網站可以初始化多個 Pixel。&lt;code>PIXEL_ID&lt;/code> 在這裡是佔位值，要換成自己 Events Manager 裡顯示的那一串。&lt;/p>
&lt;p>送轉換事件時，&lt;code>fbq('track', ...)&lt;/code> 依序接事件名稱、事件參數、以及選填的第四個引數：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1">// 第二個引數是標準事件名稱，第三個是事件參數，第四個帶這一筆事件的唯一 ID
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">fbq&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;track&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;Purchase&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl"> &lt;span class="p">{&lt;/span> &lt;span class="nx">value&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">1280&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">currency&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;TWD&amp;#39;&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl"> &lt;span class="p">{&lt;/span> &lt;span class="nx">eventID&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;order-20261022-0042&amp;#39;&lt;/span> &lt;span class="p">});&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>value&lt;/code> 與 &lt;code>currency&lt;/code> 是這一筆購買的金額與幣別，從訂單資料取值。&lt;code>eventID&lt;/code> 是這一筆事件的識別碼，只送 Pixel 的時候可以省略；同一筆事件還要從伺服器再送一次時就必須帶，原因在〈兩路並送時的去重規則〉一節。&lt;/p>
&lt;p>Pixel 也在網站自己的網域上寫 cookie：&lt;code>_fbp&lt;/code> 記錄這個瀏覽器的識別碼；使用者從廣告點進來時，落地網址會帶 &lt;code>fbclid&lt;/code> 參數，Pixel 把它存成 &lt;code>_fbc&lt;/code>。這兩個 cookie 是&lt;a href="https://tarrragon.github.io/blog/devops/monitoring/knowledge-cards/first-party-cookie/" data-link-title="First-party Cookie（第一方 cookie）" data-link-desc="盤點網站寫下的識別碼 cookie、比對追蹤碼的 cookie 期限，或判斷某個 cookie 受不受瀏覽器的第三方 cookie 限制時">第一方 cookie&lt;/a>（寫在網站自己的網域上），之後伺服器端送事件時會用到它們的值。&lt;/p></description><content:encoded><![CDATA[<p>這篇整理 Meta（Facebook / Instagram 廣告）的網站轉換追蹤：瀏覽器端的 Meta Pixel、伺服器端的 Conversions API（常簡稱 CAPI），以及兩條路徑並用時的去重與驗證。範圍是網站事件；App 端的事件走 Meta 的 App SDK，不在這篇。欄位規格以 Meta 開發者文件為準，本篇引用的規則（必填欄位、去重的時間窗、<code>event_time</code> 的上限）都會隨 Meta 改版調整，接入時回官方文件核對一次。</p>
<h2 id="廣告平台轉換追蹤的資料消費者">廣告平台轉換追蹤的資料消費者</h2>
<p>Meta Pixel 送出的事件，主要的消費者是 Meta 的廣告投放系統：投放演算法依轉換事件學習該把廣告給誰看，廣告後台依它計算每一檔廣告帶來幾筆轉換。這一點讓它跟 <a href="/blog/devops/monitoring/06-commercial-comparison/mixpanel-amplitude/" data-link-title="Mixpanel / Amplitude" data-link-desc="行為分析專用方案 vs 通用監控的差異 — Mixpanel 和 Amplitude 的 funnel / cohort / retention 分析能力">Mixpanel / Amplitude</a> 這類行為分析工具分開——行為分析的消費者是自己的產品團隊，事件怎麼命名、帶什麼屬性由自己決定；廣告平台的事件要讓平台的演算法讀得懂，所以命名照平台的規定。</p>
<p>Meta 定義了一組標準事件（standard events）：<code>PageView</code>、<code>ViewContent</code>、<code>AddToCart</code>、<code>InitiateCheckout</code>、<code>Purchase</code>、<code>Lead</code>、<code>CompleteRegistration</code> 等。廣告活動的最佳化目標綁在這些事件名稱上，例如以「購買」為目標的廣告活動讀的是 <code>Purchase</code>；<code>Purchase</code> 另外要帶 <code>value</code> 與 <code>currency</code>，平台才算得出廣告投資報酬。自己的事件命名（<a href="/blog/devops/monitoring/01-mental-model/event-naming-convention/" data-link-title="事件命名規範" data-link-desc="namespace.action 格式的事件命名、命名一致性的工程價值、和商業方案命名慣例的對應">事件命名規範</a> 的 <code>namespace.action</code>）因此需要一層對映：<code>checkout.complete</code> 送到自己的 collector，同一筆在送往 Meta 時改名成 <code>Purchase</code>。對映表寫在一個地方、由工程維護，比散在各個埋點呼叫裡改名好查。</p>
<p>廣告平台各自用自己看得到的事件做歸因，同一筆訂單可能同時被 Meta 與 Google Ads 算成自己帶來的轉換，兩個後台的轉換數加總會大於實際訂單數。跨渠道的歸因模型見 <a href="/blog/devops/monitoring/08-business-analytics/attribution/" data-link-title="Attribution" data-link-desc="使用者從哪來、哪個渠道帶來轉換 — last-touch / first-touch / multi-touch 歸因模型的差異和選擇">Attribution</a>。</p>
<h2 id="meta-pixel-在瀏覽器裡做的事">Meta Pixel 在瀏覽器裡做的事</h2>
<p>Pixel 的基本碼從 <code>connect.facebook.net</code> 載入 <code>fbevents.js</code>，再用 <code>fbq()</code> 這個全域函式初始化並送事件。<code>fbevents.js</code> 載入後會再向 Meta 取得這個 Pixel 的設定檔，設定檔載入的模組（plugin，<code>fbevents.js</code> 依 Pixel 設定檔載入的程式片段）會在網站沒有呼叫 <code>fbq()</code> 的情況下<a href="/blog/devops/monitoring/knowledge-cards/autocapture/" data-link-title="Autocapture（自動事件收集）" data-link-desc="盤點網站實際送出哪些事件與內容、報表裡出現網站沒有呼叫過的事件，或在手動埋點與自動收集之間取捨時">自己送事件</a>，例如依按鈕點擊送出的 <code>SubscribedButtonClick</code>，內容包含按鈕文字；實測見 <a href="/blog/devops/monitoring/vendors/tracking-request-capture/" data-link-title="用 Playwright 擷取追蹤碼請求：擷取方式、記錄欄位與各次結果" data-link-desc="以 Playwright 操作 Meta Pixel 與 GTM 實測頁、記錄各家 script 實際送出的請求與 cookie 的方式，結果檔記錄的欄位與它們的用途，擷取用的測試帳號，以及每一次擷取觀察到的事件請求形式、cookie 與請求參數的對應、無頭瀏覽器下的差異與 script 自動送出的事件">用 Playwright 擷取追蹤碼請求</a> 的 Meta Pixel 有視窗那一次。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="ln">1</span><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">  <span class="c1">// Meta 提供的載入片段（略）：定義 fbq 並從 connect.facebook.net 載入 fbevents.js
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1"></span>  <span class="nx">fbq</span><span class="p">(</span><span class="s1">&#39;init&#39;</span><span class="p">,</span> <span class="s1">&#39;PIXEL_ID&#39;</span><span class="p">);</span>   <span class="c1">// PIXEL_ID 換成 Events Manager 裡那個 Pixel 的 ID
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"></span>  <span class="nx">fbq</span><span class="p">(</span><span class="s1">&#39;track&#39;</span><span class="p">,</span> <span class="s1">&#39;PageView&#39;</span><span class="p">);</span>  <span class="c1">// 每一頁載入時送一次
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"></span><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span></span></span></code></pre></div><p><code>fbq('init', ...)</code> 的第二個引數是 Pixel ID，每個 Pixel 在 Meta Events Manager 裡有自己的一串數字，一個網站可以初始化多個 Pixel。<code>PIXEL_ID</code> 在這裡是佔位值，要換成自己 Events Manager 裡顯示的那一串。</p>
<p>送轉換事件時，<code>fbq('track', ...)</code> 依序接事件名稱、事件參數、以及選填的第四個引數：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1">// 第二個引數是標準事件名稱，第三個是事件參數，第四個帶這一筆事件的唯一 ID
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"></span><span class="nx">fbq</span><span class="p">(</span><span class="s1">&#39;track&#39;</span><span class="p">,</span> <span class="s1">&#39;Purchase&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">  <span class="p">{</span> <span class="nx">value</span><span class="o">:</span> <span class="mi">1280</span><span class="p">,</span> <span class="nx">currency</span><span class="o">:</span> <span class="s1">&#39;TWD&#39;</span> <span class="p">},</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">  <span class="p">{</span> <span class="nx">eventID</span><span class="o">:</span> <span class="s1">&#39;order-20261022-0042&#39;</span> <span class="p">});</span></span></span></code></pre></div><p><code>value</code> 與 <code>currency</code> 是這一筆購買的金額與幣別，從訂單資料取值。<code>eventID</code> 是這一筆事件的識別碼，只送 Pixel 的時候可以省略；同一筆事件還要從伺服器再送一次時就必須帶，原因在〈兩路並送時的去重規則〉一節。</p>
<p>Pixel 也在網站自己的網域上寫 cookie：<code>_fbp</code> 記錄這個瀏覽器的識別碼；使用者從廣告點進來時，落地網址會帶 <code>fbclid</code> 參數，Pixel 把它存成 <code>_fbc</code>。這兩個 cookie 是<a href="/blog/devops/monitoring/knowledge-cards/first-party-cookie/" data-link-title="First-party Cookie（第一方 cookie）" data-link-desc="盤點網站寫下的識別碼 cookie、比對追蹤碼的 cookie 期限，或判斷某個 cookie 受不受瀏覽器的第三方 cookie 限制時">第一方 cookie</a>（寫在網站自己的網域上），之後伺服器端送事件時會用到它們的值。</p>
<h2 id="瀏覽器端事件的損失路徑">瀏覽器端事件的損失路徑</h2>
<p>Pixel 的事件從讀者的瀏覽器發出，所以它會在讀者端被擋下，而網站這一端收不到任何錯誤：</p>
<ul>
<li><strong>廣告攔截器擋下 script 本身</strong>：攔截清單收錄 <code>connect.facebook.net</code> 這類已知追蹤網域，<code>fbevents.js</code> 載不進來，<code>fbq()</code> 之後的所有事件都不會送出。</li>
<li><strong>瀏覽器的追蹤防護縮短 cookie 壽命</strong>：Safari 的 Intelligent Tracking Prevention（ITP）會限制由 JavaScript 寫入的第一方 cookie 的有效期，<code>_fbp</code> 因此比預期更早消失：一般情形是使用者 7 天沒有互動之後刪除；從 Meta 廣告帶著 <code>fbclid</code> 進站那一次在落地頁寫下的 cookie，上限只有 24 小時，所以同一個人點了廣告、過了一天以上才回來購買時，就可能被當成另一個瀏覽器（期限規則見 <a href="/blog/devops/monitoring/knowledge-cards/first-party-cookie/" data-link-title="First-party Cookie（第一方 cookie）" data-link-desc="盤點網站寫下的識別碼 cookie、比對追蹤碼的 cookie 期限，或判斷某個 cookie 受不受瀏覽器的第三方 cookie 限制時">First-party Cookie（第一方 cookie）</a>）；部分瀏覽器與 App 內開啟的連結也會移除網址裡的 <code>fbclid</code>。</li>
<li><strong>同意聲明還沒取得</strong>：在要求事先同意的法域（例如 GDPR 下的歐盟讀者），網站在使用者同意之前不能載入 Pixel，這段期間的事件依規定就不送。</li>
<li><strong>自動化與機器人流量過濾</strong>：實測的這個 Pixel，設定檔裡的模組依 User-Agent 判斷機器人流量（模組由 Meta 依各 Pixel 的設定檔下發），<a href="/blog/devops/monitoring/knowledge-cards/headless-browser/" data-link-title="Headless Browser（無頭瀏覽器）" data-link-desc="用 Playwright 或 Puppeteer 測追蹤碼、分析報表裡的機器流量，或自建統計與商業追蹤碼的訪問數對不上時">無頭瀏覽器</a>（User-Agent 帶 <code>HeadlessChrome</code>）送出的事件在瀏覽器裡就被攔下。<code>fbevents.js</code> 照常載入、cookie 照常寫入，網站這一端看不出事件沒送；用自動化工具驗證追蹤設定時會撞上這一條，實測見 <a href="/blog/devops/monitoring/vendors/tracking-request-capture/" data-link-title="用 Playwright 擷取追蹤碼請求：擷取方式、記錄欄位與各次結果" data-link-desc="以 Playwright 操作 Meta Pixel 與 GTM 實測頁、記錄各家 script 實際送出的請求與 cookie 的方式，結果檔記錄的欄位與它們的用途，擷取用的測試帳號，以及每一次擷取觀察到的事件請求形式、cookie 與請求參數的對應、無頭瀏覽器下的差異與 script 自動送出的事件">用 Playwright 擷取追蹤碼請求</a> 的無頭模式那一次。</li>
<li><strong>跳頁中斷</strong>：購買按鈕按下後立刻導向感謝頁，送事件的請求可能還沒送完頁面就被卸載。這是所有前端事件追蹤共通的狀況，機制與 <code>sendBeacon</code> 的解法見 <a href="/blog/devops/monitoring/05-platform-adaptation/js-ts-platform/" data-link-title="JS/TS 平台適配" data-link-desc="CORS 限制、Service Worker 攔截、SPA 路由變換偵測 — 瀏覽器環境中 SDK 需要處理的平台特殊問題">JS/TS 平台適配</a>。</li>
</ul>
<p>每個網站被擋下的比例取決於讀者組成（裝攔截器的比例、iOS 的比例、歐盟流量的比例），網路上流傳的百分比是別人網站的數字。量自己網站的方式是對帳：同一段期間內，訂單資料庫裡的訂單數與 Events Manager 收到的 <code>Purchase</code> 數相比，差距就是這個網站的瀏覽器端損失。</p>
<h2 id="conversions-api從伺服器送同一批事件">Conversions API：從伺服器送同一批事件</h2>
<p>Conversions API 讓網站的伺服器直接把事件 POST 到 Meta 的 Graph API（<code>/&lt;PIXEL_ID&gt;/events</code>），請求不經過讀者的瀏覽器，所以廣告攔截器與 cookie 期限都影響不到它。代價是伺服器要自己準備瀏覽器原本會自動附上的資訊：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">  <span class="nt">&#34;data&#34;</span><span class="p">:</span> <span class="p">[{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">    <span class="nt">&#34;event_name&#34;</span><span class="p">:</span> <span class="s2">&#34;Purchase&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="nt">&#34;event_time&#34;</span><span class="p">:</span> <span class="mi">1792656000</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="nt">&#34;event_id&#34;</span><span class="p">:</span> <span class="s2">&#34;order-20261022-0042&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="nt">&#34;action_source&#34;</span><span class="p">:</span> <span class="s2">&#34;website&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">    <span class="nt">&#34;event_source_url&#34;</span><span class="p">:</span> <span class="s2">&#34;https://shop.example.com/checkout/thanks&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="nt">&#34;user_data&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">      <span class="nt">&#34;client_user_agent&#34;</span><span class="p">:</span> <span class="s2">&#34;&lt;下單時瀏覽器的 User-Agent&gt;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">      <span class="nt">&#34;client_ip_address&#34;</span><span class="p">:</span> <span class="s2">&#34;&lt;下單時的來源 IP&gt;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">      <span class="nt">&#34;fbp&#34;</span><span class="p">:</span> <span class="s2">&#34;&lt;讀自 _fbp cookie&gt;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">      <span class="nt">&#34;fbc&#34;</span><span class="p">:</span> <span class="s2">&#34;&lt;讀自 _fbc cookie&gt;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">      <span class="nt">&#34;em&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;&lt;正規化後的 email 的 SHA-256&gt;&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="nt">&#34;custom_data&#34;</span><span class="p">:</span> <span class="p">{</span> <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="mi">1280</span><span class="p">,</span> <span class="nt">&#34;currency&#34;</span><span class="p">:</span> <span class="s2">&#34;TWD&#34;</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">  <span class="p">}]</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>欄位的來源與限制：</p>
<ul>
<li><code>event_name</code>、<code>event_id</code>、<code>custom_data</code> 與 Pixel 那一筆相同，名稱的對映方式見〈廣告平台轉換追蹤的資料消費者〉一節。</li>
<li><code>event_time</code> 是事件發生的 Unix 時間（秒）。Meta 接受的範圍是送出當下往前七天內；一批資料裡只要有一筆超過七天，整個請求會被拒絕、一筆都不處理，所以用排程補送積壓的事件時要先過濾掉過期的那些。</li>
<li><code>action_source</code> 標明事件發生在哪裡，網站事件填 <code>website</code>。Meta 對網站事件另外要求 <code>event_source_url</code>（事件發生的頁面網址，網域要與 Meta 後台驗證過的網域一致）與 <code>client_user_agent</code>。</li>
<li><code>user_data</code> 是 Meta 用來把這筆事件對回某個 Meta 使用者的資料。<code>fbp</code>、<code>fbc</code> 從請求帶上來的 cookie 讀，<code>client_ip_address</code> 與 <code>client_user_agent</code> 從下單那一次請求的 header 讀，這四個欄位原樣送出；email、電話這類個人資料要先依 Meta 規定的方式正規化（去空白、轉小寫等），再做 SHA-256 雜湊才送。給的欄位越多，Events Manager 顯示的事件比對品質（Event Match Quality）越高。</li>
</ul>
<p>請求要帶 Meta 發給這個 Pixel 的 access token，這個 token 只能放在伺服器上：放進前端程式碼等於公開，任何人都能用它往這個 Pixel 灌假事件。前端程式碼裡的存取憑證會被提取的機制見 <a href="/blog/devops/monitoring/07-security-privacy/client-sdk-authentication/" data-link-title="Client-side SDK 認證的根本限制" data-link-desc="嵌在 client 端的 credential 必然可被提取 — 認清 architecture 天花板後的多層緩解策略，從 origin 驗證到 device attestation">Client-side SDK 認證的根本限制</a>。<code>user_data</code> 裡雜湊過的 email 仍然是可以對回個人的資料，送出前的同意與用途說明照 <a href="/blog/devops/monitoring/07-security-privacy/gdpr-minimization/" data-link-title="GDPR 最小化原則的工程落地" data-link-desc="資料最小化、目的限制、儲存限制 — GDPR 三個核心原則在監控系統的工程實作方式">GDPR 最小化原則的工程落地</a> 的原則處理。</p>
<h2 id="兩路並送時的去重規則">兩路並送時的去重規則</h2>
<p>Meta 建議 Pixel 與 Conversions API 並用：Pixel 送得到的時候帶著完整的瀏覽器資訊，送不到的那些由伺服器補上。兩條路徑都送成功的事件會被 Meta 收到兩次，所以 Meta 用兩個欄位判斷是不是同一筆：Pixel 的 <code>eventID</code> 與伺服器的 <code>event_id</code> 相同、且兩邊的事件名稱相同。依 Meta 文件，去重只在同一個 <code>event_id</code> 第一次送達後的 48 小時內生效；兩筆幾乎同時抵達時 Meta 保留瀏覽器那一筆；48 小時內沒有收到瀏覽器那一筆時，伺服器那一筆照常計入。</p>
<p>這條規則決定了 <code>event_id</code> 要在哪裡產生：在事件的源頭產生一次，再同時交給前端與後端。訂單編號符合這個條件，前後端都拿得到同一個值：感謝頁從訂單資料讀出編號放進 <code>fbq</code> 的第四個引數，伺服器在建立訂單時用同一個編號送 Conversions API。兩邊各自產生一個隨機 UUID 是常見的錯法，兩個值永遠對不上，每一筆轉換都會被算兩次，廣告後台的轉換數膨脹而沒有任何錯誤訊息。</p>
<p>Meta 另有一種用 <code>event_name</code> 加 <code>fbp</code> 或 <code>external_id</code> 比對的去重方式，文件註明它通常只在瀏覽器那一筆先到、伺服器那一筆後到的順序下有效，所以只當作 <code>event_id</code> 的備援。</p>
<p>去重的道理與自架 collector 處理 SDK 重送是同一件事：重送的事件要靠一個在源頭就決定的 ID 才認得出來，見 <a href="/blog/devops/monitoring/04-collector/data-integrity/" data-link-title="端到端資料完整性" data-link-desc="從 SDK 到 storage 的資料損失地圖 — 每個環節的損失類型、控制策略、完整性指標、被自己 SDK DDoS 的防護">端到端資料完整性</a>。</p>
<h2 id="用-test-events-驗證送出的事件">用 Test Events 驗證送出的事件</h2>
<p>Events Manager 在每個 Pixel 底下有 Test Events 頁面，它即時列出這個 Pixel 收到的事件、來源是瀏覽器還是伺服器、以及去重的結果。兩條路徑的驗證方式不同：</p>
<ul>
<li><strong>瀏覽器端</strong>：在 Test Events 頁面輸入網站網址開啟，或在瀏覽器裝 Meta Pixel Helper 擴充功能，操作一次購買流程，確認 <code>Purchase</code> 出現且帶著 <code>value</code>、<code>currency</code> 與 <code>eventID</code>。</li>
<li><strong>伺服器端</strong>：Test Events 頁面會給一組測試代碼，伺服器送 Conversions API 時在請求裡加上 <code>test_event_code</code> 欄位，那批事件就會出現在測試視窗裡。正式上線前要把這個欄位拿掉；帶測試代碼的事件會不會計入正式報表，以 Events Manager 當下的說明為準。</li>
</ul>
<p>驗證去重時兩條路徑一起跑：同一筆測試訂單在 Test Events 裡應該看到瀏覽器與伺服器各一筆，並標示其中一筆被去重。只看到一筆時，先確認另一條路徑確實沒送，還是 <code>event_id</code> 對不上而被當成兩筆不同的事件。</p>
<h2 id="和自架事件追蹤並存的資料流">和自架事件追蹤並存的資料流</h2>
<p>接了 Pixel 與 Conversions API 之後，同一個業務事件（一筆購買）會往三個地方送：自己的 collector、Meta 的瀏覽器端、Meta 的伺服器端。業務事件的定義只寫一次，送往各家時各自做名稱與欄位的對映，比在各個埋點位置分別呼叫三家的 API 好維護，改一個欄位不必找遍三種呼叫。這個分流結構與它的保留期、PII 取捨，在 <a href="/blog/devops/monitoring/telemetry-data-dual-use/" data-link-title="監控資料的雙重用途：行為分析與訊號治理" data-link-desc="同一份 event data 如何同時服務行為分析（funnel / cohort / attribution）和訊號治理（cardinality / cost / signal governance）— 格式交叉、治理衝突與分流架構">監控資料的雙重用途</a> 有完整的推導。</p>
<p>本篇描述的瀏覽器端行為可以在 <a href="/blog/devops/monitoring/vendors/meta-pixel-lab/" data-link-title="Meta Pixel 實測頁：在瀏覽器裡觀察 fbq 呼叫、送出的請求與寫下的 cookie" data-link-desc="在這一頁按按鈕載入 Meta Pixel、送 PageView、ViewContent 與帶 eventID 的 Purchase，並用頁面上的面板對照每個呼叫送出的請求參數，以及 _fbp 與 fbclid 帶出的 _fbc cookie">Meta Pixel 實測頁</a> 按按鈕觸發、對照它實際送出的請求與 cookie。追蹤碼要由誰、用什麼流程放上網站，是另一層的決定。行銷團隊常用 tag manager 自行加 Pixel，那條路徑的約定與治理見 <a href="/blog/devops/monitoring/vendors/google-tag-manager/" data-link-title="Google Tag Manager：追蹤碼的部署層、資料層約定與發布治理" data-link-desc="GTM 容器在頁面上承擔的部署責任、資料層作為前端與行銷之間的事件約定、預覽模式與版本發布的驗證方式、自訂 HTML 代碼帶來的安全與效能成本，以及伺服器端代碼管理改變的資料路徑">Google Tag Manager</a>。Pixel 的資料集屬於哪一個商家資產管理組合、建立之後能不能轉給另一個組合，以及委託代理商時資料集該建在誰的組合裡，見 <a href="/blog/devops/monitoring/vendors/tracking-account-ownership/" data-link-title="追蹤資產的歸屬：Meta、GTM 與 GA4 的擁有層級、權限分級與移交方式" data-link-desc="Meta Pixel 的資料集、GTM 容器與 GA4 資源（property）各自屬於哪一層的擁有者，三家的權限怎麼分級，哪些資產能換到另一個擁有者底下，以及公司自營、委託代理商、換代理商與事業轉手時的歸屬安排">追蹤資產的歸屬</a>。</p>
]]></content:encoded></item><item><title>Meta Pixel 實測頁：在瀏覽器裡觀察 fbq 呼叫、送出的請求與寫下的 cookie</title><link>https://tarrragon.github.io/blog/devops/monitoring/vendors/meta-pixel-lab/</link><pubDate>Thu, 22 Oct 2026 00:00:00 +0800</pubDate><guid>https://tarrragon.github.io/blog/devops/monitoring/vendors/meta-pixel-lab/</guid><description>&lt;p>這一頁是 &lt;a href="https://tarrragon.github.io/blog/devops/monitoring/vendors/meta-pixel-conversions-api/" data-link-title="Meta Pixel 與 Conversions API：廣告平台轉換追蹤的瀏覽器端與伺服器端" data-link-desc="Meta Pixel 在頁面上送出的標準事件與它的資料消費者、瀏覽器端事件被攔截的路徑、Conversions API 從伺服器補送同一批事件的必填欄位，以及兩路並送時以 event_id 去重與用 Test Events 驗證的方式">Meta Pixel 與 Conversions API&lt;/a> 的實測頁，範圍是瀏覽器端的 Pixel：載入 &lt;code>fbevents.js&lt;/code>、用 &lt;code>fbq()&lt;/code> 送事件，以及 Pixel 寫在網站網域上的 cookie。Conversions API 是伺服器端的路徑，在瀏覽器裡看不到，不在這一頁。&lt;/p>
&lt;h2 id="這一頁載入什麼什麼時候載入">這一頁載入什麼、什麼時候載入&lt;/h2>
&lt;p>頁面打開時不載入任何 Meta 的 script。按下「載入 Meta Pixel 並送 PageView」之後，面板才把 &lt;code>fbevents.js&lt;/code> 從 &lt;code>connect.facebook.net&lt;/code> 注入頁面，並用填在欄位裡的 Pixel ID 初始化；在那之前這一頁沒有任何送往 Meta 的請求，請求清單可以直接驗證這一點。注入的 script 在重新整理或離開這一頁時就消失，而 Pixel 寫下的 cookie 會留在這個網域上，直到按「清除這一頁的追蹤 cookie」或瀏覽器自己刪除。&lt;/p>
&lt;p>Pixel ID 欄位填的是 Meta Events Manager 裡某個 Pixel 的 ID。欄位預設空白，填自己的測試 Pixel ID：在事件管理工具的資料集頁面讀得到，商家設定頁網址裡的 &lt;code>selected_asset_id&lt;/code> 是另一個編號，填錯時頁面不會報錯（見 &lt;a href="https://tarrragon.github.io/blog/devops/monitoring/vendors/tracking-request-capture/" data-link-title="用 Playwright 擷取追蹤碼請求：擷取方式、記錄欄位與各次結果" data-link-desc="以 Playwright 操作 Meta Pixel 與 GTM 實測頁、記錄各家 script 實際送出的請求與 cookie 的方式，結果檔記錄的欄位與它們的用途，擷取用的測試帳號，以及每一次擷取觀察到的事件請求形式、cookie 與請求參數的對應、無頭瀏覽器下的差異與 script 自動送出的事件">用 Playwright 擷取追蹤碼請求&lt;/a> 的指令一節）。用自己的 Pixel 實測時，事件會出現在那個 Pixel 的 Test Events 頁面，可以對照 Meta 那一端實際收到了什麼。&lt;/p>
&lt;div class="vendor-lab" data-vendor="meta-pixel">
 &lt;style>
 .vendor-lab { border: 1px solid currentColor; border-radius: 6px; padding: 1rem; margin: 1.5rem 0; }
 .vendor-lab fieldset { border: 0; padding: 0; margin: 0 0 .75rem; }
 .vendor-lab label { display: block; margin-bottom: .25rem; }
 .vendor-lab input { font: inherit; padding: .25rem .5rem; min-width: 14rem; }
 .vendor-lab button { font: inherit; margin: .25rem .5rem .25rem 0; padding: .25rem .75rem; cursor: pointer; }
 .vendor-lab button[disabled] { cursor: not-allowed; opacity: .5; }
 .vendor-lab h4 { margin: 1rem 0 .25rem; }
 .vendor-lab ol { padding-left: 1.25rem; margin: 0; font-size: .875rem; overflow-wrap: anywhere; }
 .vendor-lab pre { font-size: .8rem; max-height: 20rem; overflow: auto; }
 .vendor-lab .vendor-lab-status { font-weight: bold; }
 &lt;/style>
 &lt;fieldset>
 &lt;label for="vendor-lab-id">Meta Pixel ID（Events Manager 裡那個 Pixel 的一串數字）&lt;/label>
 &lt;input id="vendor-lab-id" inputmode="numeric" autocomplete="off" value="" placeholder="例如 123456789012345">
 &lt;/fieldset>
 &lt;div>
 &lt;button type="button" data-action="load">載入 Meta Pixel 並送 PageView&lt;/button>
 &lt;button type="button" data-action="view-content" disabled>送 ViewContent&lt;/button>
 &lt;button type="button" data-action="purchase" disabled>送 Purchase（帶 eventID）&lt;/button>
 &lt;button type="button" data-action="fbclid">帶 fbclid 參數重新開啟這一頁&lt;/button>
 &lt;/div>
 &lt;div>
 &lt;button type="button" data-action="clear-cookies">清除這一頁的追蹤 cookie&lt;/button>
 &lt;/div>

 &lt;p class="vendor-lab-status" role="status" aria-live="polite">尚未載入任何第三方 script。&lt;/p></description><content:encoded><![CDATA[<p>這一頁是 <a href="/blog/devops/monitoring/vendors/meta-pixel-conversions-api/" data-link-title="Meta Pixel 與 Conversions API：廣告平台轉換追蹤的瀏覽器端與伺服器端" data-link-desc="Meta Pixel 在頁面上送出的標準事件與它的資料消費者、瀏覽器端事件被攔截的路徑、Conversions API 從伺服器補送同一批事件的必填欄位，以及兩路並送時以 event_id 去重與用 Test Events 驗證的方式">Meta Pixel 與 Conversions API</a> 的實測頁，範圍是瀏覽器端的 Pixel：載入 <code>fbevents.js</code>、用 <code>fbq()</code> 送事件，以及 Pixel 寫在網站網域上的 cookie。Conversions API 是伺服器端的路徑，在瀏覽器裡看不到，不在這一頁。</p>
<h2 id="這一頁載入什麼什麼時候載入">這一頁載入什麼、什麼時候載入</h2>
<p>頁面打開時不載入任何 Meta 的 script。按下「載入 Meta Pixel 並送 PageView」之後，面板才把 <code>fbevents.js</code> 從 <code>connect.facebook.net</code> 注入頁面，並用填在欄位裡的 Pixel ID 初始化；在那之前這一頁沒有任何送往 Meta 的請求，請求清單可以直接驗證這一點。注入的 script 在重新整理或離開這一頁時就消失，而 Pixel 寫下的 cookie 會留在這個網域上，直到按「清除這一頁的追蹤 cookie」或瀏覽器自己刪除。</p>
<p>Pixel ID 欄位填的是 Meta Events Manager 裡某個 Pixel 的 ID。欄位預設空白，填自己的測試 Pixel ID：在事件管理工具的資料集頁面讀得到，商家設定頁網址裡的 <code>selected_asset_id</code> 是另一個編號，填錯時頁面不會報錯（見 <a href="/blog/devops/monitoring/vendors/tracking-request-capture/" data-link-title="用 Playwright 擷取追蹤碼請求：擷取方式、記錄欄位與各次結果" data-link-desc="以 Playwright 操作 Meta Pixel 與 GTM 實測頁、記錄各家 script 實際送出的請求與 cookie 的方式，結果檔記錄的欄位與它們的用途，擷取用的測試帳號，以及每一次擷取觀察到的事件請求形式、cookie 與請求參數的對應、無頭瀏覽器下的差異與 script 自動送出的事件">用 Playwright 擷取追蹤碼請求</a> 的指令一節）。用自己的 Pixel 實測時，事件會出現在那個 Pixel 的 Test Events 頁面，可以對照 Meta 那一端實際收到了什麼。</p>
<div class="vendor-lab" data-vendor="meta-pixel">
  <style>
    .vendor-lab { border: 1px solid currentColor; border-radius: 6px; padding: 1rem; margin: 1.5rem 0; }
    .vendor-lab fieldset { border: 0; padding: 0; margin: 0 0 .75rem; }
    .vendor-lab label { display: block; margin-bottom: .25rem; }
    .vendor-lab input { font: inherit; padding: .25rem .5rem; min-width: 14rem; }
    .vendor-lab button { font: inherit; margin: .25rem .5rem .25rem 0; padding: .25rem .75rem; cursor: pointer; }
    .vendor-lab button[disabled] { cursor: not-allowed; opacity: .5; }
    .vendor-lab h4 { margin: 1rem 0 .25rem; }
    .vendor-lab ol { padding-left: 1.25rem; margin: 0; font-size: .875rem; overflow-wrap: anywhere; }
    .vendor-lab pre { font-size: .8rem; max-height: 20rem; overflow: auto; }
    .vendor-lab .vendor-lab-status { font-weight: bold; }
  </style>
  <fieldset>
    <label for="vendor-lab-id">Meta Pixel ID（Events Manager 裡那個 Pixel 的一串數字）</label>
    <input id="vendor-lab-id" inputmode="numeric" autocomplete="off" value="" placeholder="例如 123456789012345">
  </fieldset>
  <div>
    <button type="button" data-action="load">載入 Meta Pixel 並送 PageView</button>
    <button type="button" data-action="view-content" disabled>送 ViewContent</button>
    <button type="button" data-action="purchase" disabled>送 Purchase（帶 eventID）</button>
    <button type="button" data-action="fbclid">帶 fbclid 參數重新開啟這一頁</button>
  </div>
  <div>
    <button type="button" data-action="clear-cookies">清除這一頁的追蹤 cookie</button>
  </div>

  <p class="vendor-lab-status" role="status" aria-live="polite">尚未載入任何第三方 script。</p>

  <h4>這一頁的操作紀錄</h4>
  <ol class="vendor-lab-calls"></ol>

  <h4>這一頁送出的跨網域請求</h4>
  <ol class="vendor-lab-requests"></ol>

  <h4>這個網域上的追蹤 cookie</h4>
  <ol class="vendor-lab-cookies"></ol>
</div>
<script>
  (function () {
    var root = document.currentScript.previousElementSibling;
    var vendor = root.getAttribute("data-vendor");
    var idInput = root.querySelector("#vendor-lab-id");
    var statusEl = root.querySelector(".vendor-lab-status");
    var callsEl = root.querySelector(".vendor-lab-calls");
    var requestsEl = root.querySelector(".vendor-lab-requests");
    var cookiesEl = root.querySelector(".vendor-lab-cookies");
    var dataLayerEl = root.querySelector(".vendor-lab-datalayer");
    var startedAt = performance.now();

    
    var COOKIE_PREFIXES = ["_fbp", "_fbc", "_ga", "_gid", "_gcl"];
    var ID_PATTERN = { "meta-pixel": /^\d{5,20}$/, "gtm": /^GTM-[A-Z0-9]{4,12}$/ };

    function item(list, text) {
      var li = document.createElement("li");
      li.textContent = text;   
      list.appendChild(li);
    }
    function seconds() { return ((performance.now() - startedAt) / 1000).toFixed(1) + "s"; }
    function logCall(text) { item(callsEl, seconds() + "  " + text); }
    function setStatus(text) { statusEl.textContent = text; }

    
    
    function showRequest(entry) {
      var url;
      try { url = new URL(entry.name); } catch (e) { return; }
      if (url.origin === location.origin) return;
      var params = [];
      url.searchParams.forEach(function (v, k) { params.push(k + "=" + v); });
      item(requestsEl, (entry.startTime / 1000).toFixed(1) + "s  [" + entry.initiatorType + "] "
        + url.origin + url.pathname + (params.length ? "\n    " + params.join("\n    ") : ""));
    }
    try {
      new PerformanceObserver(function (list) { list.getEntries().forEach(showRequest); })
        .observe({ type: "resource", buffered: true });
    } catch (e) {
      item(requestsEl, "這個瀏覽器不支援 PerformanceObserver，請改用開發者工具的 Network 面板觀察。");
    }

    function refreshCookies() {
      cookiesEl.textContent = "";
      var found = document.cookie.split(/;\s*/).filter(function (c) {
        return COOKIE_PREFIXES.some(function (p) { return c.indexOf(p) === 0; });
      });
      if (!found.length) item(cookiesEl, "（沒有）");
      found.forEach(function (c) { item(cookiesEl, c); });
    }
    function refreshDataLayer() {
      if (!dataLayerEl) return;
      if (!window.dataLayer) { dataLayerEl.textContent = "（尚未建立）"; return; }
      try { dataLayerEl.textContent = JSON.stringify(window.dataLayer, null, 2); }
      catch (e) { dataLayerEl.textContent = "（內容含無法序列化的值）"; }
    }
    function refreshAll() { refreshCookies(); refreshDataLayer(); }

    
    function clearCookies() {
      var expired = "=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/";
      document.cookie.split(/;\s*/).forEach(function (c) {
        var name = c.split("=")[0];
        if (!COOKIE_PREFIXES.some(function (p) { return name.indexOf(p) === 0; })) return;
        document.cookie = name + expired;
        document.cookie = name + expired + "; domain=" + location.hostname;
        document.cookie = name + expired + "; domain=." + location.hostname;
      });
      logCall("清除追蹤 cookie");
      refreshAll();
    }

    function readId() {
      var id = idInput.value.trim();
      if (!ID_PATTERN[vendor].test(id)) {
        setStatus("ID 格式不符，沒有載入任何 script。");
        return null;
      }
      return id;
    }

    
    function injectScript(src, label) {
      var s = document.createElement("script");
      s.async = true;
      s.src = src;
      s.onload = function () { setStatus(label + " 已載入。"); logCall(label + " 載入完成"); refreshAll(); };
      s.onerror = function () { setStatus(label + " 載入失敗：請求被擋下（廣告攔截器、瀏覽器追蹤防護或網路政策）。"); logCall(label + " 載入失敗"); };
      document.head.appendChild(s);
    }

    var actions = {};
    var loaded = false;

    if (vendor === "meta-pixel") {
      var enable = function () {
        root.querySelectorAll('[data-action="view-content"],[data-action="purchase"]').forEach(function (b) { b.disabled = false; });
      };
      actions.load = function () {
        if (loaded) return;
        var id = readId(); if (!id) return;
        loaded = true;
        
        if (!window.fbq) {
          var n = window.fbq = function () { n.callMethod ? n.callMethod.apply(n, arguments) : n.queue.push(arguments); };
          if (!window._fbq) window._fbq = n;
          n.push = n; n.loaded = true; n.version = "2.0"; n.queue = [];
        }
        injectScript("https://connect.facebook.net/en_US/fbevents.js", "fbevents.js");
        fbq("init", id); logCall("fbq('init', '" + id + "')");
        fbq("track", "PageView"); logCall("fbq('track', 'PageView')");
        enable();
      };
      actions["view-content"] = function () {
        fbq("track", "ViewContent", { content_name: "vendor-lab" });
        logCall("fbq('track', 'ViewContent', {content_name: 'vendor-lab'})");
        refreshAll();
      };
      actions.purchase = function () {
        var eventId = "lab-" + Date.now().toString(36);
        fbq("track", "Purchase", { value: 1, currency: "TWD" }, { eventID: eventId });
        logCall("fbq('track', 'Purchase', {value: 1, currency: 'TWD'}, {eventID: '" + eventId + "'})");
        refreshAll();
      };
      actions.fbclid = function () {
        var url = new URL(location.href);
        url.searchParams.set("fbclid", "lab" + Date.now().toString(36));
        location.href = url.toString();
      };
    }

    if (vendor === "gtm") {
      actions.load = function () {
        if (loaded) return;
        var id = readId(); if (!id) return;
        loaded = true;
        
        window.dataLayer = window.dataLayer || [];
        window.dataLayer.push({ "gtm.start": Date.now(), event: "gtm.js" });
        logCall("dataLayer.push({'gtm.start': …, event: 'gtm.js'})");
        injectScript("https://www.googletagmanager.com/gtm.js?id=" + encodeURIComponent(id), "gtm.js");
        refreshAll();
      };
      actions["push-purchase"] = function () {
        window.dataLayer = window.dataLayer || [];
        var orderId = "lab-" + Date.now().toString(36);
        window.dataLayer.push({ event: "purchase", order_id: orderId, value: 1, currency: "TWD" });
        logCall("dataLayer.push({event: 'purchase', order_id: '" + orderId + "', value: 1, currency: 'TWD'})");
        refreshAll();
      };
    }

    actions["clear-cookies"] = clearCookies;

    root.addEventListener("click", function (evt) {
      var btn = evt.target.closest("button[data-action]");
      if (!btn || !actions[btn.getAttribute("data-action")]) return;
      actions[btn.getAttribute("data-action")]();
      
      setTimeout(refreshAll, 1500);
    });

    refreshAll();
  })();
</script>

<h2 id="對照面板讀出各個呼叫的效果">對照面板讀出各個呼叫的效果</h2>
<p>面板分成三塊。「操作紀錄」列出按鈕實際執行的 <code>fbq()</code> 呼叫；「這一頁送出的跨網域請求」來自瀏覽器的 Resource Timing 紀錄，列出這一頁向其他網域發出的每一個請求與它的查詢參數；「追蹤 cookie」列出這個網域上以 <code>_fbp</code>、<code>_fbc</code> 等前綴開頭的 cookie（這些是寫在網站網域上的<a href="/blog/devops/monitoring/knowledge-cards/first-party-cookie/" data-link-title="First-party Cookie（第一方 cookie）" data-link-desc="盤點網站寫下的識別碼 cookie、比對追蹤碼的 cookie 期限，或判斷某個 cookie 受不受瀏覽器的第三方 cookie 限制時">第一方 cookie</a>）。操作紀錄、請求與 cookie 並排讀，就能把一個呼叫對到它造成的網路請求與狀態變化。</p>
<ul>
<li><strong>載入 Pixel 之後</strong>：請求清單出現 <code>fbevents.js</code> 與 <code>signals/config/&lt;Pixel ID&gt;</code>（這個 Pixel 的設定檔）兩個 script 請求，cookie 清單在幾秒內出現 <code>_fbp</code>。PageView 等事件請求不會出現在這個清單裡：<code>fbevents.js</code> 以隱藏的表單把事件以 POST 提交到一個 iframe，這類提交不進這一頁的 Resource Timing。要看事件請求，打開瀏覽器開發者工具的 Network 面板，篩選 <code>facebook.com/tr</code>；表單 body 裡的 <code>ev</code> 是事件名稱，其餘參數的名稱與意義由 Meta 決定、會隨版本調整，實測的參數對應見 <a href="/blog/devops/monitoring/vendors/tracking-request-capture/" data-link-title="用 Playwright 擷取追蹤碼請求：擷取方式、記錄欄位與各次結果" data-link-desc="以 Playwright 操作 Meta Pixel 與 GTM 實測頁、記錄各家 script 實際送出的請求與 cookie 的方式，結果檔記錄的欄位與它們的用途，擷取用的測試帳號，以及每一次擷取觀察到的事件請求形式、cookie 與請求參數的對應、無頭瀏覽器下的差異與 script 自動送出的事件">用 Playwright 擷取追蹤碼請求</a> 的各次結果。</li>
<li><strong>送 Purchase</strong>：操作紀錄裡的 <code>eventID</code> 是這次按鈕產生的值，在 Network 面板那一筆 <code>/tr/</code> 請求的表單 body 裡是 <code>eid</code> 參數，這就是 Conversions API 去重時要對上的那一個。<code>value</code> 與 <code>currency</code> 在同一筆的 <code>cd[value]</code> 與 <code>cd[currency]</code>。同一時間還會多一筆<a href="/blog/devops/monitoring/knowledge-cards/autocapture/" data-link-title="Autocapture（自動事件收集）" data-link-desc="盤點網站實際送出哪些事件與內容、報表裡出現網站沒有呼叫過的事件，或在手動埋點與自動收集之間取捨時">自動事件</a> <code>SubscribedButtonClick</code>，那是 Pixel 依按鈕點擊自動送出的事件，內容包含按鈕文字。</li>
<li><strong>帶 fbclid 參數重新開啟這一頁</strong>：按鈕把一個測試用的 <code>fbclid</code> 加進網址再重新載入。重新載入後再按一次「載入 Meta Pixel」，cookie 清單會多出 <code>_fbc</code>，值裡包含剛才的 <code>fbclid</code>。這是讀者從廣告點進網站時 Pixel 保存點擊來源的方式。</li>
<li><strong>載入失敗</strong>：狀態列顯示「載入失敗」時，代表 <code>fbevents.js</code> 的請求被擋下，常見來源是廣告攔截器、瀏覽器的追蹤防護或公司網路的政策。這時按其他按鈕，<code>fbq()</code> 只會把呼叫排進佇列，請求清單裡不會出現任何送往 Meta 的請求。這正是瀏覽器端事件損失的樣子：網站這一端沒有任何錯誤訊息。</li>
</ul>
<p>請求清單在頁面打開時就可能已經有幾筆紀錄，那些是本站每一頁都會載入的第三方資源（例如從 <code>cdn.jsdelivr.net</code> 載入的 Mermaid）與本站的流量 beacon（送往 <code>script.google.com</code>），與 Meta 無關。本站的 beacon 怎麼設計見 <a href="/blog/automation/02-analytics-beacon/" data-link-title="模組二：流量 beacon 實作" data-link-desc="把「頁面被看了就送一則事件、接收端寫進 Sheet」從零做到收到第一筆真實瀏覽紀錄時的完整實作">Automation 模組二：流量 beacon 實作</a>，拿來和 Pixel 的請求並排看，是自製追蹤與第三方追蹤碼在送出內容上的對照。</p>
<p>Resource Timing 只記錄網址、看不到 POST 請求的 body，而 Pixel 以表單提交到 iframe 的事件連網址都不會出現在這裡。要看完整的請求內容，打開瀏覽器開發者工具的 Network 面板，篩選 <code>facebook</code> 再點開那一筆。</p>
]]></content:encoded></item><item><title>用 Playwright 擷取追蹤碼請求：擷取方式、記錄欄位與各次結果</title><link>https://tarrragon.github.io/blog/devops/monitoring/vendors/tracking-request-capture/</link><pubDate>Thu, 22 Oct 2026 00:00:00 +0800</pubDate><guid>https://tarrragon.github.io/blog/devops/monitoring/vendors/tracking-request-capture/</guid><description>&lt;p>這篇記錄用 Playwright 在本機操作 &lt;a href="https://tarrragon.github.io/blog/devops/monitoring/vendors/meta-pixel-lab/" data-link-title="Meta Pixel 實測頁：在瀏覽器裡觀察 fbq 呼叫、送出的請求與寫下的 cookie" data-link-desc="在這一頁按按鈕載入 Meta Pixel、送 PageView、ViewContent 與帶 eventID 的 Purchase，並用頁面上的面板對照每個呼叫送出的請求參數，以及 _fbp 與 fbclid 帶出的 _fbc cookie">Meta Pixel 實測頁&lt;/a> 與 &lt;a href="https://tarrragon.github.io/blog/devops/monitoring/vendors/google-tag-manager-lab/" data-link-title="Google Tag Manager 實測頁：在瀏覽器裡觀察容器載入、資料層事件與代碼送出的請求" data-link-desc="在這一頁按按鈕載入 GTM 容器、往資料層推 purchase 事件，並用頁面上的面板對照 window.dataLayer 的內容、容器載入的代碼與它們送出的請求">Google Tag Manager 實測頁&lt;/a>、擷取各家追蹤碼實際送出內容的方式與結果。下文的「擷取腳本」指本站寫的 Playwright 程式 &lt;code>scripts/vendor-capture/capture.mjs&lt;/code>（在本站的公開 repo &lt;a href="https://github.com/tarrragon/blog">tarrragon/blog&lt;/a> 裡），「script」指服務商（vendor）提供、在頁面上執行的 JavaScript（&lt;code>fbevents.js&lt;/code>、&lt;code>gtm.js&lt;/code>）。各家 script 由服務商自己的伺服器提供、隨時改版，所以本篇的每一份結果都只代表擷取當下的行為，引用時要連同擷取時間與版本資訊一起看。&lt;/p>
&lt;h2 id="擷取方式讓瀏覽器自動化操作實測頁">擷取方式：讓瀏覽器自動化操作實測頁&lt;/h2>
&lt;p>擷取腳本不直接把服務商的 script 載入一個空白頁，而是開啟實測頁、照讀者的操作順序按按鈕。這樣擷取到的是和讀者在實測頁上觸發的同一組 JavaScript 呼叫：同樣的 &lt;code>fbq()&lt;/code> 呼叫、同樣的資料層（dataLayer，頁面上供 GTM 讀取事件的全域陣列 &lt;code>window.dataLayer&lt;/code>，定義見 &lt;a href="https://tarrragon.github.io/blog/devops/monitoring/vendors/google-tag-manager/" data-link-title="Google Tag Manager：追蹤碼的部署層、資料層約定與發布治理" data-link-desc="GTM 容器在頁面上承擔的部署責任、資料層作為前端與行銷之間的事件約定、預覽模式與版本發布的驗證方式、自訂 HTML 代碼帶來的安全與效能成本，以及伺服器端代碼管理改變的資料路徑">Google Tag Manager&lt;/a> 的資料層一節）內容，結果可以拿來對照實測頁的面板（實測頁上列出操作紀錄、跨網域請求與追蹤 cookie 的區塊）。&lt;/p>
&lt;p>擷取腳本在 Meta Pixel 實測頁上依序做的動作是：載入 Pixel 並送 PageView、送 ViewContent、送帶 &lt;code>eventID&lt;/code> 的 Purchase，再把 &lt;code>fbclid&lt;/code> 參數加進網址重新開啟頁面並再載入一次 Pixel，觀察 &lt;code>_fbc&lt;/code> cookie 的產生。在 GTM 實測頁上是載入容器、按實測頁的「往資料層推 purchase 事件」按鈕。每個動作之後等三秒，讓 script 送完請求、寫完 cookie。&lt;/p>
&lt;p>實測頁的面板用瀏覽器的 Resource Timing 列出跨網域請求，Playwright 則直接監看瀏覽器送出的每一個請求，所以擷取結果多了面板拿不到的資訊：POST 請求的 body、回應的狀態碼與標頭、帶 &lt;code>HttpOnly&lt;/code> 旗標而 &lt;code>document.cookie&lt;/code> 讀不到的 cookie，以及面板根本列不出來的請求。Meta Pixel 的事件請求就是最後一種：&lt;code>fbevents.js&lt;/code> 以隱藏的表單把事件提交到一個 iframe，這類提交不進父頁面的 Resource Timing，所以實測頁面板上只看得到 &lt;code>fbevents.js&lt;/code> 與它的設定檔，看不到任何一筆事件；在 Playwright 的紀錄與瀏覽器開發者工具的 Network 面板裡看得到。&lt;/p>
&lt;p>擷取腳本預設以&lt;a href="https://tarrragon.github.io/blog/devops/monitoring/knowledge-cards/headless-browser/" data-link-title="Headless Browser（無頭瀏覽器）" data-link-desc="用 Playwright 或 Puppeteer 測追蹤碼、分析報表裡的機器流量，或自建統計與商業追蹤碼的訪問數對不上時">無頭模式&lt;/a>（headless）啟動 Chromium，這時瀏覽器的 User-Agent 帶 &lt;code>HeadlessChrome&lt;/code> 字樣。實測時 Meta Pixel 在無頭模式下照樣載入、照樣寫 cookie，事件請求卻一筆都不送出，原因在 Pixel 的設定檔（&lt;code>signals/config/&amp;lt;Pixel ID&amp;gt;&lt;/code>）載入的 &lt;code>botblocking&lt;/code> 模組（plugin，&lt;code>fbevents.js&lt;/code> 依 Pixel 設定檔載入的程式片段）：依擷取當時那一版設定檔的原始碼，這個模組拿 &lt;code>navigator.userAgent&lt;/code> 比對一份機器人規則清單，清單裡有 &lt;code>HeadlessChrome/&lt;/code>，比對成功時由這個模組在事件送出之前把它攔下（模組程式碼裡對應的訊息字串是 &lt;code>[Meta pixel] Bot traffic detected and blocked&lt;/code>）。&lt;code>fbevents.js&lt;/code> 本體也偵測 &lt;code>HeadlessChrome&lt;/code>，但只把結果寫進事件請求的 &lt;code>o&lt;/code> 參數。&lt;/p>
&lt;p>兩組對照支持這個歸因，都沒有存成結果檔。作者在 Playwright 的瀏覽器設定裡只把 User-Agent 換成一般 Chrome 的字串、其餘照舊以無頭模式執行，事件就送出了，這證明結果取決於 User-Agent，但 &lt;code>fbevents.js&lt;/code> 本體同樣讀 User-Agent，單靠這一組分不出攔截的是模組還是本體。另一組來自一次填錯 Pixel ID 的擷取：同樣是無頭模式，不存在的 ID 拿到的設定檔裡沒有 &lt;code>botblocking&lt;/code>（只有幾個不分 ID 的通用模組，該 ID 專屬的那一段是空的），向 &lt;code>www.facebook.com/tr/&lt;/code> 的事件請求卻照樣送出（那次是 GET，&lt;code>o&lt;/code> 參數含 &lt;code>IS_HEADLESS&lt;/code>），所以本體偵測到無頭模式之後並沒有攔截，攔截來自設定檔載入的模組。那份結果檔因為 ID 填錯而刪除了。GTM 容器裡的 GA4 代碼在無頭模式下照常送出事件請求。依 User-Agent 辨識自動化流量是通用的做法，本站自己的流量統計怎麼分辨機器流量見 &lt;a href="https://tarrragon.github.io/blog/automation/06-reading-the-data/automated-traffic/" data-link-title="辨識自動化流量" data-link-desc="接收端讀不到 HTTP 標頭時在前端蒐集訊號分辨機器與真人；多數 AI 訓練爬蟲不執行 JavaScript，因此不會出現在這份統計裡">辨識自動化流量&lt;/a>。&lt;/p>
&lt;p>所以擷取 Meta Pixel 要加 &lt;code>--headed&lt;/code>，讓 Playwright 開一個有視窗的瀏覽器操作實測頁。擷取腳本不改寫 User-Agent，有視窗的瀏覽器仍然是自動化控制的瀏覽器，Meta 之後若加入其他偵測條件，同一個指令的結果可能改變，這也是每份結果要記下擷取模式的理由。&lt;/p></description><content:encoded><![CDATA[<p>這篇記錄用 Playwright 在本機操作 <a href="/blog/devops/monitoring/vendors/meta-pixel-lab/" data-link-title="Meta Pixel 實測頁：在瀏覽器裡觀察 fbq 呼叫、送出的請求與寫下的 cookie" data-link-desc="在這一頁按按鈕載入 Meta Pixel、送 PageView、ViewContent 與帶 eventID 的 Purchase，並用頁面上的面板對照每個呼叫送出的請求參數，以及 _fbp 與 fbclid 帶出的 _fbc cookie">Meta Pixel 實測頁</a> 與 <a href="/blog/devops/monitoring/vendors/google-tag-manager-lab/" data-link-title="Google Tag Manager 實測頁：在瀏覽器裡觀察容器載入、資料層事件與代碼送出的請求" data-link-desc="在這一頁按按鈕載入 GTM 容器、往資料層推 purchase 事件，並用頁面上的面板對照 window.dataLayer 的內容、容器載入的代碼與它們送出的請求">Google Tag Manager 實測頁</a>、擷取各家追蹤碼實際送出內容的方式與結果。下文的「擷取腳本」指本站寫的 Playwright 程式 <code>scripts/vendor-capture/capture.mjs</code>（在本站的公開 repo <a href="https://github.com/tarrragon/blog">tarrragon/blog</a> 裡），「script」指服務商（vendor）提供、在頁面上執行的 JavaScript（<code>fbevents.js</code>、<code>gtm.js</code>）。各家 script 由服務商自己的伺服器提供、隨時改版，所以本篇的每一份結果都只代表擷取當下的行為，引用時要連同擷取時間與版本資訊一起看。</p>
<h2 id="擷取方式讓瀏覽器自動化操作實測頁">擷取方式：讓瀏覽器自動化操作實測頁</h2>
<p>擷取腳本不直接把服務商的 script 載入一個空白頁，而是開啟實測頁、照讀者的操作順序按按鈕。這樣擷取到的是和讀者在實測頁上觸發的同一組 JavaScript 呼叫：同樣的 <code>fbq()</code> 呼叫、同樣的資料層（dataLayer，頁面上供 GTM 讀取事件的全域陣列 <code>window.dataLayer</code>，定義見 <a href="/blog/devops/monitoring/vendors/google-tag-manager/" data-link-title="Google Tag Manager：追蹤碼的部署層、資料層約定與發布治理" data-link-desc="GTM 容器在頁面上承擔的部署責任、資料層作為前端與行銷之間的事件約定、預覽模式與版本發布的驗證方式、自訂 HTML 代碼帶來的安全與效能成本，以及伺服器端代碼管理改變的資料路徑">Google Tag Manager</a> 的資料層一節）內容，結果可以拿來對照實測頁的面板（實測頁上列出操作紀錄、跨網域請求與追蹤 cookie 的區塊）。</p>
<p>擷取腳本在 Meta Pixel 實測頁上依序做的動作是：載入 Pixel 並送 PageView、送 ViewContent、送帶 <code>eventID</code> 的 Purchase，再把 <code>fbclid</code> 參數加進網址重新開啟頁面並再載入一次 Pixel，觀察 <code>_fbc</code> cookie 的產生。在 GTM 實測頁上是載入容器、按實測頁的「往資料層推 purchase 事件」按鈕。每個動作之後等三秒，讓 script 送完請求、寫完 cookie。</p>
<p>實測頁的面板用瀏覽器的 Resource Timing 列出跨網域請求，Playwright 則直接監看瀏覽器送出的每一個請求，所以擷取結果多了面板拿不到的資訊：POST 請求的 body、回應的狀態碼與標頭、帶 <code>HttpOnly</code> 旗標而 <code>document.cookie</code> 讀不到的 cookie，以及面板根本列不出來的請求。Meta Pixel 的事件請求就是最後一種：<code>fbevents.js</code> 以隱藏的表單把事件提交到一個 iframe，這類提交不進父頁面的 Resource Timing，所以實測頁面板上只看得到 <code>fbevents.js</code> 與它的設定檔，看不到任何一筆事件；在 Playwright 的紀錄與瀏覽器開發者工具的 Network 面板裡看得到。</p>
<p>擷取腳本預設以<a href="/blog/devops/monitoring/knowledge-cards/headless-browser/" data-link-title="Headless Browser（無頭瀏覽器）" data-link-desc="用 Playwright 或 Puppeteer 測追蹤碼、分析報表裡的機器流量，或自建統計與商業追蹤碼的訪問數對不上時">無頭模式</a>（headless）啟動 Chromium，這時瀏覽器的 User-Agent 帶 <code>HeadlessChrome</code> 字樣。實測時 Meta Pixel 在無頭模式下照樣載入、照樣寫 cookie，事件請求卻一筆都不送出，原因在 Pixel 的設定檔（<code>signals/config/&lt;Pixel ID&gt;</code>）載入的 <code>botblocking</code> 模組（plugin，<code>fbevents.js</code> 依 Pixel 設定檔載入的程式片段）：依擷取當時那一版設定檔的原始碼，這個模組拿 <code>navigator.userAgent</code> 比對一份機器人規則清單，清單裡有 <code>HeadlessChrome/</code>，比對成功時由這個模組在事件送出之前把它攔下（模組程式碼裡對應的訊息字串是 <code>[Meta pixel] Bot traffic detected and blocked</code>）。<code>fbevents.js</code> 本體也偵測 <code>HeadlessChrome</code>，但只把結果寫進事件請求的 <code>o</code> 參數。</p>
<p>兩組對照支持這個歸因，都沒有存成結果檔。作者在 Playwright 的瀏覽器設定裡只把 User-Agent 換成一般 Chrome 的字串、其餘照舊以無頭模式執行，事件就送出了，這證明結果取決於 User-Agent，但 <code>fbevents.js</code> 本體同樣讀 User-Agent，單靠這一組分不出攔截的是模組還是本體。另一組來自一次填錯 Pixel ID 的擷取：同樣是無頭模式，不存在的 ID 拿到的設定檔裡沒有 <code>botblocking</code>（只有幾個不分 ID 的通用模組，該 ID 專屬的那一段是空的），向 <code>www.facebook.com/tr/</code> 的事件請求卻照樣送出（那次是 GET，<code>o</code> 參數含 <code>IS_HEADLESS</code>），所以本體偵測到無頭模式之後並沒有攔截，攔截來自設定檔載入的模組。那份結果檔因為 ID 填錯而刪除了。GTM 容器裡的 GA4 代碼在無頭模式下照常送出事件請求。依 User-Agent 辨識自動化流量是通用的做法，本站自己的流量統計怎麼分辨機器流量見 <a href="/blog/automation/06-reading-the-data/automated-traffic/" data-link-title="辨識自動化流量" data-link-desc="接收端讀不到 HTTP 標頭時在前端蒐集訊號分辨機器與真人；多數 AI 訓練爬蟲不執行 JavaScript，因此不會出現在這份統計裡">辨識自動化流量</a>。</p>
<p>所以擷取 Meta Pixel 要加 <code>--headed</code>，讓 Playwright 開一個有視窗的瀏覽器操作實測頁。擷取腳本不改寫 User-Agent，有視窗的瀏覽器仍然是自動化控制的瀏覽器，Meta 之後若加入其他偵測條件，同一個指令的結果可能改變，這也是每份結果要記下擷取模式的理由。</p>
<h2 id="擷取結果檔json的欄位">擷取結果檔（JSON）的欄位</h2>
<p>擷取腳本每次執行寫出一份 JSON，檔名是服務商名稱加上 UTC 擷取時間（例如 <code>meta-pixel-20261022T1726Z.json</code>），放在 <code>scripts/vendor-capture/results/</code>。欄位分成三組：</p>
<ul>
<li><strong>重現條件</strong>：<code>capturedAt</code>（UTC）與 <code>capturedAtTaipei</code>、<code>browser</code>（Chromium 版本）、<code>userAgent</code>、<code>headless</code>（這次是否以無頭模式執行）、<code>labUrl</code>、使用的 Pixel 或容器 ID。<code>headless</code> 欄位從 2026-10-23 01:26 那一次擷取起才有，01:23 與 00:45 兩份結果沒有這個欄位，看 <code>userAgent</code> 有沒有 <code>HeadlessChrome</code>。結果和這些條件綁在一起才有意義，換一個瀏覽器版本或服務商改版之後，同一個動作可能送出不同的參數。</li>
<li><strong>script 的版本線索</strong>：<code>requests</code> 裡 <code>fbevents.js</code>、<code>gtm.js</code> 那幾筆的 <code>body</code> 欄位（script 本體的位元組數與 SHA-256）與回應標頭，以及 <code>vendorRuntime.fbqVersion</code>（頁面上 <code>fbq.version</code> 的值）。實測兩家的回應都不帶 <code>last-modified</code> 與 <code>etag</code>，所以讀結果的人判斷 script 有沒有改版，以本體雜湊為主。<code>fbq.version</code> 在網站安裝的 Pixel 基本碼（base code）裡先被設成 <code>2.0</code>，<code>fbevents.js</code> 載入後覆寫成實際版本。<code>gtm.js</code> 的內容由容器設定與 Google 提供的 GTM 執行時程式碼（runtime）一起產生，雜湊變了之後要再到 GTM 後台的版本紀錄查容器那段期間有沒有發布，才分得出是哪一邊改的。</li>
<li><strong>觀察對象</strong>：<code>steps</code>（每個動作之後實測頁狀態訊息列的文字）、<code>requests</code>（每一筆跨網域請求的方法、網址、查詢參數、POST body、狀態碼）、<code>cookies</code>（追蹤 cookie 的名稱、值、網域、到期時間與旗標）、<code>vendorRuntime.dataLayer</code>（GTM 實測時資料層的完整內容）。</li>
</ul>
<p>判讀 <code>requests</code> 時，<code>status</code> 是主要訊號。瀏覽器收到回應之後才被中止的請求，Playwright 會觸發失敗事件，擷取腳本在失敗事件裡也讀取已收到的回應，所以結果檔同時記下 <code>status</code> 與 <code>failure</code>，這時以 <code>status</code> 為準：請求送達了。本篇的擷取裡只有 GA4 的 <code>g/collect</code> 出現這個組合，00:45 那一則記錄了它與作者用 Chrome DevTools Protocol 做的對照。<code>status</code> 為 <code>null</code> 而帶 <code>failure</code> 的，是沒有收到回應的請求。</p>
<h2 id="擷取用的測試帳號">擷取用的測試帳號</h2>
<p>擷取用的 Pixel 與 GTM 容器都是測試專用的資產，各自放在只放實測資產的擁有者底下：Meta 另開一個商家資產管理組合，GTM 與 GA4 各另開一個帳戶。這樣安排的理由見 <a href="/blog/devops/monitoring/vendors/tracking-account-ownership/" data-link-title="追蹤資產的歸屬：Meta、GTM 與 GA4 的擁有層級、權限分級與移交方式" data-link-desc="Meta Pixel 的資料集、GTM 容器與 GA4 資源（property）各自屬於哪一層的擁有者，三家的權限怎麼分級，哪些資產能換到另一個擁有者底下，以及公司自營、委託代理商、換代理商與事業轉手時的歸屬安排">追蹤資產的歸屬</a>。建立時三家的檢查與預設值會影響擷取結果，記在這裡：</p>
<ul>
<li><strong>Meta 對組合名稱有檢查</strong>：<code>vendor-lab-test</code> 與 <code>Tarragon</code> 兩個名稱都被退回，錯誤訊息是組合名稱必須如實反映商家，<code>Tarragon Tech Notes</code> 通過。Meta 的說明頁只寫組合名稱要和商家對外使用的名稱一致、不能有特殊字元，沒有列出退件的判斷條件；錯誤訊息附的連結是粉絲專頁的命名守則，其中一條是名稱不能只由一個統稱用詞組成（守則舉的例子是「披薩」），<code>Tarragon</code> 是香草的英文名稱，與這一條相符。GTM 與 GA4 這次以 <code>vendor-lab-test</code> 當帳戶名稱，都直接建立成功。</li>
<li><strong>Meta 的建立流程詢問要不要把既有的粉絲專頁加入新組合</strong>，預設不勾選。Meta 資料集不需要粉絲專頁，這次略過。</li>
<li><strong>Meta 資料集的建立表單有一個預設勾選的選項</strong>，表單上的說明是勾選之後，組合裡現有與日後新建的每一個資料集都會連結到 Meta 的 Conversions API（轉換 API），搭配 Pixel 把網站事件也送到 Meta 的伺服器端。這個選項由 Meta 那一端代為處理伺服器端的傳送，與 <a href="/blog/devops/monitoring/vendors/meta-pixel-conversions-api/" data-link-title="Meta Pixel 與 Conversions API：廣告平台轉換追蹤的瀏覽器端與伺服器端" data-link-desc="Meta Pixel 在頁面上送出的標準事件與它的資料消費者、瀏覽器端事件被攔截的路徑、Conversions API 從伺服器補送同一批事件的必填欄位，以及兩路並送時以 event_id 去重與用 Test Events 驗證的方式">Meta Pixel 與 Conversions API</a> 描述的、由網站自己的伺服器呼叫 Conversions API 是不同的設置方式。擷取要觀察的是瀏覽器端送出的內容，這次取消勾選，所以測試 Pixel 沒有伺服器端的事件來源。</li>
<li><strong>GA4 建立帳戶時，四個資料共用選項有三個預設勾選</strong>（「根據輸入內容和業務洞察資料建立模型」「技術支援」「依據您業務性質提供的建議」），這次全部取消；GTM 帳戶的匿名資料共用選項預設不勾選。</li>
</ul>
<p>Pixel ID 在事件管理工具（Events Manager）的資料集頁面讀，商家設定頁網址裡的 <code>selected_asset_id</code> 是設定頁內部的資產編號，與 Pixel ID 不同；GTM 容器 ID（<code>GTM-</code> 開頭）顯示在 GTM 後台工作區頁面的上方。</p>
<h2 id="執行擷取的指令">執行擷取的指令</h2>
<p>指令都在 repo 的根目錄執行。第一次執行前，先裝好 Node.js，安裝 repo 的相依套件（<code>@playwright/test</code> 與 <code>http-server</code>）與 Playwright 用的 Chromium：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 取得擷取腳本與實測頁的原始碼</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">git clone https://github.com/tarrragon/blog.git <span class="o">&amp;&amp;</span> <span class="nb">cd</span> blog
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 安裝相依套件與 Playwright 的 Chromium；沒有裝 Chromium 時，第一次執行會出現 Executable doesn&#39;t exist</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">npm ci
</span></span><span class="line"><span class="ln">6</span><span class="cl">npx playwright install chromium</span></span></code></pre></div><p>擷取指令本身：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 擷取 Meta Pixel：--id 填測試用 Pixel 的 ID，--headed 讓事件請求送得出去</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">node scripts/vendor-capture/capture.mjs --vendor meta-pixel --id &lt;Pixel ID&gt; --headed
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># 擷取 GTM：--id 填測試用容器的 ID（GTM- 開頭）</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">node scripts/vendor-capture/capture.mjs --vendor gtm --id &lt;GTM-容器ID&gt;
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"># 改測本機建置的網站：先建置網站並啟動靜態伺服器，再把 --url 指到本機的實測頁</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">make site
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">mkdir -p .test-www <span class="o">&amp;&amp;</span> ln -sfn ../public .test-www/blog
</span></span><span class="line"><span class="ln">10</span><span class="cl">npx http-server .test-www -p <span class="m">4173</span> -s <span class="p">&amp;</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">node scripts/vendor-capture/capture.mjs --vendor meta-pixel --id &lt;Pixel ID&gt; --headed <span class="se">\
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="se"></span>  --url http://localhost:4173/blog/devops/monitoring/vendors/meta-pixel-lab/
</span></span><span class="line"><span class="ln">13</span><span class="cl">node scripts/vendor-capture/capture.mjs --vendor gtm --id &lt;GTM-容器ID&gt; <span class="se">\
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="se"></span>  --url http://localhost:4173/blog/devops/monitoring/vendors/google-tag-manager-lab/</span></span></code></pre></div><p>擷取腳本依 <code>--vendor</code>（<code>meta-pixel</code> 或 <code>gtm</code>）決定開哪一個實測頁、照哪一組動作操作。<code>--id</code> 是要填進實測頁欄位的 ID，用測試專用的 Pixel 與容器，擷取送出的事件才不會混進正式的廣告報表；兩個 ID 去哪裡讀，見〈擷取用的測試帳號〉一節的最後一段。省略 <code>--url</code> 時，擷取腳本開正式站的實測頁；加 <code>--out &lt;目錄&gt;</code> 時，擷取腳本把結果檔寫到那個目錄；<code>--headed</code> 不帶值，加上它就以有視窗的瀏覽器執行，執行期間螢幕上會開一個 Chromium 視窗、跑完自動關閉。本篇的擷取都在有桌面的 macOS 上執行，沒有顯示器的環境（例如沒有桌面的 Linux 伺服器）開不了有視窗的瀏覽器，本篇沒有驗證那種環境的做法。</p>
<p>擷取腳本啟動的 Chromium 由 <code>@playwright/test</code> 的版本決定；沒有跑過 <code>npx playwright install chromium</code>、或 Playwright 升級之後，執行時會出現 <code>Executable doesn't exist at …</code> 這個錯誤。這時重跑安裝指令，或用環境變數 <code>PW_CHROMIUM_PATH</code> 指定本機已有的 Chromium 執行檔，例如 <code>PW_CHROMIUM_PATH=&lt;Chromium 執行檔路徑&gt; node scripts/vendor-capture/capture.mjs …</code>。</p>
<p>改測本機那幾行指令裡，<code>make site</code> 用 Hugo 把網站建置到 <code>public/</code>，接著用 <code>npx pagefind</code> 產生站內搜尋的索引（這一步要連網下載 pagefind）；建置前要先裝好 Hugo，並以 <code>git submodule update --init --recursive</code> 拉下網站的佈景主題。本站的網址前綴是 <code>/blog/</code>，所以把 <code>public/</code> 以符號連結掛在 <code>.test-www/blog</code> 底下，再用 <code>http-server</code> 從 <code>.test-www</code> 提供檔案，網址才會和正式站的路徑結構一致。<code>4173</code> 是 <code>http-server</code> 監聽的連接埠，與 <code>--url</code> 裡的埠號要一致，換成其他未被占用的埠號時兩處一起改；網站的網址前綴不是 <code>/blog/</code> 時，<code>.test-www</code> 底下的資料夾名稱與 <code>--url</code> 的路徑一起改。<code>-s</code> 讓 <code>http-server</code> 不輸出每一筆存取紀錄，結尾的 <code>&amp;</code> 讓它在背景執行，擷取完用 <code>kill %1</code> 停掉它。</p>
<p>Pixel ID 填錯時沒有任何錯誤訊息：Meta 的伺服器對不存在的 ID 也回應 <code>signals/config/&lt;ID&gt;</code> 的請求（狀態碼 200），設定檔裡仍有幾個不分 ID 的通用模組，而該 ID 專屬的那一段只有 <code>/* empty plugin */</code>，<code>fbevents.js</code> 照樣送出事件。確認 ID 的方法是看 <code>signals/config</code> 回來的設定檔裡有沒有以這個 ID 開頭的 <code>config.set(&quot;&lt;Pixel ID&gt;&quot;, …)</code> 設定（存在的 Pixel 有好幾行，不存在的一行都沒有），或直接在事件管理工具的資料集頁面讀 ID。</p>
<p>執行環境（跑擷取腳本的那台機器）要能連到 <code>connect.facebook.net</code>、<code>www.facebook.com</code>、<code>www.googletagmanager.com</code> 與各家 script 用到的網域。連不到的時候擷取腳本照樣跑完並寫出結果檔，<code>steps</code> 會顯示 script 載入失敗，<code>requests</code> 裡向那些網域的請求帶 <code>failure</code> 欄位；這時結果檔反映的是網路環境，不是服務商 script 的行為。</p>
<h2 id="各次擷取的結果">各次擷取的結果</h2>
<p>這一節收錄服務商的 script 實際載入、請求有回應的擷取，依時間由新到舊排列，每一則寫明擷取時間、瀏覽器版本、script 的版本線索，以及和實測頁、<a href="/blog/devops/monitoring/vendors/meta-pixel-conversions-api/" data-link-title="Meta Pixel 與 Conversions API：廣告平台轉換追蹤的瀏覽器端與伺服器端" data-link-desc="Meta Pixel 在頁面上送出的標準事件與它的資料消費者、瀏覽器端事件被攔截的路徑、Conversions API 從伺服器補送同一批事件的必填欄位，以及兩路並送時以 event_id 去重與用 Test Events 驗證的方式">Meta Pixel 與 Conversions API</a>、<a href="/blog/devops/monitoring/vendors/google-tag-manager/" data-link-title="Google Tag Manager：追蹤碼的部署層、資料層約定與發布治理" data-link-desc="GTM 容器在頁面上承擔的部署責任、資料層作為前端與行銷之間的事件約定、預覽模式與版本發布的驗證方式、自訂 HTML 代碼帶來的安全與效能成本，以及伺服器端代碼管理改變的資料路徑">Google Tag Manager</a> 兩篇機制文對照之後的觀察。網路被擋下的擷取不收，理由在〈執行擷取的指令〉一節的最後一段。</p>
<h3 id="2026-10-23-0126台北meta-pixel有視窗的瀏覽器">2026-10-23 01:26（台北）：Meta Pixel，有視窗的瀏覽器</h3>
<p>擷取時間是 UTC 2026-10-22 17:26:10，瀏覽器是 Playwright 提供的 Chromium 148.0.7778.96，在 macOS 上以 <code>--headed</code> 執行，User-Agent 不帶 <code>HeadlessChrome</code>；實測頁開在本機建置的網站。Pixel 是〈擷取用的測試帳號〉一節建立的測試 Meta 資料集。結果檔是 <code>scripts/vendor-capture/results/meta-pixel-20261022T1726Z.json</code>。</p>
<p>版本線索：<code>fbevents.js</code> 本體 435,058 位元組，SHA-256 開頭是 <code>6f3aa7a0d8d9</code>；<code>fbq.version</code> 是 <code>2.9.427</code>，<code>signals/config</code> 請求的查詢參數同樣帶 <code>v=2.9.427</code>、<code>r=stable</code>。這個 Pixel 的設定檔裡出現 26 個模組名稱，與本篇觀察相關的是 <code>cookie</code>（寫 <code>_fbp</code> 與 <code>_fbc</code>）、<code>inferredevents</code>（<a href="/blog/devops/monitoring/knowledge-cards/autocapture/" data-link-title="Autocapture（自動事件收集）" data-link-desc="盤點網站實際送出哪些事件與內容、報表裡出現網站沒有呼叫過的事件，或在手動埋點與自動收集之間取捨時">自動事件</a>）與 <code>botblocking</code>（機器人流量）；不存在的 ID 拿到的設定檔裡沒有 <code>botblocking</code>，也沒有任何 <code>config.set(&quot;&lt;ID&gt;&quot;, …)</code> 設定。</p>
<p><strong>事件請求的形式。</strong> 每一筆事件都是向 <code>www.facebook.com/tr/</code> 送出的 POST，參數放在表單 body 裡，<code>rqm=formPOST</code>。<code>fbevents.js</code> 建立一個隱藏的表單，把它提交到一個 iframe，所以 Playwright 把這些請求記成 <code>document</code> 類型（<code>resourceType</code>），實測頁面板也因此列不出它們。<code>ev</code> 是事件名稱；<code>fbevents.js</code> 把 <code>fbq('track', …)</code> 的第三個引數（事件參數物件）轉成 <code>cd[...]</code> 參數，例如 ViewContent 的 <code>cd[content_name]</code>、Purchase 的 <code>cd[value]</code> 與 <code>cd[currency]</code>，第四個引數帶的 <code>eventID</code> 轉成 <code>eid</code>。網站同時從瀏覽器與伺服器送同一筆事件時，Meta 用 <code>eid</code> 把兩邊去重，機制見 <a href="/blog/devops/monitoring/vendors/meta-pixel-conversions-api/" data-link-title="Meta Pixel 與 Conversions API：廣告平台轉換追蹤的瀏覽器端與伺服器端" data-link-desc="Meta Pixel 在頁面上送出的標準事件與它的資料消費者、瀏覽器端事件被攔截的路徑、Conversions API 從伺服器補送同一批事件的必填欄位，以及兩路並送時以 event_id 去重與用 Test Events 驗證的方式">Meta Pixel 與 Conversions API</a> 的去重一節；這次的測試 Pixel 沒有伺服器端的事件來源，<code>eid</code> 只出現在瀏覽器端這一路。</p>
<p><strong>Pixel 自動加送的事件。</strong> Pixel 載入之後，每按一次實測頁的按鈕，<code>fbevents.js</code> 都在實測頁呼叫的事件之外多送一筆 <code>SubscribedButtonClick</code>，依兩筆請求的 <code>ts</code> 參數，相差不到 10 毫秒。這筆事件由 <code>fbevents.js</code> 讀取被點的按鈕之後自動產生，內容是按鈕文字（<code>cd[buttonText]</code> 是「送 ViewContent」）、按鈕的 DOM 特徵（<code>cd[buttonFeatures]</code>）與頁面特徵（<code>cd[pageFeatures]</code>）。網站開發者沒有為按鈕寫任何追蹤程式，按鈕文字就由 <code>fbevents.js</code> 送到了 Meta；盤點一個網站送出哪些資料時，要把 Pixel 自動加送的事件算進去。載入 Pixel 的那一下按鈕沒有對應的 <code>SubscribedButtonClick</code>，那時 <code>fbevents.js</code> 還沒載入。</p>
<p><strong>cookie 與參數的對應。</strong> <code>_fbp</code> 的值是 <code>fb.0.</code> 接毫秒時間戳與一串隨機數，每一筆事件的 <code>fbp</code> 參數都帶著這個值。帶 <code>fbclid</code> 重新開啟頁面之後多了 <code>_fbc</code>，值是 <code>fb.0.</code> 接毫秒時間戳與 <code>fbclid</code> 的原值，那一次的 PageView 同時帶 <code>fbp</code> 與 <code>fbc</code> 兩個參數，另外還有一個 <code>fbcs</code> 參數，原始碼裡它與 <code>fbc</code> 寫在同一組點擊 ID 的處理裡，本篇沒有進一步確認它記錄的內容。兩個 cookie 都由 <code>fbevents.js</code> 以 JavaScript 寫入（<code>httpOnly</code> 為 false）、<code>SameSite=Lax</code>，到期時間是擷取時間起算 90 天。</p>
<p><strong><code>o</code> 參數。</strong> 這一次的每一筆事件都帶 <code>o=4126</code>。<code>fbevents.js</code> 原始碼裡把 <code>o</code> 定義成一組旗標的位元和，4126 拆開是 <code>FIRST_PARTY_COOKIES</code>、<code>AUTO_CONFIG</code>、<code>CONFIG_LOADING</code>、<code>SUPPORTS_DEFINE_PROPERTY</code> 與 <code>SUPPORTS_SEND_BEACON</code>。原始碼裡另有一個 <code>IS_HEADLESS</code> 旗標，在無頭模式下加進 <code>o</code>；01:23 那次無頭擷取沒有送出任何事件，所以沒有 <code>o</code> 值可以對照。另一個參數 <code>coo</code> 在原始碼裡記錄的是免程式碼事件設定（codeless）是否被關閉，不是 cookie 的設定。</p>
<h3 id="2026-10-23-0123台北meta-pixel無頭模式">2026-10-23 01:23（台北）：Meta Pixel，無頭模式</h3>
<p>擷取時間是 UTC 2026-10-22 17:23:23，瀏覽器是 Chromium 148.0.7778.96 的無頭模式；Pixel、實測頁與操作順序與 01:26 那次以 <code>--headed</code> 執行的擷取相同，差別只在沒有加 <code>--headed</code>。結果檔是 <code>scripts/vendor-capture/results/meta-pixel-20261022T1723Z.json</code>。</p>
<p>版本線索：<code>fbevents.js</code> 本體的位元組數與 SHA-256、<code>fbq.version</code> 都與 01:26 那一次相同，兩次擷取用的是同一版 script，差別只來自擷取模式。</p>
<p><code>fbevents.js</code> 與 <code>signals/config</code> 都載入成功，<code>_fbp</code> 與 <code>_fbc</code> 照樣寫入，向 <code>www.facebook.com/tr/</code> 的事件請求一筆都沒有：PageView、ViewContent、Purchase 都沒有送出。差別來自無頭模式的 User-Agent 帶 <code>HeadlessChrome</code>，攔下事件的是 Pixel 設定檔載入的 <code>botblocking</code> 模組，機制見〈擷取方式〉一節。實測頁狀態訊息列顯示「fbevents.js 已載入」，從頁面上看不出事件沒送。<a href="/blog/devops/monitoring/vendors/meta-pixel-conversions-api/" data-link-title="Meta Pixel 與 Conversions API：廣告平台轉換追蹤的瀏覽器端與伺服器端" data-link-desc="Meta Pixel 在頁面上送出的標準事件與它的資料消費者、瀏覽器端事件被攔截的路徑、Conversions API 從伺服器補送同一批事件的必填欄位，以及兩路並送時以 event_id 去重與用 Test Events 驗證的方式">Meta Pixel 與 Conversions API</a> 列的瀏覽器端損失路徑裡，script 被廣告攔截器擋下時網站還有機會察覺載入失敗；這一次是 script 載入成功、cookie 也寫了，事件在送出之前被設定檔載入的模組攔下。</p>
<h3 id="2026-10-23-0045台北gtm-容器只帶一個-ga4-代碼">2026-10-23 00:45（台北）：GTM 容器只帶一個 GA4 代碼</h3>
<p>擷取時間是 UTC 2026-10-22 16:45:30，瀏覽器是 Playwright 提供的 Chromium 148.0.7778.96 的無頭模式（collect 請求的 <code>uaa=arm</code>、<code>uab=64</code> 顯示是 macOS arm64），實測頁開在本機建置的網站（<code>http://localhost:4173/blog/monitoring/vendors/google-tag-manager-lab/</code>）。GTM 容器是〈擷取用的測試帳號〉一節建立的測試容器，發布的版本裡只有一個 Google Analytics 代碼（tag），觸發條件（trigger）是 Initialization - All Pages，沒有任何以 <code>purchase</code> 為名的自訂事件觸發條件。代碼與觸發條件的定義見 <a href="/blog/devops/monitoring/vendors/google-tag-manager/" data-link-title="Google Tag Manager：追蹤碼的部署層、資料層約定與發布治理" data-link-desc="GTM 容器在頁面上承擔的部署責任、資料層作為前端與行銷之間的事件約定、預覽模式與版本發布的驗證方式、自訂 HTML 代碼帶來的安全與效能成本，以及伺服器端代碼管理改變的資料路徑">Google Tag Manager</a> 講 GTM 責任的那一節。結果檔是 <code>scripts/vendor-capture/results/gtm-20261022T1645Z.json</code>。</p>
<p>版本線索：<code>gtm.js</code> 的回應本體 546,027 位元組，SHA-256 開頭是 <code>b970456b1ca6</code>；回應標頭只有 <code>content-type</code>、<code>date</code> 與 <code>cache-control: private, max-age=900</code>。</p>
<p><strong>載入容器之後送出的請求。</strong> 瀏覽器先向 <code>www.googletagmanager.com</code> 取得 <code>gtm.js?id=&lt;容器 ID&gt;</code>（200），約 0.1 秒後由容器裡的 GA4 代碼向 <code>www.google-analytics.com/g/collect</code> 送出一筆 POST（204）。這一筆 collect 請求的事件資料全部放在網址的查詢參數裡，POST body 是空的：<code>tid</code> 是評估 ID（Measurement ID），<code>en=page_view</code> 是事件名稱，<code>dl</code> 與 <code>dt</code> 是頁面網址與標題。<code>dl</code> 記錄的網址沒有連接埠號，實測頁實際開在 4173 埠，所以在本機用不同埠號測試時，GA4 報表上的頁面網址看不出是哪一個埠號的伺服器送來的。</p>
<p><strong>GA4 代碼寫下的 cookie。</strong> 一筆 <code>page_view</code> 之後，實測頁所在的網域（這次是 <code>localhost</code>）多出兩個 cookie。<code>_ga</code> 的值是 <code>GA1.1.</code> 接兩段數字，這兩段數字與 collect 請求的 <code>cid</code> 參數相同，GA4 用它辨識同一個瀏覽器。另一個 cookie 的名稱是 <code>_ga_</code> 接上評估 ID 去掉 <code>G-</code> 的部分，值記錄這個瀏覽器在這個 GA4 資源（property）下的工作階段，<code>s</code> 之後的時間戳與 collect 請求的 <code>sid</code> 參數相同。兩個 cookie 都由 GA4 代碼以 JavaScript 寫入（<code>httpOnly</code> 為 false）、<code>SameSite=Lax</code>，到期時間是擷取時間起算 400 天（這是 Chrome 對 cookie 到期時間的上限，GA4 代碼自己設的預設是兩年，見 <a href="/blog/devops/monitoring/knowledge-cards/first-party-cookie/" data-link-title="First-party Cookie（第一方 cookie）" data-link-desc="盤點網站寫下的識別碼 cookie、比對追蹤碼的 cookie 期限，或判斷某個 cookie 受不受瀏覽器的第三方 cookie 限制時">First-party Cookie（第一方 cookie）</a>）。</p>
<p><strong>往資料層推 purchase 事件之後。</strong> <code>window.dataLayer</code> 依序是實測頁推的 <code>gtm.js</code>、容器自己推的 <code>gtm.dom</code> 與 <code>gtm.load</code>，以及實測頁的「往資料層推 purchase 事件」按鈕推的 <code>purchase</code>（帶 <code>order_id</code>、<code>value</code>、<code>currency</code>）。推入 <code>purchase</code> 之後沒有新的請求，成因是這個容器裡沒有以名稱為 <code>purchase</code> 的自訂事件觸發條件啟動的 GA4 事件代碼，那一筆只留在陣列裡。<a href="/blog/devops/monitoring/vendors/google-tag-manager/" data-link-title="Google Tag Manager：追蹤碼的部署層、資料層約定與發布治理" data-link-desc="GTM 容器在頁面上承擔的部署責任、資料層作為前端與行銷之間的事件約定、預覽模式與版本發布的驗證方式、自訂 HTML 代碼帶來的安全與效能成本，以及伺服器端代碼管理改變的資料路徑">Google Tag Manager</a> 的資料層一節描述的分工，在網路請求上就是這個樣子：網站推什麼事件由前端決定，事件是否變成送往某一家的請求由容器的設定決定。</p>
<p><strong>Playwright 對 collect 請求的記錄。</strong> Playwright 把 <code>g/collect</code> 那一筆記成 <code>requestfailed</code>（<code>net::ERR_ABORTED</code>）。作者在這次擷取之後另外用 Chrome DevTools Protocol 的網路事件對照同一個操作，瀏覽器在中止之前已收到 204，事件有送達；那次對照沒有存成結果檔。結果檔因此同時帶 <code>status: 204</code> 與 <code>failure</code>，判讀以 <code>status</code> 為準，見〈擷取結果檔（JSON）的欄位〉。請求清單裡另有一筆 <code>cdn.jsdelivr.net</code> 的 Mermaid script，那是本站每一頁都會載入的資源，與 GTM 無關。</p>
]]></content:encoded></item><item><title>追蹤資產的歸屬：Meta、GTM 與 GA4 的擁有層級、權限分級與移交方式</title><link>https://tarrragon.github.io/blog/devops/monitoring/vendors/tracking-account-ownership/</link><pubDate>Fri, 23 Oct 2026 00:00:00 +0800</pubDate><guid>https://tarrragon.github.io/blog/devops/monitoring/vendors/tracking-account-ownership/</guid><description>&lt;p>這篇整理 Meta、Google Tag Manager（GTM）與 Google Analytics 4（GA4）三家服務商（vendor）的帳號結構：網站上的追蹤資產屬於哪一層的擁有者、誰能對它做什麼、它能不能換到另一個擁有者底下。範圍是擁有關係與權限的結構，各家後台的操作步驟以官方說明為準。內容依據 2026 年 10 月讀取的各家說明頁，帳號規則由服務商決定、會改，引用時要回原頁確認；文中由權限設計推出、說明頁沒有直接寫的做法，會在句子裡標明。&lt;/p>
&lt;h2 id="網站程式碼引用的-id-與它所屬的擁有者">網站程式碼引用的 ID 與它所屬的擁有者&lt;/h2>
&lt;p>三家的追蹤碼放進網站時，程式碼裡都寫著一個 ID，而這個 ID 指向一個由某一層擁有者持有的資產：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Meta&lt;/strong>：網站程式碼裡寫的是 Pixel ID（一串數字）。Pixel 收到的事件在 Meta 那一端整理成一個資料集（dataset），本文稱它為 &lt;strong>Meta 資料集&lt;/strong>，事件管理工具（Events Manager）的資料集頁面顯示的 ID 就是網站要填的 Pixel ID。Meta 資料集的擁有者是商家資產管理組合（business portfolio，舊名 Business Manager，下文簡稱&lt;strong>組合&lt;/strong>）；廣告帳號與粉絲專頁也是組合底下的資產。Facebook 的個人帳號不擁有組合底下的資產，它是組合裡的一位成員，透過組合取得權限。另一條路徑是還沒有建組合、直接在某個人的廣告帳號底下建立 Pixel，這時 Pixel 跟著那個廣告帳號，而那個廣告帳號由建立它的人以自己的 Facebook 帳號管理。&lt;/li>
&lt;li>&lt;strong>GTM&lt;/strong>：網站程式碼裡寫的是 &lt;code>GTM-&lt;/code> 開頭的容器 ID。&lt;strong>GTM 容器&lt;/strong>（container）是存放代碼（tag）、觸發條件（trigger）與變數的設定單位，網站上的一段片段載入它；容器屬於一個 GTM 帳戶（account），一個帳戶底下可以有多個容器。Google 的說明寫一家公司通常只需要一個帳戶。&lt;/li>
&lt;li>&lt;strong>GA4&lt;/strong>：直接安裝 GA4 時，網站程式碼裡寫的是 &lt;code>G-&lt;/code> 開頭的評估 ID（Measurement ID）。評估 ID 屬於網站資料串流（data stream），資料串流屬於一個 &lt;strong>GA4 資源&lt;/strong>（property，一個網站或 App 的報表與設定單位），資源屬於一個 GA4 帳戶。Google 對帳戶的定義是「一個法律實體擁有其資料的一組資源」。&lt;/li>
&lt;/ul>
&lt;p>判斷一個資產的歸屬時要確認兩件事：它屬於哪一層的擁有者，以及它能不能換到另一個擁有者底下。資產換得了擁有者時，網站程式碼不必動；換不了時，要讓新擁有者持有它就只能建一個新資產，網站上的 ID 跟著改，舊資產累積的資料留在原擁有者那裡。三家的資產各屬於哪一種，在〈追蹤資產換擁有者的條件〉一節逐家說明。&lt;/p>
&lt;p>Meta 的服務條款規定每個人只建立一個自己的個人帳號，所以「另開一個個人帳號給公司用」這條路在 Meta 不成立。組合是公司資產與員工個人帳號之間的分隔：資產屬於組合，個人帳號只是進出組合的身分。&lt;/p>
&lt;h2 id="metagtm-與-ga4-的權限分級">Meta、GTM 與 GA4 的權限分級&lt;/h2>
&lt;p>三家都把權限分成兩層：管理擁有者本身（組合或帳戶）的權限，與操作底下個別資產的權限。&lt;/p>
&lt;p>&lt;strong>Meta 的組合&lt;/strong>分成完整存取權限（full access，舊名完整控制權）與部分存取權限（partial access）。部分存取權限再分成基本（Basic，舊名員工）與應用程式和整合（Apps and integrations，舊名開發人員）；財務相關的權限是另外加上的附加權限。持有完整存取權限的成員也可以給其他人 3 到 75 天的暫時存取權限，暫時存取只有基本權限、到期自動失效，適合只需要短期協助的外部人員。持有完整存取權限的成員可以新增與移除成員、變更組合的設定，Meta 建議至少兩位成員擁有完整存取權限。成員對個別資產（某個 Meta 資料集、某個廣告帳號）的權限另外指派。&lt;/p>
&lt;p>&lt;strong>GTM&lt;/strong> 在帳戶層有管理員（Admin）與使用者（User）兩種角色，在容器層有五級權限：無權限、讀取（Read）、編輯（Edit）、核准（Approve）、發布（Publish）。持有編輯權限的成員可以修改容器裡的代碼、觸發條件與變數，但不能建立版本也不能發布；持有核准權限的成員另外可以建立版本與工作區（workspace，容器尚未發布的草稿），但不能發布。&lt;a href="https://tarrragon.github.io/blog/devops/monitoring/vendors/google-tag-manager/" data-link-title="Google Tag Manager：追蹤碼的部署層、資料層約定與發布治理" data-link-desc="GTM 容器在頁面上承擔的部署責任、資料層作為前端與行銷之間的事件約定、預覽模式與版本發布的驗證方式、自訂 HTML 代碼帶來的安全與效能成本，以及伺服器端代碼管理改變的資料路徑">Google Tag Manager&lt;/a> 的自訂 HTML 一節說明過發布權限等同於在網站上執行任意程式碼的權限，容器層的分級讓這個權限可以只交給少數人。GTM 沒有「擁有者」這個角色，帳戶只有管理員，後台不允許把所有管理員移除；Google 建議至少有兩位管理員。&lt;/p>
&lt;p>&lt;strong>GA4&lt;/strong> 的角色是管理員（Administrator）、編輯者（Editor）、行銷人員（Marketer）、分析師（Analyst）、檢視者（Viewer），可以指派在帳戶層或資源層。分析師在檢視者的權限之上，可以把自己建立的探索分析分享給資源的其他成員；行銷人員在分析師的權限之上，可以建立與編輯目標對象、事件與關鍵事件（key events），並修改歸因模式的設定；資源層的完整設定權由編輯者持有。另外有兩項可以加在角色上的資料限制：不含成本指標（No Cost Metrics）與不含營收指標（No Revenue Metrics），各自讓成員看不到那一類指標，用在外部人員只該看流量與成效、不該看到營收或廣告花費的情況。GA4 的使用者管理說明頁沒有提到權限到期的設定，和 Meta 的暫時存取權限不同：只需要短期看報表的外部人員，要由管理員在期限到時自己移除。&lt;/p>
&lt;h2 id="追蹤資產換擁有者的條件meta-資料集gtm-容器與-ga4-資源">追蹤資產換擁有者的條件：Meta 資料集、GTM 容器與 GA4 資源&lt;/h2>
&lt;p>&lt;strong>Meta 資料集不能轉移，也不能刪除。&lt;/strong> Meta 的說明頁在建立資料集的步驟之前寫明這一點，替代做法是把資料集分享給合作夥伴（partner，另一個組合，例如代理商的組合）或加入成員。分享只能由擁有資產的組合發起，取得分享的合作夥伴即使有完整權限，也不能再分享給第三方。同樣固定的還有廣告帳號：在某個組合裡建立的廣告帳號永久屬於那個組合，不能刪除也不能轉出；不屬於任何組合的廣告帳號可以被認領進一個組合，而廣告帳號屬於別人時，要由那個擁有者批准認領的要求。粉絲專頁可以換組合，流程是原擁有者先把它從自己的組合移除，另一個組合再加入它。&lt;/p>
&lt;p>所以 Pixel 建在某個組合底下之後，要讓另一方使用它只有兩條路：原組合把 Meta 資料集分享給對方，資料集仍屬於原組合；或對方在自己的組合另建一個資料集、把網站上的 Pixel ID 換掉，代價是原資料集累積的事件資料留在原組合裡。&lt;/p>
&lt;p>&lt;strong>GTM 容器沒有官方的搬移功能。&lt;/strong> Google 的說明頁沒有寫容器能不能從一個帳戶移到另一個帳戶，有文件記載的路徑是把容器匯出成 JSON 檔，再匯入到另一個帳戶的容器裡（匯入時可選擇覆寫或合併）。匯入的目標是另一個容器，它有自己的容器 ID，所以走這條路之後網站上的容器片段要換成新的 ID。不換 ID 的做法是不搬容器、換人移交：在原帳戶加入新擁有者的管理員，再移除原本的管理員。先加後移的順序是因為後台不允許把所有管理員移除，反過來做時移除最後一位會失敗。這是由權限設計推出的做法，前提是整個帳戶裡只有要移交的容器。&lt;/p>
&lt;p>&lt;strong>GA4 資源可以搬到另一個帳戶。&lt;/strong> 執行搬移的人要在來源帳戶與目的帳戶都同時有管理員與編輯者兩個角色。Google Marketing Platform 機構（organization）是位在帳戶之上、由公司建立用來集中管理 Google 行銷產品的單位，搬移受它限制：兩個帳戶分屬不同機構時不能搬；只有目的帳戶屬於機構時，由目的帳戶所屬機構的管理員把來源帳戶連結進同一個機構。說明頁沒有列出兩個帳戶都不屬於任何機構時的限制。搬移時報表資料是移過去而不是複製一份，資料串流與它的設定、資源設定、與其他產品的連結都一起移動，所以網站上的評估 ID 不必改；搬移之前的變更記錄留在來源帳戶。連結本身跟著資源移動；被連結的 Google Ads 帳戶與 BigQuery 專案的歸屬，不在本篇範圍。不能搬的情形包括資源連結了 Google Ad Manager、還有未抽樣報表（GA4 360 付費版才有的報表）正在處理、目的帳戶的資源數已達上限（預設 50 個），以及 GA4 360 資源的機構連結還沒有驗證。&lt;/p>
&lt;p>三家可以換擁有者的單位不同：Meta 資料集與組合內建立的廣告帳號不能換，GTM 容器只能複製設定、不能保留 ID，GA4 資源能整個搬。三家共通的是最高的那一層（組合、帳戶）可以換人移交：把新擁有者的人加成完整存取權限或管理員，再移除原本的人，底下的資產與 ID 全部不動。換人移交是由三家的權限設計推出的做法，說明頁沒有寫成移交流程。它的前提是那一層裡只放著要移交的資產，而且三件事要先處理：Meta 組合裡由別的組合以合作夥伴身分分享進來的資產，換人之後會連同存取一起交出去；廣告帳號綁的付款方式仍是原擁有者的，換人不會改掉它；GA4 與 GTM 帳戶若連結在原擁有者的 Google Marketing Platform 機構底下，換人不會讓帳戶離開那個機構（機構管理員之後還保有哪些權限，本篇沒有查證）。&lt;/p>
&lt;h2 id="追蹤資產的歸屬安排公司自營與委託代理商">追蹤資產的歸屬安排：公司自營與委託代理商&lt;/h2>
&lt;p>Meta 資料集不能轉、GTM 容器不能保留 ID、GA4 資源跨機構不能搬，這三項限制推出一個安排原則：&lt;strong>追蹤資產建在公司自己的組合與帳戶底下，代理商以合作夥伴或成員的身分取得權限。&lt;/strong> 反過來由代理商在自己的組合或帳戶裡建立 Pixel、容器與資源再給公司用，日常運作沒有差別，差別出現在合作結束的時候：Meta 資料集留在代理商的組合裡，公司要嘛繼續靠代理商分享才看得到，要嘛另建一個並改網站上的 Pixel ID；GTM 容器要匯出匯入並換 ID；GA4 資源要搬到公司的帳戶，而兩邊分屬不同機構時搬不了。GTM 的說明頁直接寫了這個建議：帳戶要由組織內部的人管理，不要交給外部代理商。&lt;/p>
&lt;p>各家對應的做法，以及這樣分的理由：&lt;/p></description><content:encoded><![CDATA[<p>這篇整理 Meta、Google Tag Manager（GTM）與 Google Analytics 4（GA4）三家服務商（vendor）的帳號結構：網站上的追蹤資產屬於哪一層的擁有者、誰能對它做什麼、它能不能換到另一個擁有者底下。範圍是擁有關係與權限的結構，各家後台的操作步驟以官方說明為準。內容依據 2026 年 10 月讀取的各家說明頁，帳號規則由服務商決定、會改，引用時要回原頁確認；文中由權限設計推出、說明頁沒有直接寫的做法，會在句子裡標明。</p>
<h2 id="網站程式碼引用的-id-與它所屬的擁有者">網站程式碼引用的 ID 與它所屬的擁有者</h2>
<p>三家的追蹤碼放進網站時，程式碼裡都寫著一個 ID，而這個 ID 指向一個由某一層擁有者持有的資產：</p>
<ul>
<li><strong>Meta</strong>：網站程式碼裡寫的是 Pixel ID（一串數字）。Pixel 收到的事件在 Meta 那一端整理成一個資料集（dataset），本文稱它為 <strong>Meta 資料集</strong>，事件管理工具（Events Manager）的資料集頁面顯示的 ID 就是網站要填的 Pixel ID。Meta 資料集的擁有者是商家資產管理組合（business portfolio，舊名 Business Manager，下文簡稱<strong>組合</strong>）；廣告帳號與粉絲專頁也是組合底下的資產。Facebook 的個人帳號不擁有組合底下的資產，它是組合裡的一位成員，透過組合取得權限。另一條路徑是還沒有建組合、直接在某個人的廣告帳號底下建立 Pixel，這時 Pixel 跟著那個廣告帳號，而那個廣告帳號由建立它的人以自己的 Facebook 帳號管理。</li>
<li><strong>GTM</strong>：網站程式碼裡寫的是 <code>GTM-</code> 開頭的容器 ID。<strong>GTM 容器</strong>（container）是存放代碼（tag）、觸發條件（trigger）與變數的設定單位，網站上的一段片段載入它；容器屬於一個 GTM 帳戶（account），一個帳戶底下可以有多個容器。Google 的說明寫一家公司通常只需要一個帳戶。</li>
<li><strong>GA4</strong>：直接安裝 GA4 時，網站程式碼裡寫的是 <code>G-</code> 開頭的評估 ID（Measurement ID）。評估 ID 屬於網站資料串流（data stream），資料串流屬於一個 <strong>GA4 資源</strong>（property，一個網站或 App 的報表與設定單位），資源屬於一個 GA4 帳戶。Google 對帳戶的定義是「一個法律實體擁有其資料的一組資源」。</li>
</ul>
<p>判斷一個資產的歸屬時要確認兩件事：它屬於哪一層的擁有者，以及它能不能換到另一個擁有者底下。資產換得了擁有者時，網站程式碼不必動；換不了時，要讓新擁有者持有它就只能建一個新資產，網站上的 ID 跟著改，舊資產累積的資料留在原擁有者那裡。三家的資產各屬於哪一種，在〈追蹤資產換擁有者的條件〉一節逐家說明。</p>
<p>Meta 的服務條款規定每個人只建立一個自己的個人帳號，所以「另開一個個人帳號給公司用」這條路在 Meta 不成立。組合是公司資產與員工個人帳號之間的分隔：資產屬於組合，個人帳號只是進出組合的身分。</p>
<h2 id="metagtm-與-ga4-的權限分級">Meta、GTM 與 GA4 的權限分級</h2>
<p>三家都把權限分成兩層：管理擁有者本身（組合或帳戶）的權限，與操作底下個別資產的權限。</p>
<p><strong>Meta 的組合</strong>分成完整存取權限（full access，舊名完整控制權）與部分存取權限（partial access）。部分存取權限再分成基本（Basic，舊名員工）與應用程式和整合（Apps and integrations，舊名開發人員）；財務相關的權限是另外加上的附加權限。持有完整存取權限的成員也可以給其他人 3 到 75 天的暫時存取權限，暫時存取只有基本權限、到期自動失效，適合只需要短期協助的外部人員。持有完整存取權限的成員可以新增與移除成員、變更組合的設定，Meta 建議至少兩位成員擁有完整存取權限。成員對個別資產（某個 Meta 資料集、某個廣告帳號）的權限另外指派。</p>
<p><strong>GTM</strong> 在帳戶層有管理員（Admin）與使用者（User）兩種角色，在容器層有五級權限：無權限、讀取（Read）、編輯（Edit）、核准（Approve）、發布（Publish）。持有編輯權限的成員可以修改容器裡的代碼、觸發條件與變數，但不能建立版本也不能發布；持有核准權限的成員另外可以建立版本與工作區（workspace，容器尚未發布的草稿），但不能發布。<a href="/blog/devops/monitoring/vendors/google-tag-manager/" data-link-title="Google Tag Manager：追蹤碼的部署層、資料層約定與發布治理" data-link-desc="GTM 容器在頁面上承擔的部署責任、資料層作為前端與行銷之間的事件約定、預覽模式與版本發布的驗證方式、自訂 HTML 代碼帶來的安全與效能成本，以及伺服器端代碼管理改變的資料路徑">Google Tag Manager</a> 的自訂 HTML 一節說明過發布權限等同於在網站上執行任意程式碼的權限，容器層的分級讓這個權限可以只交給少數人。GTM 沒有「擁有者」這個角色，帳戶只有管理員，後台不允許把所有管理員移除；Google 建議至少有兩位管理員。</p>
<p><strong>GA4</strong> 的角色是管理員（Administrator）、編輯者（Editor）、行銷人員（Marketer）、分析師（Analyst）、檢視者（Viewer），可以指派在帳戶層或資源層。分析師在檢視者的權限之上，可以把自己建立的探索分析分享給資源的其他成員；行銷人員在分析師的權限之上，可以建立與編輯目標對象、事件與關鍵事件（key events），並修改歸因模式的設定；資源層的完整設定權由編輯者持有。另外有兩項可以加在角色上的資料限制：不含成本指標（No Cost Metrics）與不含營收指標（No Revenue Metrics），各自讓成員看不到那一類指標，用在外部人員只該看流量與成效、不該看到營收或廣告花費的情況。GA4 的使用者管理說明頁沒有提到權限到期的設定，和 Meta 的暫時存取權限不同：只需要短期看報表的外部人員，要由管理員在期限到時自己移除。</p>
<h2 id="追蹤資產換擁有者的條件meta-資料集gtm-容器與-ga4-資源">追蹤資產換擁有者的條件：Meta 資料集、GTM 容器與 GA4 資源</h2>
<p><strong>Meta 資料集不能轉移，也不能刪除。</strong> Meta 的說明頁在建立資料集的步驟之前寫明這一點，替代做法是把資料集分享給合作夥伴（partner，另一個組合，例如代理商的組合）或加入成員。分享只能由擁有資產的組合發起，取得分享的合作夥伴即使有完整權限，也不能再分享給第三方。同樣固定的還有廣告帳號：在某個組合裡建立的廣告帳號永久屬於那個組合，不能刪除也不能轉出；不屬於任何組合的廣告帳號可以被認領進一個組合，而廣告帳號屬於別人時，要由那個擁有者批准認領的要求。粉絲專頁可以換組合，流程是原擁有者先把它從自己的組合移除，另一個組合再加入它。</p>
<p>所以 Pixel 建在某個組合底下之後，要讓另一方使用它只有兩條路：原組合把 Meta 資料集分享給對方，資料集仍屬於原組合；或對方在自己的組合另建一個資料集、把網站上的 Pixel ID 換掉，代價是原資料集累積的事件資料留在原組合裡。</p>
<p><strong>GTM 容器沒有官方的搬移功能。</strong> Google 的說明頁沒有寫容器能不能從一個帳戶移到另一個帳戶，有文件記載的路徑是把容器匯出成 JSON 檔，再匯入到另一個帳戶的容器裡（匯入時可選擇覆寫或合併）。匯入的目標是另一個容器，它有自己的容器 ID，所以走這條路之後網站上的容器片段要換成新的 ID。不換 ID 的做法是不搬容器、換人移交：在原帳戶加入新擁有者的管理員，再移除原本的管理員。先加後移的順序是因為後台不允許把所有管理員移除，反過來做時移除最後一位會失敗。這是由權限設計推出的做法，前提是整個帳戶裡只有要移交的容器。</p>
<p><strong>GA4 資源可以搬到另一個帳戶。</strong> 執行搬移的人要在來源帳戶與目的帳戶都同時有管理員與編輯者兩個角色。Google Marketing Platform 機構（organization）是位在帳戶之上、由公司建立用來集中管理 Google 行銷產品的單位，搬移受它限制：兩個帳戶分屬不同機構時不能搬；只有目的帳戶屬於機構時，由目的帳戶所屬機構的管理員把來源帳戶連結進同一個機構。說明頁沒有列出兩個帳戶都不屬於任何機構時的限制。搬移時報表資料是移過去而不是複製一份，資料串流與它的設定、資源設定、與其他產品的連結都一起移動，所以網站上的評估 ID 不必改；搬移之前的變更記錄留在來源帳戶。連結本身跟著資源移動；被連結的 Google Ads 帳戶與 BigQuery 專案的歸屬，不在本篇範圍。不能搬的情形包括資源連結了 Google Ad Manager、還有未抽樣報表（GA4 360 付費版才有的報表）正在處理、目的帳戶的資源數已達上限（預設 50 個），以及 GA4 360 資源的機構連結還沒有驗證。</p>
<p>三家可以換擁有者的單位不同：Meta 資料集與組合內建立的廣告帳號不能換，GTM 容器只能複製設定、不能保留 ID，GA4 資源能整個搬。三家共通的是最高的那一層（組合、帳戶）可以換人移交：把新擁有者的人加成完整存取權限或管理員，再移除原本的人，底下的資產與 ID 全部不動。換人移交是由三家的權限設計推出的做法，說明頁沒有寫成移交流程。它的前提是那一層裡只放著要移交的資產，而且三件事要先處理：Meta 組合裡由別的組合以合作夥伴身分分享進來的資產，換人之後會連同存取一起交出去；廣告帳號綁的付款方式仍是原擁有者的，換人不會改掉它；GA4 與 GTM 帳戶若連結在原擁有者的 Google Marketing Platform 機構底下，換人不會讓帳戶離開那個機構（機構管理員之後還保有哪些權限，本篇沒有查證）。</p>
<h2 id="追蹤資產的歸屬安排公司自營與委託代理商">追蹤資產的歸屬安排：公司自營與委託代理商</h2>
<p>Meta 資料集不能轉、GTM 容器不能保留 ID、GA4 資源跨機構不能搬，這三項限制推出一個安排原則：<strong>追蹤資產建在公司自己的組合與帳戶底下，代理商以合作夥伴或成員的身分取得權限。</strong> 反過來由代理商在自己的組合或帳戶裡建立 Pixel、容器與資源再給公司用，日常運作沒有差別，差別出現在合作結束的時候：Meta 資料集留在代理商的組合裡，公司要嘛繼續靠代理商分享才看得到，要嘛另建一個並改網站上的 Pixel ID；GTM 容器要匯出匯入並換 ID；GA4 資源要搬到公司的帳戶，而兩邊分屬不同機構時搬不了。GTM 的說明頁直接寫了這個建議：帳戶要由組織內部的人管理，不要交給外部代理商。</p>
<p>各家對應的做法，以及這樣分的理由：</p>
<ul>
<li><strong>Meta</strong>：公司建立自己的組合，Meta 資料集與廣告帳號建在組合底下，再以合作夥伴的方式分享給代理商的組合。代理商自己的員工由代理商在它的組合裡增減，公司不必追蹤代理商的人事異動；合作結束時，公司撤銷對代理商組合的分享，就一次收回所有代理商員工的存取。</li>
<li><strong>GTM</strong>：帳戶由公司建立，公司的管理員把代理商的人員加成帳戶的使用者，再給容器層的權限。發布權限等同在網站上執行任意程式碼，所以權限給到哪一級取決於公司是否在發布前審查代理商的變更：有人審查時，公司把容器層權限給到核准（Approve）這一級，發布由公司的人做；不審查而把發布權限交給代理商時，代理商發布的自訂 HTML 會直接在每一位讀者的瀏覽器上執行，出事的後果由公司承擔。</li>
<li><strong>GA4</strong>：帳戶與資源由公司建立，公司的管理員把代理商的人員加在資源層，角色依代理商要做的事、給到剛好夠用的那一級（<a href="/blog/backend/knowledge-cards/least-privilege/" data-link-title="Least Privilege" data-link-desc="說明身份、服務與人員只應取得完成工作所需的最小權限">最小權限</a>）：看報表與做分析給分析師角色，需要調整目標對象、關鍵事件與歸因設定給行銷人員角色，需要改資源的其他設定才給編輯者角色；代理商不該看到營收或廣告花費時，再加上對應的資料限制。GA4 的說明頁沒有針對代理商的建議，這一條是由角色設計推出的安排。</li>
</ul>
<p><strong>擁有最高管理權的成員（Meta 的完整存取權限、GTM 與 GA4 的管理員）至少兩位，而且至少一位是公司裡長期在職的人。</strong> Meta 與 GTM 的說明頁都建議至少兩位，GTM 還禁止移除所有管理員；GA4 的說明頁沒有這項建議，而它的帳戶同樣只能由管理員增減成員。只有一位時的失效不會立刻出現：組合由一個人就建得起來，建立的人自動取得完整存取權限；其他成員各自握有廣告帳號與資料集的權限，已經指派給其他成員的投放與報表權限不需要完整存取權限，所以唯一的管理員離職或帳號遺失之後，日常的投放與看報表照常運作（由權限設計推出），要等到需要加入新代理商或移除離職者的那一天，才發現沒有人做得到。另一種情形來得比較快：那一位的個人檔案被 Meta 限制刊登廣告時，Meta 的說明寫明，只連結他一個人的廣告帳號也可能一起被停用。</p>
<p>唯一的管理員已經不在、才發現沒有人管理得了之後，三家能不能救回各不相同。</p>
<p>Meta 有一條申訴路徑（管理員爭議），說明頁列出的適用情形有：持有完整存取權限的是沒有所有權的前員工或承包商；公司換了擁有者而失去組合的存取權；組合裡沒有任何人持有完整存取權限；持有完整存取權限的人已過世（附死亡證明）；有法院命令證明申請人是組合所屬公司的合法擁有者。申請時要附上申請人的身分證明、證明擁有這家公司的文件與簽名的聲明書，並且要登入與這個組合有關聯的 Facebook 個人檔案，從 Meta 的商家支援中心送出；核准之後申請人取得完整存取權限，原有成員不會被移出組合。前承包商握著組合不交還，是說明頁明列的情形；唯一一位管理員自己的 Facebook 帳號遺失則不在清單裡，而且那時公司手上可能已經沒有任何與組合有關聯的個人檔案可以登入送件，所以要先走 Facebook 個人帳號的復原，帳號找不回來時，這條申訴路徑很可能走不通（依送件條件推出，本站沒有實際送件驗證）。</p>
<p>GA4 的復原方式是在帳戶裡每一個送資料的網站上放一個寫有驗證資訊的 <code>analytics.txt</code> 檔（放在根網域或送資料的子網域，不能上傳檔案時改在頁面加 meta tag），證明網站屬於自己，帳戶連結了 Google Ads 或 AdSense 時也要證明那些帳戶屬於自己，再填 Google 的聯絡表單。</p>
<p>GTM 的情形寫在同一份 GA4 復原說明頁裡：GTM 目前沒有復原途徑，失去所有管理員的 GTM 帳戶救不回來，只能另建容器、換掉網站上的容器 ID。事先多設一位管理員，省下的就是這些申訴與重建。</p>
<p>只有一個人的公司也適用同一個理由：第二位成員承擔的責任是在唯一的帳號遺失時，還有另一個身分能繼續管理，所以第二位不必是員工，可以是合夥人、家人或委託的會計事務所，給他完整存取權限或管理員。這個人同樣有權限移除原本的管理員，所以人選的條件是可以信任，而不只是方便聯絡。Meta 的條款不允許同一個人另開帳號充當第二位。找不到第二位時，風險集中在那唯一一個帳號上。Meta 的說明頁要求從與組合有關聯的個人檔案送出管理員爭議，那個帳號本身遺失時，這條申訴路徑可能走不通（依送件條件推出，本站沒有實際送件驗證），所以能做的是降低那個帳號遺失的機率、並保住它的復原途徑：替自己的 Facebook 與 Google 帳號開啟兩步驟驗證，把帳號綁定的復原電子郵件與手機號碼設成自己長期持有、不隨公司網域或門號變動的那一組。</p>
<p>帳號是被盜而不是遺失時，情形不同：盜用者以那個帳號的完整存取權限持有組合，網站上的 Pixel 仍把訪客的事件送進他控制的資料集，廣告帳號綁定的付款方式也還能被使用。這時有兩件事不必等帳號復原、也不需要那個帳號的權限，可以同時做：從網站拿掉那個 Pixel 的程式碼，切斷網站送進去的資料；向發卡機構停用廣告帳號綁的那張卡，擋住持續中的扣款（由權限設計推出）。之後走 Facebook 帳號的復原，帳號拿不回來時，在新的組合另建資料集，放回網站的是新的 Pixel ID。</p>
<p><strong>成員的身分屬於個人，資產屬於擁有者。</strong> 三家的成員都是個人身分：Meta 是個人的 Facebook 帳號，GTM 與 GA4 是 Google 帳號。成員用的是公司 Google Workspace 的帳號時，公司在 Workspace 停權或刪除那個帳號，他就登不進 GTM 與 GA4（由帳號機制推出：GTM 與 GA4 以 Google 帳號登入）；用的是個人 Gmail 或個人 Facebook 帳號時，公司停用內部帳號碰不到它們，要到每一個組合與帳戶裡逐一移除；整份離職撤權清單見 <a href="/blog/devops/infra/08-governance-habits/handover-design/" data-link-title="職務交接與存取撤銷設計" data-link-desc="人員異動時的存取撤銷順序、credential rotation、最小交接清單，以及讓交接成本結構性降低的 infra 設計原則">職務交接與存取撤銷設計</a>。資產建在公司的組合與帳戶底下時，員工離職只要由公司的管理員把他從成員中移除，公司就收回了他的權限，資產本身不受影響；資產建在員工自己的組合或帳戶底下時，那位員工就是擁有者，公司沒有可以移除他的權限。</p>
<h2 id="資產與權限的移交方式換代理商與事業轉手">資產與權限的移交方式：換代理商與事業轉手</h2>
<p>移交的每一條路，除了看資產屬於哪一層，還要看兩件事：這一步要哪一方動手，以及那一方是否還聯絡得上、願意配合。下文的「對方」指公司以外要動手的那一方：換代理商時是舊代理商或廣告帳號的擁有者，事業轉手時是賣方；標「由權限設計推出」的做法，是本篇依三家的權限規則推導、Meta 與 Google 的說明頁沒有寫成流程的做法。認領廣告帳號要擁有者批准，分享舊的 Meta 資料集、匯出 GTM 容器、把帳戶連結進 Google Marketing Platform 機構都要對方的人操作，GA4 資源的搬移還要求執行的人在兩個帳戶都同時是管理員與編輯者。對方的狀態分成三種，出口各不相同。對方聯絡得上而且配合時，那段期間是處理這些事的窗口。對方失聯或拒絕配合、但手上沒有公司網站還在使用的資產權限時，剩下的只有不需要對方的路：另建資產、換掉網站上的 ID，以及 Meta 的管理員爭議與 GA4 的 <code>analytics.txt</code> 驗證這兩條由公司自己提出的復原途徑；管理員爭議要從與組合有關聯的個人檔案送件，公司的人從來不是那個組合的成員時這條路可能走不通，<code>analytics.txt</code> 要放在帳戶裡每一個送資料的網站上，所以資源放在代理商、帳戶裡還有其他客戶網站時，<code>analytics.txt</code> 這條路用不上（兩者皆依送件條件推出）。對方不只不配合、還握著資產的權限時（盜用帳號的人、與公司起爭議的代理商），對方能繼續收到網站送出的資料，透過 GTM 容器的發布權限還能在網站上執行程式碼，所以先切斷網站對那個資產的引用：拿掉頁面上屬於對方帳戶的 Pixel 與容器片段，再談移交或另建（由權限設計推出）。</p>
<p><strong>換代理商時</strong>，資產若一開始就建在公司的組合與帳戶底下，要處理的只有權限：公司撤銷舊代理商的合作夥伴分享與成員身分，再把 Meta 資料集與廣告帳號以合作夥伴的方式分享給新代理商的組合，把 GTM 與 GA4 的成員權限給新代理商的人員。撤銷之前先列出舊代理商在公司資產上建立的整合（由權限設計推出）：GTM 容器裡指向代理商自己帳戶的代碼、GA4 連結的代理商 Google Ads 帳戶、Meta 上由代理商系統發出的 Conversions API 存取權杖。這些整合不會因為撤銷權限而消失：指向代理商帳戶的代碼與產品連結會繼續把資料送給代理商；依賴代理商發出的存取權杖的伺服器端事件，可能在撤銷後停止運作。</p>
<p>資產建在舊代理商的組合或帳戶底下時，網站上載入的 ID 是處理的起點。網站上可能同時留著舊代理商建立的 ID，以及更早之前的代理商或員工留下的 ID，而追蹤碼依 ID 送事件：留著的舊 Pixel ID 會讓事件繼續送進舊代理商的資料集，新的資料集也照常收事件，網站畫面與新報表都不會顯示異常；留著的舊 GTM 容器片段更嚴重，舊代理商仍持有那個容器的發布權限，等於仍能在公司網站上執行程式碼。所以要先查清每個網站實際載入的 Pixel ID、容器 ID 與評估 ID（查法見 <a href="/blog/devops/monitoring/vendors/tracking-request-capture/" data-link-title="用 Playwright 擷取追蹤碼請求：擷取方式、記錄欄位與各次結果" data-link-desc="以 Playwright 操作 Meta Pixel 與 GTM 實測頁、記錄各家 script 實際送出的請求與 cookie 的方式，結果檔記錄的欄位與它們的用途，擷取用的測試帳號，以及每一次擷取觀察到的事件請求形式、cookie 與請求參數的對應、無頭瀏覽器下的差異與 script 自動送出的事件">用 Playwright 擷取追蹤碼請求</a>，或在瀏覽器開發者工具的 Network 面板篩選 Meta 與 Google 的網域），再依各家處理：</p>
<ul>
<li><strong>Meta</strong>：Meta 資料集無法轉給另一個組合，公司在自己的組合建新資料集、把網站上的 Pixel ID 換掉。查看舊資料的管道只能由擁有舊資料集的組合開放，合作結束後能不能開放只取決於舊代理商是否配合，所以要在換掉 Pixel ID 之前請舊代理商以合作夥伴的方式把舊資料集分享給公司。Pixel 建在舊代理商某一位人員自己的廣告帳號底下時，那個廣告帳號若不屬於任何組合，公司可以提出認領，由那位人員批准之後進入公司的組合；Meta 的說明頁沒有寫 Pixel 會不會隨廣告帳號一起進來，認領之後要在公司組合的資料集清單裡確認。那位人員不批准、舊代理商與那位人員都聯絡不上，或認領之後 Pixel 不在清單裡時，公司另建新資料集、換掉 Pixel ID，舊的歷史事件拿不回來。</li>
<li><strong>GTM</strong>：公司請舊代理商把容器匯出成 JSON 檔，由公司的人匯入公司帳戶裡的新容器，再換掉網站上的容器片段。舊代理商的帳戶專為這家公司建立、帳戶裡沒有其他客戶的容器時，也可以換人移交（由權限設計推出）：舊代理商把公司的人加成那個帳戶的管理員，再移除代理商的人，帳戶整個移交、容器 ID 不變。舊代理商的帳戶放著多個客戶的容器時，換人移交會把其他客戶的容器一起交出去，所以只有匯出匯入一途。舊代理商聯絡不上或不配合時，公司依網站上實際執行的追蹤碼（Network 面板裡看得到的 Pixel ID、評估 ID 與各家請求）在自己的帳戶建一個新容器，重新設定代碼，再換掉容器片段；舊容器的設定拿不回來。</li>
<li><strong>GA4</strong>：GA4 資源（property）的搬移受 Google Marketing Platform 機構（帳戶之上的管理單位）限制，說明頁寫明擋下的情形是兩個帳戶分屬不同機構，以及只有目的帳戶屬於機構而來源帳戶不屬於；執行的人在兩個帳戶都要同時是管理員與編輯者。依兩個帳戶目前與機構的關係：
<ul>
<li>兩個帳戶屬於同一個機構：資源可以直接搬到公司的帳戶，評估 ID（Measurement ID）不變。</li>
<li>只有公司的帳戶屬於機構：公司是目的帳戶，這正是說明頁擋下的狀態，所以要先由公司所屬機構的管理員把舊代理商的帳戶連結進公司的機構，兩個帳戶屬於同一個機構之後再搬；連結這一步要舊代理商配合。</li>
<li>只有舊代理商的帳戶屬於機構：Google 的說明頁沒有寫這一種情形，本篇沒有驗證能不能直接搬。說明頁寫明可以搬的是兩個帳戶屬於同一個機構的情形，所以符合說明頁條件的做法是請舊代理商機構的管理員把公司的帳戶連結進同一個機構再搬，這要舊代理商配合（依說明頁的條件推出）。搬完之後公司的帳戶仍在舊代理商的機構底下，能不能移出、機構管理員還保有哪些權限，本篇沒有查證（見〈追蹤資產換擁有者的條件〉末段）。</li>
<li>兩個帳戶分屬不同機構：Google 的說明頁寫明不能搬。</li>
<li>兩個帳戶都不屬於任何機構：說明頁沒有寫是否能直接搬。符合說明頁條件的做法是公司建立機構、把兩個帳戶連結進同一個機構再搬；舊代理商的帳戶專為這家公司建立時，也可以把整個帳戶換人移交（由權限設計推出）。</li>
<li>以上各種情形都搬不了，或需要配合的舊代理商聯絡不上、不配合時（任一成立）：公司另建資源、換掉網站上的評估 ID，歷史報表留在舊帳戶。</li>
</ul>
</li>
</ul>
<p><strong>事業轉手時</strong>，買方要的是整套資產連同歷史資料。Meta、GTM 與 GA4 三家都能用換人的方式移交最上層的擁有者（Meta 的組合、GTM 帳戶、GA4 帳戶；由權限設計推出）：賣方把買方指定的人加成組合的完整存取權限，以及 GTM 帳戶與 GA4 帳戶的管理員，再移除賣方的人。在移除賣方的人之前，要先由買方確認自己登得進去、管理得了，因為移除之後賣方已經沒有權限補救；買方這時才發現自己管理不了的話，Meta 只剩管理員爭議、GA4 只剩 <code>analytics.txt</code> 驗證、GTM 沒有復原途徑。這條路徑底下的 Pixel ID、容器 ID、評估 ID 與歷史資料都不動，前提是三件事先處理好（理由見〈追蹤資產換擁有者的條件〉一節末段）：組合裡沒有別的組合分享進來的資產、廣告帳號的付款方式改成買方的、GA4 與 GTM 帳戶不在賣方的機構底下。</p>
<p>賣方的組合或帳戶裡如果還放著與這個事業無關的資產，整個組合或帳戶不能直接交出去。GA4 與 GTM 還有一條路：賣方先把無關的 GA4 資源搬到賣方另一個帳戶、把無關的 GTM 容器匯出匯入到賣方的新帳戶（那些無關容器原本所在的賣方網站要換成新容器 ID，買方的網站不受影響），帳戶裡只剩這個事業的資產之後再整個移交，買方手上的評估 ID 與容器 ID 都不動（由權限設計推出）。Meta 的組合做不到這一步，因為資料集與組合內建立的廣告帳號搬不出去，只能逐項處理；GA4 與 GTM 不走上面那條路時也一樣逐項處理：粉絲專頁移到買方的組合；Meta 資料集只能由賣方以合作夥伴的方式分享給買方，或由買方另建，組合內建立的廣告帳號也只能由賣方分享給買方或由買方另建；GA4 資源依換代理商那一段 GA4 條列的機構條件搬到買方的帳戶，買賣雙方分屬不同機構時搬不了，只能由買方另建；GTM 容器匯出匯入。</p>
<p>所以一個組合或帳戶只放一個事業的資產，轉手時才有整包移交這個選項。Google 說一家公司通常只需要一個 GTM 帳戶，本篇認為這適用於只經營一個事業的公司；同一家公司經營日後可能分拆出售的事業時，按事業各開一個組合與帳戶。</p>
<h2 id="本站實測資產的擁有者">本站實測資產的擁有者</h2>
<p>本站為 <a href="/blog/devops/monitoring/vendors/meta-pixel-lab/" data-link-title="Meta Pixel 實測頁：在瀏覽器裡觀察 fbq 呼叫、送出的請求與寫下的 cookie" data-link-desc="在這一頁按按鈕載入 Meta Pixel、送 PageView、ViewContent 與帶 eventID 的 Purchase，並用頁面上的面板對照每個呼叫送出的請求參數，以及 _fbp 與 fbclid 帶出的 _fbc cookie">Meta Pixel 實測頁</a> 與 <a href="/blog/devops/monitoring/vendors/google-tag-manager-lab/" data-link-title="Google Tag Manager 實測頁：在瀏覽器裡觀察容器載入、資料層事件與代碼送出的請求" data-link-desc="在這一頁按按鈕載入 GTM 容器、往資料層推 purchase 事件，並用頁面上的面板對照 window.dataLayer 的內容、容器載入的代碼與它們送出的請求">Google Tag Manager 實測頁</a> 建立的測試資產，套用的是同一個原則：一個擁有者只放一個用途的資產。Meta 另開一個組合、GTM 與 GA4 各另開一個帳戶，裡面只放實測用的 Meta 資料集、GTM 容器與 GA4 資源，不放進登入帳號原本就有的組合或帳戶；實測結束時，GTM 與 GA4 的帳戶可以整個刪除（刪除後分別有 30 天與 35 天可以從垃圾桶救回），Meta 資料集依規則刪不掉，會一直留在這個只放實測資產的組合裡，不會混進其他用途的組合。建立過程中三家各自的檢查與預設值（Meta 的組合名稱審查、資料集表單預設勾選的 Conversions API 選項、GA4 預設勾選的資料共用選項），以及這些資產上做的擷取，記錄在 <a href="/blog/devops/monitoring/vendors/tracking-request-capture/" data-link-title="用 Playwright 擷取追蹤碼請求：擷取方式、記錄欄位與各次結果" data-link-desc="以 Playwright 操作 Meta Pixel 與 GTM 實測頁、記錄各家 script 實際送出的請求與 cookie 的方式，結果檔記錄的欄位與它們的用途，擷取用的測試帳號，以及每一次擷取觀察到的事件請求形式、cookie 與請求參數的對應、無頭瀏覽器下的差異與 script 自動送出的事件">用 Playwright 擷取追蹤碼請求</a> 的測試帳號一節與各次結果。</p>
<h2 id="延伸閱讀資產建立之後的資料路徑與部署權限">延伸閱讀：資產建立之後的資料路徑與部署權限</h2>
<p>這篇處理的是資產屬於誰；資產建立之後，事件怎麼從網站送進那個 Meta 資料集或 GA4 資源，由各家的機制文說明。Pixel 的瀏覽器端與伺服器端兩條路徑，以及兩條路徑送出同一個事件時靠 <code>event_id</code> 去重，見 <a href="/blog/devops/monitoring/vendors/meta-pixel-conversions-api/" data-link-title="Meta Pixel 與 Conversions API：廣告平台轉換追蹤的瀏覽器端與伺服器端" data-link-desc="Meta Pixel 在頁面上送出的標準事件與它的資料消費者、瀏覽器端事件被攔截的路徑、Conversions API 從伺服器補送同一批事件的必填欄位，以及兩路並送時以 event_id 去重與用 Test Events 驗證的方式">Meta Pixel 與 Conversions API</a>；代理商取得 GTM 容器的發布權限之後能在網站上做什麼、發布前怎麼驗證，見 <a href="/blog/devops/monitoring/vendors/google-tag-manager/" data-link-title="Google Tag Manager：追蹤碼的部署層、資料層約定與發布治理" data-link-desc="GTM 容器在頁面上承擔的部署責任、資料層作為前端與行銷之間的事件約定、預覽模式與版本發布的驗證方式、自訂 HTML 代碼帶來的安全與效能成本，以及伺服器端代碼管理改變的資料路徑">Google Tag Manager</a> 的預覽模式與自訂 HTML 兩節。</p>
]]></content:encoded></item></channel></rss>