跳轉至

建立 2026-09-21 更新 2026-09-21

上線注意事項

用 GAS 當 LINE Bot 後台,個人或小團隊用起來很順,但它有幾個先天限制。這頁把會踩的坑集中講:無法驗證簽章、重複事件、時間限制、額度,以及什麼時候該換平台。

無法驗證 Webhook 簽章

LINE 官方要求:每個 Webhook 請求都帶 x-line-signature 標頭,內容是用 Channel secret 對原始請求內容做 HMAC-SHA256 再 Base64 的結果;機器人必須先比對簽章,不符或沒有簽章就不要處理。

問題是 GAS Web App 的 doPost(e) 讀不到請求標頭e 裡沒有 headers),所以簽章驗證在 GAS 裡做不到。後果是:任何知道你 /exec 網址的人,都能偽造一個像 LINE 事件的請求,觸發你的機器人邏輯、消耗你的額度。

實務上的減輕辦法,由簡到嚴:

做法 效果 限制
網址後面加一段長隨機字串,doPost 檢查 不知道網址的人打不進來 網址外流就失效;密碼在網址裡,容易出現在日誌
敏感操作只認白名單 userId 偽造事件也不能做管理動作 事件內容仍可被偽造
機器人回覆不洩漏私人資料 就算被偽造,攻擊者也拿不到東西 仍會消耗額度
前面放一層中繼服務驗簽,再轉給 GAS 真正符合官方要求 多一個要維護的服務

網址加密碼的寫法:把 Webhook URL 設成 .../exec?key=長隨機字串(設定後用 Verify 確認仍然通),再在 doPost 開頭比對:

function doPost(e) {
  const key = PropertiesService.getScriptProperties().getProperty("WEBHOOK_KEY");
  if (e.parameter.key !== key) return okResponse(); // 不處理,也不透露原因
  // ...原本的處理
}

function okResponse() {
  return ContentService.createTextOutput(JSON.stringify({ ok: true }))
    .setMimeType(ContentService.MimeType.JSON);
}

Web App 也無法自訂 HTTP 狀態碼,所以擋掉的請求對方看起來仍是成功,不是 401。

要做到真正驗簽,標準架構是:LINE → 你自己的中繼服務(例如 Cloud Run 或 Cloudflare Workers)→ 用 Channel secret 驗證 x-line-signature → 通過才轉發給 GAS,並帶上上面那種共用密碼。這是進階題,本站不展開;如果機器人會處理金錢、個資或商業用途,建議一開始就用這個架構,或直接不用 GAS。

重複事件與 302 回應

LINE 要求 Webhook 回 2xx。網路不穩或回應不是 2xx 時,LINE 可能重送同一個事件(需在主控台開啟 Webhook redelivery);重送的事件內容相同,webhookEventIdreplyToken 都不變,只有 deliveryContext.isRedelivery 變成 true

另外,GAS Web App 收到 POST 後,實際上會先回應一個 302 轉址,把結果放在另一個網址。有些 Webhook 發送方(例如金流)會把 3xx 當成失敗而重送。LINE 不在乎回應本文,許多社群教學直接用 GAS 接 LINE 也能運作,但這不是官方保證的行為,請以你自己的 Verify 與實測為準。

不論原因,機器人都必須能承受同一事件被送兩次。做法是用 webhookEventId 去重:

function doPost(e) {
  const body = JSON.parse(e.postData.contents);
  (body.events || []).forEach((event) => {
    if (isDuplicate(event.webhookEventId)) return;
    try {
      handleEvent(event);
    } catch (err) {
      console.error(err.stack); // 一個事件出錯,不影響同批其他事件
    }
  });
  return okResponse();
}

function isDuplicate(eventId) {
  if (!eventId) return false;
  const cache = CacheService.getScriptCache();
  if (cache.get(eventId)) return true;
  cache.put(eventId, "1", 21600); // 記 6 小時(快取的最長時間)
  return false;
}

這個做法在兩次請求「完全同時」到達時仍可能漏網;要更嚴格就在 isDuplicate 外面包 LockService。「記帳」這類會寫入資料的機器人,去重比回覆型的更重要,否則同一筆會被記兩次。

時間限制

限制 影響 對策
replyToken 收到後約 1 分鐘內要用 處理太久,回覆會失敗 快速的事同步做完再 reply
單次執行 6 分鐘 長工作被終止 reply「處理中」,慢的部分切段續跑,見 效能與執行限制
Web App 冷啟動 久沒人用的第一則訊息回得慢 可接受就好;不能接受就換平台
官方說時限可能變動 別依賴精確秒數 越早回覆越好

處理超過一分鐘的工作時,不能靠 reply,必須在做完後用 push,而 push 會消耗額度。

額度

兩邊各有一套:

LINE 端(台灣地區官方帳號)

訊息類型 是否計費
Reply(回覆) 免費,不計入則數
Push、Multicast、Broadcast、Narrowcast 計入每月則數

台灣目前的方案:輕用量月費 0 元、每月 200 則免費;中用量 800 元、3,000 則;高用量 1,200 元、6,000 則(皆未稅)。輕用量與中用量用完就不能再發,只有高用量可以加購。LINE 已公告方案將於 2026 年 11 月 1 日調整,請以 LINE Biz-Solutions 最新資訊為準。

GAS 端(一般 Google 帳號)

  • 每次 replypush 都是一次 UrlFetchApp,每天上限 20,000 次(Workspace 100,000 次)。一天回覆超過兩萬則就會撞牆。
  • 同時執行數每位使用者 30 個。因為 Web App 通常是「以我執行」,所有人的訊息都算在你一個人頭上。訊息瞬間大量湧入時,超過上限的請求可能失敗。
  • 全部用量都吃你的帳號配額,如果同一帳號還跑著其他腳本,會互相排擠,見 效能與執行限制

金鑰與部署管理

  • Channel access token 只放 Script Properties。不小心貼到公開的地方,立刻到 LINE Developers Console 重新發行,舊的即失效。
  • 官方建議使用有到期時間的 token(v2.1)。長期不換的 token 一旦外流,風險較高。
  • 每個專案的版本數上限是 200(見配額表),定期到 管理部署作業 清掉不用的舊版本。
  • clasp 把程式放進 Git,改動才有紀錄可回溯。不要把 token 提交進 Git

什麼時候該換平台

情況 建議
需要真正驗證簽章 中繼服務驗簽,或整個搬到 Cloud Run 等
訊息量大(每天上萬則)或要低延遲 換有常駐服務的平台
資料要交易、關聯查詢 換資料庫,不再用試算表
需要記錄使用者對話、複雜狀態機 用官方 SDK(Node.js、Python 等)寫成一般後端

GAS 的定位是「最快做出可用的原型」與「小規模自用」。用 GAS 驗證想法,需求長大了再搬,邏輯(指令路由、資料處理)多半可以直接帶過去。

上線檢查清單

  • [ ] 加好友歡迎訊息與自動回應訊息已關閉
  • [ ] Token 在 Script Properties,程式碼裡沒有任何金鑰
  • [ ] 已決定簽章驗證的處理方式(至少有網址密碼)
  • [ ] webhookEventId 去重已加上
  • [ ] 每個事件外層有 try/catch,錯誤有記錄
  • [ ] 有 default 分支,看不懂的輸入會回提示
  • [ ] 算過 Push 用量是否在免費額度內
  • [ ] 最新版本已部署,用真的手機測過一遍