<?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>模組五：部署、配額與安全 on Tarragon</title><link>https://tarrragon.github.io/blog/automation/05-deploy-quota-security/</link><description>Recent content in 模組五：部署、配額與安全 on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Mon, 06 Jul 2026 00:00:00 +0800</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/automation/05-deploy-quota-security/index.xml" rel="self" type="application/rss+xml"/><item><title>部署與存取權限的安全含義</title><link>https://tarrragon.github.io/blog/automation/05-deploy-quota-security/deployment-and-access/</link><pubDate>Mon, 06 Jul 2026 00:00:00 +0800</pubDate><guid>https://tarrragon.github.io/blog/automation/05-deploy-quota-security/deployment-and-access/</guid><description>&lt;p>beacon 接收端要接住匿名訪客，就必須設成公開可存取，這帶來一組要理解清楚的安全含義。核心觀念是：這個端點的威脅模型不是「資料外洩」，而是「別人拿這個公開端點能做什麼」。先釐清兩個部署設定各自的安全角色，再界定實際的風險範圍。&lt;/p>
&lt;h2 id="execute-as-與-who-has-access-的分工">execute as 與 who has access 的分工&lt;/h2>
&lt;p>部署 web app 時的兩個設定，一個決定「用誰的權限跑」、一個決定「誰能呼叫」，安全含義不同。&lt;/p>
&lt;p>&lt;strong>execute as（執行身分）決定程式用誰的權限執行。&lt;/strong> 選「我」時，任何打進來的請求都以你的身分執行程式——這是必要的，因為匿名訪客沒有你試算表的寫入權，只有用你的身分才寫得進去。它的含義是：這支程式能碰的資料範圍等於你授權給它的範圍（那一張試算表），而不是呼叫者的權限。所以程式裡&lt;strong>只該做你願意讓匿名請求觸發的事&lt;/strong>——&lt;code>doPost&lt;/code> 寫一列 log 是安全的，但如果程式裡寫了「刪除整張表」的邏輯，那也會用你的權限被匿名觸發。&lt;/p>
&lt;p>&lt;strong>who has access（誰可以存取）決定誰能呼叫這個網址。&lt;/strong> 接匿名 beacon 必須選「所有人」（完全不需登入），這也表示網址一旦洩漏（而它必然公開，見下一段），任何人都能呼叫。這兩個設定合起來的效果是：&lt;strong>一個任何人都能觸發、但只會執行你寫的那幾行、且以你的身分操作你授權的那張表的端點。&lt;/strong>&lt;/p>
&lt;h2 id="這個網址必然公開">這個網址必然公開&lt;/h2>
&lt;p>beacon 端點的網址會被寫進每一頁的 client-side JavaScript，任何人檢視原始碼都看得到。它藏不住，這是 client beacon 架構的本質、不是設定失誤——GA、Cloudflare Web Analytics 那些的收集端點同樣是公開的。所以安全策略不能建立在「保密網址」上，而要建立在「限制這個公開端點能造成的破壞」上。&lt;/p>
&lt;p>這也界定了什麼&lt;strong>不是&lt;/strong>風險：別人拿到網址讀不到你的 Sheet，因為 &lt;code>doPost&lt;/code> 只做 &lt;code>appendRow&lt;/code> 然後回一個固定的 &lt;code>{ok:true}&lt;/code>，不回傳任何試算表內容；他也碰不到你其他 Google 資料，因為授權範圍只綁那張表。端點是「只能寫、寫入內容固定、回應不洩漏」的，這個形狀本身就把資料外洩排除了。&lt;/p>
&lt;h2 id="實際的風險範圍">實際的風險範圍&lt;/h2>
&lt;p>公開端點的真實風險是騷擾型的，有兩種。&lt;strong>髒資料&lt;/strong>：有人直接對網址 POST 任意內容，往你的 log 塞垃圾列、污染統計。&lt;strong>配額消耗&lt;/strong>：有人狂打這個端點，吃掉你的執行配額，嚴重時排擠正常 beacon（配額的細節見&lt;a href="https://tarrragon.github.io/blog/automation/05-deploy-quota-security/quota-abuse-privacy/" data-link-title="配額、濫用防護、隱私與遷移訊號" data-link-desc="免費配額實際碰撞時會怎樣、擋髒資料與過濾自己瀏覽的做法、不記 PII 的隱私立場、以及量大到該離開 Sheets 的訊號">配額、濫用與隱私&lt;/a>）。&lt;/p>
&lt;p>這兩種風險的共通點是它們&lt;strong>不會洩漏或破壞你的資料，只會污染統計或耗資源&lt;/strong>。對沒沒無聞的個人 blog，攻擊者缺乏動機花力氣灌一個小站的瀏覽計數，實際發生機率低。所以務實的姿態是：先讓公開端點跑著、把「限制破壞範圍」的保護當成「遇到再加」的選項，而不是上線前就必備。具體的防護手段在下一篇。&lt;/p>
&lt;h2 id="更新部署不換網址安全角度">更新部署不換網址（安全角度）&lt;/h2>
&lt;p>日常維護會反覆改 &lt;code>doPost&lt;/code>，改完要讓 &lt;code>/exec&lt;/code> 反映新版本、而網址不變——用「管理部署作業 → 編輯 → 版本選新版本」，不要用「新增部署作業」（那會產生新網址）。這個操作除了避免假故障（見&lt;a href="https://tarrragon.github.io/blog/automation/01-apps-script-basics/web-app-deployment-model/" data-link-title="web app 部署模型與授權" data-link-desc="把 Apps Script 掛成可被 HTTP 呼叫的端點時，doGet/doPost 進入點、exec 與 dev 兩種網址、以及更新部署為什麼要用同一個網址">web app 部署模型&lt;/a>），也有安全意義：換網址意味著舊網址可能還殘留在快取的頁面裡繼續被打，管理起來多一個要追蹤的公開端點。用同一個部署更新，公開端點就始終只有一個、行為可控。&lt;/p>
&lt;h2 id="下一步">下一步&lt;/h2>
&lt;p>存取權限的安全框架清楚後，配額實際會怎麼碰撞、怎麼擋濫用、隱私怎麼守、以及量大到該遷移的訊號，見&lt;a href="https://tarrragon.github.io/blog/automation/05-deploy-quota-security/quota-abuse-privacy/" data-link-title="配額、濫用防護、隱私與遷移訊號" data-link-desc="免費配額實際碰撞時會怎樣、擋髒資料與過濾自己瀏覽的做法、不記 PII 的隱私立場、以及量大到該離開 Sheets 的訊號">配額、濫用與隱私&lt;/a>。&lt;/p></description><content:encoded><![CDATA[<p>beacon 接收端要接住匿名訪客，就必須設成公開可存取，這帶來一組要理解清楚的安全含義。核心觀念是：這個端點的威脅模型不是「資料外洩」，而是「別人拿這個公開端點能做什麼」。先釐清兩個部署設定各自的安全角色，再界定實際的風險範圍。</p>
<h2 id="execute-as-與-who-has-access-的分工">execute as 與 who has access 的分工</h2>
<p>部署 web app 時的兩個設定，一個決定「用誰的權限跑」、一個決定「誰能呼叫」，安全含義不同。</p>
<p><strong>execute as（執行身分）決定程式用誰的權限執行。</strong> 選「我」時，任何打進來的請求都以你的身分執行程式——這是必要的，因為匿名訪客沒有你試算表的寫入權，只有用你的身分才寫得進去。它的含義是：這支程式能碰的資料範圍等於你授權給它的範圍（那一張試算表），而不是呼叫者的權限。所以程式裡<strong>只該做你願意讓匿名請求觸發的事</strong>——<code>doPost</code> 寫一列 log 是安全的，但如果程式裡寫了「刪除整張表」的邏輯，那也會用你的權限被匿名觸發。</p>
<p><strong>who has access（誰可以存取）決定誰能呼叫這個網址。</strong> 接匿名 beacon 必須選「所有人」（完全不需登入），這也表示網址一旦洩漏（而它必然公開，見下一段），任何人都能呼叫。這兩個設定合起來的效果是：<strong>一個任何人都能觸發、但只會執行你寫的那幾行、且以你的身分操作你授權的那張表的端點。</strong></p>
<h2 id="這個網址必然公開">這個網址必然公開</h2>
<p>beacon 端點的網址會被寫進每一頁的 client-side JavaScript，任何人檢視原始碼都看得到。它藏不住，這是 client beacon 架構的本質、不是設定失誤——GA、Cloudflare Web Analytics 那些的收集端點同樣是公開的。所以安全策略不能建立在「保密網址」上，而要建立在「限制這個公開端點能造成的破壞」上。</p>
<p>這也界定了什麼<strong>不是</strong>風險：別人拿到網址讀不到你的 Sheet，因為 <code>doPost</code> 只做 <code>appendRow</code> 然後回一個固定的 <code>{ok:true}</code>，不回傳任何試算表內容；他也碰不到你其他 Google 資料，因為授權範圍只綁那張表。端點是「只能寫、寫入內容固定、回應不洩漏」的，這個形狀本身就把資料外洩排除了。</p>
<h2 id="實際的風險範圍">實際的風險範圍</h2>
<p>公開端點的真實風險是騷擾型的，有兩種。<strong>髒資料</strong>：有人直接對網址 POST 任意內容，往你的 log 塞垃圾列、污染統計。<strong>配額消耗</strong>：有人狂打這個端點，吃掉你的執行配額，嚴重時排擠正常 beacon（配額的細節見<a href="/blog/automation/05-deploy-quota-security/quota-abuse-privacy/" data-link-title="配額、濫用防護、隱私與遷移訊號" data-link-desc="免費配額實際碰撞時會怎樣、擋髒資料與過濾自己瀏覽的做法、不記 PII 的隱私立場、以及量大到該離開 Sheets 的訊號">配額、濫用與隱私</a>）。</p>
<p>這兩種風險的共通點是它們<strong>不會洩漏或破壞你的資料，只會污染統計或耗資源</strong>。對沒沒無聞的個人 blog，攻擊者缺乏動機花力氣灌一個小站的瀏覽計數，實際發生機率低。所以務實的姿態是：先讓公開端點跑著、把「限制破壞範圍」的保護當成「遇到再加」的選項，而不是上線前就必備。具體的防護手段在下一篇。</p>
<h2 id="更新部署不換網址安全角度">更新部署不換網址（安全角度）</h2>
<p>日常維護會反覆改 <code>doPost</code>，改完要讓 <code>/exec</code> 反映新版本、而網址不變——用「管理部署作業 → 編輯 → 版本選新版本」，不要用「新增部署作業」（那會產生新網址）。這個操作除了避免假故障（見<a href="/blog/automation/01-apps-script-basics/web-app-deployment-model/" data-link-title="web app 部署模型與授權" data-link-desc="把 Apps Script 掛成可被 HTTP 呼叫的端點時，doGet/doPost 進入點、exec 與 dev 兩種網址、以及更新部署為什麼要用同一個網址">web app 部署模型</a>），也有安全意義：換網址意味著舊網址可能還殘留在快取的頁面裡繼續被打，管理起來多一個要追蹤的公開端點。用同一個部署更新，公開端點就始終只有一個、行為可控。</p>
<h2 id="下一步">下一步</h2>
<p>存取權限的安全框架清楚後，配額實際會怎麼碰撞、怎麼擋濫用、隱私怎麼守、以及量大到該遷移的訊號，見<a href="/blog/automation/05-deploy-quota-security/quota-abuse-privacy/" data-link-title="配額、濫用防護、隱私與遷移訊號" data-link-desc="免費配額實際碰撞時會怎樣、擋髒資料與過濾自己瀏覽的做法、不記 PII 的隱私立場、以及量大到該離開 Sheets 的訊號">配額、濫用與隱私</a>。</p>
]]></content:encoded></item><item><title>配額、濫用防護、隱私與遷移訊號</title><link>https://tarrragon.github.io/blog/automation/05-deploy-quota-security/quota-abuse-privacy/</link><pubDate>Mon, 06 Jul 2026 00:00:00 +0800</pubDate><guid>https://tarrragon.github.io/blog/automation/05-deploy-quota-security/quota-abuse-privacy/</guid><description>&lt;p>統計上線後長期要守住的是四件事：配額不被打爆、髒資料不污染統計、隱私邊界守住、以及在量成長到 Sheets 撐不住前認出訊號。這一篇把這四件事各給一個務實的判斷與做法，收在「什麼時候該換更重的工具」。&lt;/p>
&lt;h2 id="配額實際碰撞會怎樣">配額實際碰撞會怎樣&lt;/h2>
&lt;p>免費配額的三條線（單次 6 分鐘、同時併發 30、觸發器每日 90 分鐘，見&lt;a href="https://tarrragon.github.io/blog/automation/knowledge-cards/execution-quota/" data-link-title="Execution Quota（執行配額）" data-link-desc="Apps Script 個人帳號的執行時間、同時併發與觸發器每日總時間上限，決定免費膠水層能承受多大的量">執行配額&lt;/a>）在正常個人 blog 幾乎碰不到，但知道碰到時的症狀有助於診斷。&lt;/p>
&lt;p>&lt;strong>同時併發 30&lt;/strong> 是接收 beacon 最可能先碰的線：某篇文章瞬間爆紅、一秒內湧入超過 30 個瀏覽，第 31 個以後的 &lt;code>doPost&lt;/code> 會拿到錯誤、那幾筆瀏覽漏記。症狀是「爆量時段的統計數字明顯偏低」。&lt;strong>單次 6 分鐘&lt;/strong> 接 beacon 用不到（寫一列幾百毫秒），但彙總 trigger 全表掃描在資料很多時會逼近，症狀是彙總 trigger 開始逾時失敗。&lt;strong>每日 90 分鐘&lt;/strong> 是所有觸發器加總，正常一天彙總一次遠遠用不完，除非彙總寫得很沒效率或觸發器被重複註冊。&lt;/p>
&lt;p>碰到併發上限的處理不是「調高配額」（個人帳號調不了），而是「削峰」——beacon 本來就是可容忍少量遺失的統計，爆量漏記幾筆不影響「哪篇熱門」的判斷；真的很在意，訊號就指向遷移到吞吐更高的 Workers。&lt;/p>
&lt;h2 id="濫用防護與資料乾淨度">濫用防護與資料乾淨度&lt;/h2>
&lt;p>公開端點的騷擾型風險（髒資料、配額消耗，見&lt;a href="https://tarrragon.github.io/blog/automation/05-deploy-quota-security/deployment-and-access/" data-link-title="部署與存取權限的安全含義" data-link-desc="beacon 接收端設成匿名可存取時，execute as 與 who has access 各自的安全含義、以及這個公開端點實際能被拿來做什麼">部署與存取權限&lt;/a>），對應兩個層級的防護。&lt;/p>
&lt;p>&lt;strong>過濾自己的瀏覽&lt;/strong>是最先該做、報酬最高的一項——不是防外人，是防站主把開發與自我瀏覽混進統計。前端的 hostname guard 已經擋掉本機預覽（見&lt;a href="https://tarrragon.github.io/blog/automation/02-analytics-beacon/frontend-beacon/" data-link-title="前端 beacon 與 CORS 障礙" data-link-desc="靜態站用瀏覽器送瀏覽事件到 Apps Script 時，為什麼要用 sendBeacon 送 text/plain 才不會被 CORS preflight 擋下">前端 beacon&lt;/a>）；要進一步排除「在正式站上自己一直重整」，可以在瀏覽器存一個退出標記、beacon 讀到就不送，或彙總時依訪客識別碼過濾。兩者的取捨在於前者省下配額與列數、後者保留資料可回頭校正，而它們可以並用。退出標記的實作、邊界與各瀏覽器的差異見&lt;a href="https://tarrragon.github.io/blog/automation/06-reading-the-data/visitor-identity/" data-link-title="訪客識別與 opt-out" data-link-desc="流量記錄裡的來源網址大量空白、任兩列之間看不出是不是同一個人時，補上識別欄位並保留退出機制">訪客識別與 opt-out&lt;/a>——存標記的版本同時也是給讀者的退出入口，適合公開在網站說明裡。&lt;/p>
&lt;p>&lt;strong>擋隨手亂打&lt;/strong>用一個約定 token：前端 payload 放一個固定字串、&lt;code>doPost&lt;/code> 檢查對不上就丟掉不寫。這能擋掉不知情的爬蟲與隨手 POST，成本很低。但要誠實看待它的邊界：token 也在 client JS 裡看得到，鐵了心要灌的人抓一下原始碼就有——所以 token 是「擋雜訊」不是「擋攻擊」。對個人 blog，擋雜訊通常就夠了；真的被針對性灌爆，那是遷移到有更多防護手段（rate limiting、驗證）的平台的訊號。&lt;/p>
&lt;h2 id="隱私與-pii-邊界">隱私與 PII 邊界&lt;/h2>
&lt;p>這套統計在隱私上的立足點是&lt;strong>根本不收集個人身分資訊&lt;/strong>，這讓它天然乾淨。具體有三條線值得明確守住：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>不記 IP&lt;/strong>：Apps Script 的 web app 收不到訪客 IP，所以就算想記也記不到——這反而是好事，少一個 PII 來源。&lt;/li>
&lt;li>&lt;strong>裝置只送粗標籤&lt;/strong>：&lt;code>mobile&lt;/code> / &lt;code>tablet&lt;/code> / &lt;code>desktop&lt;/code> 三選一足以回答「用什麼裝置看」，不送完整 &lt;code>userAgent&lt;/code>（那帶版本等可組成&lt;a href="https://tarrragon.github.io/blog/automation/knowledge-cards/browser-fingerprint/" data-link-title="Browser Fingerprint（瀏覽器指紋）" data-link-desc="由多個單獨無害的瀏覽器環境屬性組合而成的裝置特徵集合，決定自建統計蒐集判別訊號時的能力上限">瀏覽器指紋&lt;/a>的細節）。&lt;/li>
&lt;li>&lt;strong>不放可識別個人的欄位&lt;/strong>：這一章講到的 payload 只有路徑、來源、語言、裝置，沒有任何綁到個人的東西。&lt;a href="https://tarrragon.github.io/blog/automation/06-reading-the-data/visitor-identity/" data-link-title="訪客識別與 opt-out" data-link-desc="流量記錄裡的來源網址大量空白、任兩列之間看不出是不是同一個人時，補上識別欄位並保留退出機制">模組六&lt;/a>會加上兩個隨機識別碼——它們同樣不含個人資訊，但確實會被寫進瀏覽器的儲存空間，下一段的同意機制討論就是為此。&lt;/li>
&lt;/ul>
&lt;p>不收 PII 讓「處理個資刪除請求」這類負擔不會發生，但&lt;strong>同意機制的判斷不是由 PII 決定的&lt;/strong>。歐盟 ePrivacy 指令第 5(3) 條管的是「在使用者終端設備儲存或讀取資訊」這個動作本身，與存的內容是否為個資無關——市面上宣稱免同意橫幅的分析服務（Plausible、Fathom）之所以能這樣宣稱，正是因為它們完全不在瀏覽器儲存任何識別碼。&lt;a href="https://tarrragon.github.io/blog/automation/06-reading-the-data/visitor-identity/" data-link-title="訪客識別與 opt-out" data-link-desc="流量記錄裡的來源網址大量空白、任兩列之間看不出是不是同一個人時，補上識別欄位並保留退出機制">模組六的訪客識別&lt;/a>在 &lt;code>localStorage&lt;/code> 存了兩個，因此那一步跨過了這條線。&lt;/p>
&lt;p>實務上個人網站的合規風險與商業追蹤網路不在同一個量級，但這是取捨不是豁免。想更保守，可以尊重瀏覽器的 Do Not Track 訊號（&lt;code>navigator.doNotTrack === &amp;quot;1&amp;quot;&lt;/code> 時不送 beacon），或者不存識別碼、只做頁面計數。&lt;/p>
&lt;h2 id="遷移訊號什麼時候該離開-sheets">遷移訊號：什麼時候該離開 Sheets&lt;/h2>
&lt;p>Apps Script + Sheets 是為「量小、要人直接看試算表」設計的（選型見&lt;a href="https://tarrragon.github.io/blog/automation/00-mental-model/free-tier-and-tool-choice/" data-link-title="免費額度的思考方式與工具選型" data-link-desc="判斷免費膠水層撐不撐得住自己流量時該看哪個限制、以及 Apps Script 與 Cloudflare Workers 各自適合什麼場景">模組零&lt;/a>）。以下訊號累積出現時，代表量已經長到該換更重的工具：&lt;/p>
&lt;ul>
&lt;li>爆量時段頻繁撞併發上限、漏記明顯到影響判斷。&lt;/li>
&lt;li>彙總 trigger 即使分表、只讀增量後仍逼近 6 分鐘。&lt;/li>
&lt;li>raw log 的 cell 數逼近試算表上限（見&lt;a href="https://tarrragon.github.io/blog/automation/03-sheet-as-database/data-model-and-capacity/" data-link-title="資料模型與容量邊界" data-link-desc="raw log 表的欄位怎麼設計才好彙總、以及 Sheets 累積到多少列會開始撐不住、撐不住的訊號長什麼樣">資料模型與容量邊界&lt;/a>）。&lt;/li>
&lt;li>需要的查詢已經超出試算表能力（多維交叉、即時儀表板）。&lt;/li>
&lt;/ul>
&lt;p>遷移的方向是把接收端換成 Cloudflare Workers、儲存換成 D1（免費 SQLite）：吞吐與查詢能力遠高於 Sheets，代價是失去「打開試算表就能看」的便利、要自己做查詢介面。判準用一句話收斂：&lt;strong>當「Sheets 即儀表板」的便利已經被它的容量與吞吐限制抵銷，就是遷移的時機&lt;/strong>——在那之前，簡單版一直是對的選擇。&lt;/p>
&lt;h2 id="管線完成之後">管線完成之後&lt;/h2>
&lt;p>到這裡，一套不租主機、資料存在自己試算表裡的流量統計已經建好，而且知道它的配額邊界、防濫用手段與遷移訊號在哪。&lt;/p>
&lt;p>&lt;strong>管線能跑，不代表資料讀得出意義。&lt;/strong> 收到的來源網址大量空白、任兩列之間看不出是不是同一個人、真人與機器抓取混在一起——這些問題只在真實流量打進來之後才浮現，處理方式見&lt;a href="https://tarrragon.github.io/blog/automation/06-reading-the-data/" data-link-title="模組六：收到資料之後" data-link-desc="流量統計上線、資料開始累積，但欄位讀不出「這是誰、是不是同一個人、是不是機器」時的判讀與補強">模組六：收到資料之後&lt;/a>。那一章會在 payload 上增加欄位，本章的配額判斷也會因為列數翻倍而需要重算。&lt;/p>
&lt;p>想把同一套膠水模式套到別的靜態站或別的工具，回&lt;a href="https://tarrragon.github.io/blog/automation/00-mental-model/free-tier-and-tool-choice/" data-link-title="免費額度的思考方式與工具選型" data-link-desc="判斷免費膠水層撐不撐得住自己流量時該看哪個限制、以及 Apps Script 與 Cloudflare Workers 各自適合什麼場景">模組零的選型&lt;/a>重新判斷即可。&lt;/p></description><content:encoded><![CDATA[<p>統計上線後長期要守住的是四件事：配額不被打爆、髒資料不污染統計、隱私邊界守住、以及在量成長到 Sheets 撐不住前認出訊號。這一篇把這四件事各給一個務實的判斷與做法，收在「什麼時候該換更重的工具」。</p>
<h2 id="配額實際碰撞會怎樣">配額實際碰撞會怎樣</h2>
<p>免費配額的三條線（單次 6 分鐘、同時併發 30、觸發器每日 90 分鐘，見<a href="/blog/automation/knowledge-cards/execution-quota/" data-link-title="Execution Quota（執行配額）" data-link-desc="Apps Script 個人帳號的執行時間、同時併發與觸發器每日總時間上限，決定免費膠水層能承受多大的量">執行配額</a>）在正常個人 blog 幾乎碰不到，但知道碰到時的症狀有助於診斷。</p>
<p><strong>同時併發 30</strong> 是接收 beacon 最可能先碰的線：某篇文章瞬間爆紅、一秒內湧入超過 30 個瀏覽，第 31 個以後的 <code>doPost</code> 會拿到錯誤、那幾筆瀏覽漏記。症狀是「爆量時段的統計數字明顯偏低」。<strong>單次 6 分鐘</strong> 接 beacon 用不到（寫一列幾百毫秒），但彙總 trigger 全表掃描在資料很多時會逼近，症狀是彙總 trigger 開始逾時失敗。<strong>每日 90 分鐘</strong> 是所有觸發器加總，正常一天彙總一次遠遠用不完，除非彙總寫得很沒效率或觸發器被重複註冊。</p>
<p>碰到併發上限的處理不是「調高配額」（個人帳號調不了），而是「削峰」——beacon 本來就是可容忍少量遺失的統計，爆量漏記幾筆不影響「哪篇熱門」的判斷；真的很在意，訊號就指向遷移到吞吐更高的 Workers。</p>
<h2 id="濫用防護與資料乾淨度">濫用防護與資料乾淨度</h2>
<p>公開端點的騷擾型風險（髒資料、配額消耗，見<a href="/blog/automation/05-deploy-quota-security/deployment-and-access/" data-link-title="部署與存取權限的安全含義" data-link-desc="beacon 接收端設成匿名可存取時，execute as 與 who has access 各自的安全含義、以及這個公開端點實際能被拿來做什麼">部署與存取權限</a>），對應兩個層級的防護。</p>
<p><strong>過濾自己的瀏覽</strong>是最先該做、報酬最高的一項——不是防外人，是防站主把開發與自我瀏覽混進統計。前端的 hostname guard 已經擋掉本機預覽（見<a href="/blog/automation/02-analytics-beacon/frontend-beacon/" data-link-title="前端 beacon 與 CORS 障礙" data-link-desc="靜態站用瀏覽器送瀏覽事件到 Apps Script 時，為什麼要用 sendBeacon 送 text/plain 才不會被 CORS preflight 擋下">前端 beacon</a>）；要進一步排除「在正式站上自己一直重整」，可以在瀏覽器存一個退出標記、beacon 讀到就不送，或彙總時依訪客識別碼過濾。兩者的取捨在於前者省下配額與列數、後者保留資料可回頭校正，而它們可以並用。退出標記的實作、邊界與各瀏覽器的差異見<a href="/blog/automation/06-reading-the-data/visitor-identity/" data-link-title="訪客識別與 opt-out" data-link-desc="流量記錄裡的來源網址大量空白、任兩列之間看不出是不是同一個人時，補上識別欄位並保留退出機制">訪客識別與 opt-out</a>——存標記的版本同時也是給讀者的退出入口，適合公開在網站說明裡。</p>
<p><strong>擋隨手亂打</strong>用一個約定 token：前端 payload 放一個固定字串、<code>doPost</code> 檢查對不上就丟掉不寫。這能擋掉不知情的爬蟲與隨手 POST，成本很低。但要誠實看待它的邊界：token 也在 client JS 裡看得到，鐵了心要灌的人抓一下原始碼就有——所以 token 是「擋雜訊」不是「擋攻擊」。對個人 blog，擋雜訊通常就夠了；真的被針對性灌爆，那是遷移到有更多防護手段（rate limiting、驗證）的平台的訊號。</p>
<h2 id="隱私與-pii-邊界">隱私與 PII 邊界</h2>
<p>這套統計在隱私上的立足點是<strong>根本不收集個人身分資訊</strong>，這讓它天然乾淨。具體有三條線值得明確守住：</p>
<ul>
<li><strong>不記 IP</strong>：Apps Script 的 web app 收不到訪客 IP，所以就算想記也記不到——這反而是好事，少一個 PII 來源。</li>
<li><strong>裝置只送粗標籤</strong>：<code>mobile</code> / <code>tablet</code> / <code>desktop</code> 三選一足以回答「用什麼裝置看」，不送完整 <code>userAgent</code>（那帶版本等可組成<a href="/blog/automation/knowledge-cards/browser-fingerprint/" data-link-title="Browser Fingerprint（瀏覽器指紋）" data-link-desc="由多個單獨無害的瀏覽器環境屬性組合而成的裝置特徵集合，決定自建統計蒐集判別訊號時的能力上限">瀏覽器指紋</a>的細節）。</li>
<li><strong>不放可識別個人的欄位</strong>：這一章講到的 payload 只有路徑、來源、語言、裝置，沒有任何綁到個人的東西。<a href="/blog/automation/06-reading-the-data/visitor-identity/" data-link-title="訪客識別與 opt-out" data-link-desc="流量記錄裡的來源網址大量空白、任兩列之間看不出是不是同一個人時，補上識別欄位並保留退出機制">模組六</a>會加上兩個隨機識別碼——它們同樣不含個人資訊，但確實會被寫進瀏覽器的儲存空間，下一段的同意機制討論就是為此。</li>
</ul>
<p>不收 PII 讓「處理個資刪除請求」這類負擔不會發生，但<strong>同意機制的判斷不是由 PII 決定的</strong>。歐盟 ePrivacy 指令第 5(3) 條管的是「在使用者終端設備儲存或讀取資訊」這個動作本身，與存的內容是否為個資無關——市面上宣稱免同意橫幅的分析服務（Plausible、Fathom）之所以能這樣宣稱，正是因為它們完全不在瀏覽器儲存任何識別碼。<a href="/blog/automation/06-reading-the-data/visitor-identity/" data-link-title="訪客識別與 opt-out" data-link-desc="流量記錄裡的來源網址大量空白、任兩列之間看不出是不是同一個人時，補上識別欄位並保留退出機制">模組六的訪客識別</a>在 <code>localStorage</code> 存了兩個，因此那一步跨過了這條線。</p>
<p>實務上個人網站的合規風險與商業追蹤網路不在同一個量級，但這是取捨不是豁免。想更保守，可以尊重瀏覽器的 Do Not Track 訊號（<code>navigator.doNotTrack === &quot;1&quot;</code> 時不送 beacon），或者不存識別碼、只做頁面計數。</p>
<h2 id="遷移訊號什麼時候該離開-sheets">遷移訊號：什麼時候該離開 Sheets</h2>
<p>Apps Script + Sheets 是為「量小、要人直接看試算表」設計的（選型見<a href="/blog/automation/00-mental-model/free-tier-and-tool-choice/" data-link-title="免費額度的思考方式與工具選型" data-link-desc="判斷免費膠水層撐不撐得住自己流量時該看哪個限制、以及 Apps Script 與 Cloudflare Workers 各自適合什麼場景">模組零</a>）。以下訊號累積出現時，代表量已經長到該換更重的工具：</p>
<ul>
<li>爆量時段頻繁撞併發上限、漏記明顯到影響判斷。</li>
<li>彙總 trigger 即使分表、只讀增量後仍逼近 6 分鐘。</li>
<li>raw log 的 cell 數逼近試算表上限（見<a href="/blog/automation/03-sheet-as-database/data-model-and-capacity/" data-link-title="資料模型與容量邊界" data-link-desc="raw log 表的欄位怎麼設計才好彙總、以及 Sheets 累積到多少列會開始撐不住、撐不住的訊號長什麼樣">資料模型與容量邊界</a>）。</li>
<li>需要的查詢已經超出試算表能力（多維交叉、即時儀表板）。</li>
</ul>
<p>遷移的方向是把接收端換成 Cloudflare Workers、儲存換成 D1（免費 SQLite）：吞吐與查詢能力遠高於 Sheets，代價是失去「打開試算表就能看」的便利、要自己做查詢介面。判準用一句話收斂：<strong>當「Sheets 即儀表板」的便利已經被它的容量與吞吐限制抵銷，就是遷移的時機</strong>——在那之前，簡單版一直是對的選擇。</p>
<h2 id="管線完成之後">管線完成之後</h2>
<p>到這裡，一套不租主機、資料存在自己試算表裡的流量統計已經建好，而且知道它的配額邊界、防濫用手段與遷移訊號在哪。</p>
<p><strong>管線能跑，不代表資料讀得出意義。</strong> 收到的來源網址大量空白、任兩列之間看不出是不是同一個人、真人與機器抓取混在一起——這些問題只在真實流量打進來之後才浮現，處理方式見<a href="/blog/automation/06-reading-the-data/" data-link-title="模組六：收到資料之後" data-link-desc="流量統計上線、資料開始累積，但欄位讀不出「這是誰、是不是同一個人、是不是機器」時的判讀與補強">模組六：收到資料之後</a>。那一章會在 payload 上增加欄位，本章的配額判斷也會因為列數翻倍而需要重算。</p>
<p>想把同一套膠水模式套到別的靜態站或別的工具，回<a href="/blog/automation/00-mental-model/free-tier-and-tool-choice/" data-link-title="免費額度的思考方式與工具選型" data-link-desc="判斷免費膠水層撐不撐得住自己流量時該看哪個限制、以及 Apps Script 與 Cloudflare Workers 各自適合什麼場景">模組零的選型</a>重新判斷即可。</p>
]]></content:encoded></item></channel></rss>