← 逆流而上
Wudelay

n8n webhook 教學:用網址觸發你的自動化流程

Wudelay AI 2026-09-1226 分鐘的航程

n8n webhook 教學:用網址觸發你的自動化流程

n8n webhook 是 n8n 裡最常見的觸發方式之一:Webhook 節點會產生一組網址,外部服務(表單系統、LINE、Stripe、其他網站的後端)只要對這組網址發送 HTTP 請求,就能啟動對應的工作流程。每個 Webhook 節點其實會同時產生「測試網址」與「正式網址」兩組不同的 URL,兩者的觸發條件、有效時間、資料顯示方式都不一樣,這也是新手設定 webhook 時最容易卡關、以為「webhook 壞了」的地方。

但真正讓一個 webhook 從「能跑」變成「能上線」的,通常不是這兩組網址,而是後面那些一開始不會想到的事:節點內建的 Header Auth 擋不住偽造請求、金流平台會把同一筆事件重送好幾次、n8n Cloud 的 webhook 超過 100 秒沒回應就會被切斷。這些狀況在測試階段幾乎不會出現,要等正式流量進來之後才會浮上來。

本文會依序整理:Webhook 節點的基本參數、測試網址與正式網址的實際差異、一套從 0 到能被外部觸發的建置步驟、四種回應模式與非同步架構、HMAC 簽章驗證的實作方式、重送與冪等性的處理、Wait 節點的 resume webhook、常見錯誤排查、自架環境的網址設定,以及高流量情境下的 queue mode 與並行控制。前半段不需要 n8n 基礎也能照著操作,想先補概念可參考〈深入了解 N8N:工作流節點與部署選擇〉。

n8n webhook 節點的基本設定

在 n8n 的工作流程畫布中新增 Webhook 節點後,節點本身只是一個入口,實際行為由幾組參數決定。

HTTP Method 與 Path

Webhook 節點預設只接受單一 HTTP 方法(例如 GET 或 POST);如果同一個網址要同時接受多種方法,需要在節點設定中開啟「Allow Multiple HTTP Methods」。n8n 支援的方法包括 DELETE、GET、HEAD、PATCH、POST、PUT。

Path(路徑)預設是系統隨機產生的一串字串,也可以手動改成好記的路徑,例如 /order-created,甚至可以加入路由參數,例如 /orders/:orderId,讓 :orderId 的值可以在後續節點以表達式取用。要注意的是:同一個路徑與 HTTP 方法的組合,同一時間只能有一個已發佈(Active)的 webhook 在使用,否則會出現路徑衝突的錯誤。

單次請求的資料量上限是 16MB,若需要傳更大的檔案,可透過環境變數 N8N_PAYLOAD_SIZE_MAX 調高。

四種驗證方式:Basic、Header、JWT、None

Webhook 節點內建四種 Authentication 選項:

  • None:不驗證,任何人拿到網址都能觸發,僅適合內部測試,或本來就有其他防護(例如已設定 IP 白名單)的情境。
  • Basic Auth:呼叫端要帶帳號密碼,設定最簡單,多數第三方服務的 webhook 設定介面都支援。
  • Header Auth:驗證某個自訂 HTTP 標頭(例如一組固定 token),常見於串接的第三方系統只能設定 Header 的情境。
  • JWT Auth:驗證 JSON Web Token,適合呼叫端本身就有簽發 JWT 能力的系統。

正式環境對外公開的 webhook,建議至少開啟 Header Auth 或 Basic Auth 其中一種,避免網址外流後被任意呼叫。不過要先講清楚一件事:這四種驗證方式的前提,都是「呼叫端願意照你的規則帶東西」。像 Stripe、GitHub、LINE 這類平台不會讓你自訂認證標頭,它們用的是另一套機制——HMAC 簽章,這部分後面有獨立一段說明。

n8n webhook 測試網址與正式網址:差在哪裡?

這是多數人第一次用 n8n webhook 卡關的地方:畫布上明明有兩組網址,複製錯一組,工作流程就是「觸發不了」。根據 n8n 官方文件,兩者差異整理如下。

項目 測試網址(Test URL) 正式網址(Production URL)
網址格式 路徑包含 /webhook-test/ 路徑為 /webhook/
啟動方式 點擊節點上的「Listen for test event」後才會註冊 把整個工作流程切成「Active」發佈後自動註冊
有效時間 註冊後僅保持監聽 120 秒 只要流程保持 Active,就持續監聽
資料可見度 收到的資料會即時顯示在編輯器畫布上,方便除錯 編輯器不會顯示資料,要看結果需到「Executions」頁籤查執行紀錄
適用階段 開發、除錯、確認欄位結構 正式提供給外部服務呼叫

換句話說:開發階段用測試網址,一邊送測試請求一邊在畫布上確認資料長相;等流程邏輯都對了,把工作流程切成 Active 狀態發佈,再把外部服務(例如串接的表單、金流、LINE Bot 後台)設定的網址換成正式網址,兩者只差在路徑中的 -test。常見的失誤是流程都測好了,卻忘記把外部服務裡登記的網址從測試網址換成正式網址,導致正式環境完全沒有觸發紀錄。

反過來的失誤同樣常見,而且更難查:簽章驗證的流程在測試網址上怎麼測都不過,原因是忘了先點「Listen for test event」,請求根本沒進到流程,看到的錯誤其實是 404 而不是驗證失敗。遇到「webhook 沒反應」時,第一件事永遠是先確認現在用的是哪一組網址、那組網址當下有沒有在監聽。

手把手教學:建立一個能被外部觸發的 webhook 流程

以下以「接收一筆訂單資料,並回傳處理結果」為例,示範完整流程。

  1. 新增 Webhook 節點:在畫布上新增 Webhook 節點作為觸發點,HTTP Method 選 POST,Path 改成好記的字串,例如 order-created
  2. 點擊「Listen for test event」:節點會顯示測試網址,並開始監聽,這個狀態會維持 120 秒。
  3. 從外部發送一次測試請求:可以用 Postman、curl 或任何 HTTP 工具,對測試網址送出一筆 POST 請求並帶上 JSON 內容,例如訂單編號與金額欄位。
  4. 確認資料結構:請求送出後,畫布上的 Webhook 節點會顯示收到的內容,可確認欄位名稱、巢狀結構是否符合預期,方便後續節點取值。
  5. 接上後續處理節點:依需求接上寫入 Google Sheets、發送通知、呼叫其他 API 等節點,組成完整邏輯。
  6. 決定回應方式(Respond):Webhook 節點的 Respond 選項有四種,下一段會詳細比較。需要讓呼叫端拿到實際處理結果的情境,通常會選「When Last Node Finishes」或「Using ‘Respond to Webhook’ Node」。
  7. 發佈工作流程(Active):邏輯與回應都確認沒問題後,把工作流程切換成 Active,n8n 會正式註冊正式網址。
  8. 把正式網址交給外部服務:回到串接的第三方系統後台,把原本測試用的網址換成正式網址(把路徑裡的 -test 拿掉),儲存設定。
  9. 用 Executions 頁籤驗證:正式網址上線後,資料不會再顯示在畫布上,需要到 Executions 頁籤查看每一次執行的紀錄與結果,確認是否成功。

這九步跑完,webhook 就能被外部觸發了。接下來的章節處理的是另一層問題:流程跑得久會怎樣、請求是偽造的會怎樣、同一筆事件送兩次會怎樣。

四種回應模式與 100 秒天花板

Webhook 節點的 Respond 設定決定「呼叫端什麼時候、拿到什麼」,這個選擇同時也決定了整條流程能跑多久。

Respond 設定 呼叫端收到什麼 回應時機 適用情境
Immediately 固定訊息「Workflow got started」 收到請求的當下 呼叫端只需要知道「有收到」,不需要處理結果
When Last Node Finishes 最後一個節點的輸出資料 整條流程跑完 流程短、呼叫端需要最終結果
Using ‘Respond to Webhook’ Node 由 Respond to Webhook 節點決定 流程執行到該節點時 要自訂狀態碼與標頭,或想在流程中段就先回應
Streaming response 逐段串流回傳的內容 持續回傳直到結束 搭配支援串流的節點,例如 AI 逐字輸出

前兩種最單純,問題出在第二種:只要選了「When Last Node Finishes」,呼叫端就得等整條流程跑完才會收到回應。流程裡只要有一個節點慢(呼叫外部 API、處理大量資料、跑 AI 模型),呼叫端那邊就會逾時。n8n Cloud 的上限是 100 秒,超過就會回 524;就算是自架,多數第三方平台自己也有 10 到 30 秒的逾時設定,真正能用的時間比想像中短很多。

非同步模式:先回應,再處理

解法是把「回應」跟「處理」拆開。官方文件說明 Respond to Webhook 節點可以放在工作流程的任何位置,只要需要回傳其他節點的資料時才放在那些節點之後。文件另外提到,如果流程裡有第二個 Respond to Webhook 節點,它會在第一個之後執行但被忽略——這也反過來說明,回應送出以後,後面的節點仍然會繼續執行。

依照這個機制,一條耐得住慢處理的流程長這樣:

  1. Webhook 節點的 Respond 選「Using ‘Respond to Webhook’ Node」。
  2. 緊接著放一個 Respond to Webhook 節點,Respond With 選「No Data」或一個簡短的 JSON,Response Code 設 200。
  3. 回應節點之後,才接真正的處理邏輯——寫入資料庫、呼叫 API、通知使用者。

這樣呼叫端在幾百毫秒內就拿到 200,不會因為後面的處理跑了三分鐘而逾時。Stripe 官方文件對開發者的建議也是同一個原則:必須在任何可能造成逾時的複雜邏輯之前,就先回傳 2xx 狀態碼。

如果呼叫端真的需要拿到處理結果,而處理又快不了,官方在常見問題中建議的做法是拆成兩個 webhook:第一個負責接收請求、立刻回應並啟動處理;第二個提供給呼叫端輪詢查詢處理狀態。

Respond to Webhook 節點的細節

如果 Webhook 節點的 Respond 設定選的是「Using ‘Respond to Webhook’ Node」,工作流程裡就必須另外放一個 Respond to Webhook 節點,否則呼叫端會收不到回應而逾時。

這個節點的「Respond With」可以選擇 All Incoming Items(全部項目)、First Incoming Item(第一筆)、JSON、Text、JWT Token、Binary File、Redirect(轉址)或 No Data(不回傳內容);Options 裡則能自訂 Response Code 與 Response Headers。

有一個容易踩的限制要記住:官方文件明確寫著,Respond to Webhook 節點只會執行一次,而且使用第一筆輸入資料項目。即使搭配表達式或 Loop 節點,回應內容也只會包含第一次執行的結果。如果前面的節點輸出多筆資料而且全部都要回傳,需要先用其他節點把多筆資料整併成單一物件,或改用 All Incoming Items。

什麼時候不需要這個節點?如果只是要讓外部服務知道「有收到、有觸發」,Respond 選「Immediately」就夠用,不必特地接 Respond to Webhook 節點。

Header Auth 擋不住偽造請求:HMAC 簽章驗證

這是內建的四種 Authentication 涵蓋不到的一塊,也是 webhook 上線前最值得補的一道防線。

問題在於:Stripe、GitHub、LINE、Shopify 這些平台送 webhook 給你的時候,不會讓你在它們的後台自訂一組帳密或自訂標頭。它們的做法是用一把只有雙方知道的 secret,對整包請求內容算出一組 HMAC 簽章,放在特定的 HTTP 標頭裡送過來。接收端要用同一把 secret 重算一次,兩邊對得上才代表這個請求真的來自該平台、而且內容在傳輸過程中沒有被改過。

如果跳過這一步,只要有人知道你的 webhook 網址,就能自己組一包「付款成功」的 JSON 打進來。Stripe 文件把這個風險寫得很直接:沒有驗證的話,攻擊者可以送假的 webhook 事件到你的端點,觸發出貨、開通帳號權限或竄改紀錄這類動作。

主流平台的簽章規格對照

各平台的機制原理相同,細節卻都不一樣——標頭名稱、摘要編碼、簽的內容三者只要錯一個,驗證就不會過。

平台 標頭名稱 演算法 摘要編碼 被簽的內容
GitHub X-Hub-Signature-256 HMAC-SHA256 十六進位,前面帶 sha256= 前綴 原始請求內容
Stripe Stripe-Signature HMAC-SHA256 十六進位,包在 t=…,v1=… 結構裡 時間戳記 + 半形句點 + 原始請求內容
LINE x-line-signature HMAC-SHA256 Base64 原始請求內容(key 為 Channel secret)

Stripe 的結構要特別看一下。它的標頭長得像 t=1492774577,v1=5257a869…,其中 t 是時間戳記、v1 是簽章;驗證時要先把標頭用逗號拆開、再用等號拆成前後兩段取值,然後把「時間戳記 + 一個半形句點 + 原始內容」串成 signed_payload 字串,拿它去算 HMAC。官方文件另外提醒:為了避免降級攻擊,要忽略所有不是 v1 的 scheme。

為什麼一定要用 Raw Body

這是實作時最常翻車的地方:HMAC 是對「位元組」做運算,不是對「解析後的物件」。請求內容只要被解析成 JSON 再重新序列化,空白字元、鍵值順序、編碼都可能跟原本送來的不一樣,算出來的簽章就對不上。

三家平台的文件都特別警告過這件事。Stripe 寫的是:Stripe 需要請求的原始內容才能進行簽章驗證,如果使用框架,要確認它沒有動到 raw body,任何對 raw body 的修改都會造成驗證失敗。LINE 的說法更嚴格:不要修改收到的 x-line-signature 標頭或請求內容字串,要原封不動地保存,任何解析、格式化、跳脫字元的解讀或非 UTF-8 編碼都會讓驗證失敗。GitHub 則提醒要確保以 UTF-8 處理內容,因為 webhook 內容可能包含 Unicode 字元。

在 n8n 裡,對應的設定就是 Webhook 節點 Options 中的 Raw Body——官方對它的描述是「指定 Webhook 節點以原始格式接收資料,例如 JSON 或 XML」。開啟之後,原始內容不會進到 $json,而是以 Base64 編碼放在二進位資料裡($binary.data.data),需要先解碼回 UTF-8 字串,才是真正要拿去算 HMAC 的那串內容。

在 n8n 裡組出驗證流程

n8n 內建的 Crypto 節點就能算 HMAC,不需要寫外部服務。它的 Hmac 動作支援 MD5、SHA256、SHA384、SHA512、SHA3-256、SHA3-384、SHA3-512,Encoding 可以選 BASE64 或 HEX,secret 則來自 Crypto 節點的憑證設定。組起來的流程如下:

  1. Webhook 節點開啟 Raw Body,並確認節點有把需要的標頭一起帶進來。
  2. 把 Base64 解碼成 UTF-8 字串:用 Code 節點或 Extract from File 這類節點,把 $binary.data.data 還原成原始內容字串。Stripe 的情境要再往前一步,把時間戳記與句點接在前面組成 signed_payload。
  3. Crypto 節點算 HMAC:Type 選 SHA256,Encoding 依平台選 HEX(GitHub、Stripe)或 BASE64(LINE、Shopify),Value 填上一步得到的字串。
  4. 比對:用 IF 節點比較算出來的值與標頭裡的值。GitHub 的標頭帶 sha256= 前綴,比對前要先去掉;Stripe 則要先從 t=…,v1=… 結構中取出 v1 的值。
  5. 對不上就中止:走 IF 的 false 分支,接 Respond to Webhook 回 401 或 403,並用 Stop and Error 結束流程,不要讓後續邏輯有機會執行。

摘要編碼選錯是最常見的失敗原因之一:同樣是 HMAC-SHA256,GitHub 與 Stripe 要十六進位,LINE 與 Shopify 要 Base64,兩邊算出來的值長得完全不同,比對永遠不會過。

時間戳記與比較方式

簽章驗證過了,還有一個重放攻擊(replay attack)的問題:攻擊者攔截到一包合法的內容與它的簽章,之後原封不動地重送一次,簽章依然是對的。

Stripe 的做法是把時間戳記一起簽進去——因為時間戳記是 signed_payload 的一部分,攻擊者改了它簽章就會失效。接收端可以計算目前時間與收到的時間戳記差多少,超過容許範圍就拒絕。官方函式庫的預設容許值是 5 分鐘,文件同時提醒不要把容許值設成 0,那等於完全關掉這項檢查。做這個檢查的前提是伺服器時間要準,官方建議用 NTP 校時。

另外一個細節是比較方式。GitHub 文件明確建議不要用一般的相等運算子比對簽章,而要用常數時間比較(例如 crypto.timingSafeEqual),避免透過回應時間的差異推測出正確的簽章。在 n8n 裡如果是用 Code 節點做最後比對,這一步可以照做;用 IF 節點比對則沒有這個選項,屬於實務上的取捨。

重送與重複觸發:webhook 的冪等性

這是另一個測試階段看不到、上線後才會出事的問題:同一筆事件,你的 webhook 可能會收到不只一次。

以 Stripe 為例,官方文件說明它在正式環境會以指數退避的方式重試投遞,最長持續三天;沙盒環境則是在幾小時內重試三次。除此之外還能手動重送——在 Dashboard 上事件建立後 15 天內可以按 Resend,用 CLI 則是 30 天內。文件在最佳實務中直接寫著:webhook 端點偶爾會收到同一個事件超過一次。

重送的觸發條件通常是「沒有在時間內收到 2xx」。但實務上麻煩的情境是:你的流程其實已經處理完了,只是回應慢了一步或中間斷線,對方沒收到 200,於是重送——結果就是同一筆訂單被寫進資料庫兩次、同一封通知寄了兩遍。

處理方式是讓流程具備冪等性(idempotency),也就是同一筆事件跑幾次,結果都一樣。常見做法:

  • 記錄已處理的事件 ID:Stripe 文件建議把處理過的事件 ID 記下來,已經記錄過的就不再處理。實作上可以在 n8n 流程開頭先查一次資料庫或 Google Sheets,查到就直接回 200 結束。
  • 用業務層的唯一鍵取代事件 ID:如果來源不提供事件 ID,可以用訂單編號、交易序號這類本來就唯一的欄位當判斷依據。Stripe 文件也提到,某些情況下會產生兩個不同的 Event 物件,這時要改用 data.object 的 ID 搭配 event.type 來辨識重複。
  • 寫入時用 upsert 而不是 insert:讓重複寫入變成覆蓋同一筆,而不是新增第二筆,從資料層把重複吃掉。

還有一個容易被忽略的前提:事件順序不保證。Stripe 文件明確說明不保證事件會依照產生的順序送達,並提醒不要用 created 時間戳記來判斷順序或是否已處理過,因為不同事件可能共用同一個時間戳記。如果流程邏輯依賴「先收到 A 才會收到 B」,那它遲早會在正式環境出錯。

進階設定:CORS、IP 白名單與條件過濾

Webhook 節點的 Options 裡還有幾個常用的進階設定。

選項 用途
Allowed Origins (CORS) 設定允許的跨來源網域,用逗號分隔多個網址;控制哪些網域可以用瀏覽器端 JavaScript 直接呼叫這個 webhook
IP(s) Allowlist 限制誰可以觸發這組 webhook 網址,填入以逗號分隔的清單,不在名單內的請求會被擋下
Only Run If 對收到的請求執行一段表達式,只有條件成立時工作流程才會執行
Raw Body 以原始格式接收資料(JSON 或 XML 原文),簽章驗證必用
Binary Property 讓 Webhook 節點能接收二進位資料,例如圖片或音訊檔
Ignore Bots 忽略連結預覽器、網頁爬蟲這類機器人發出的請求
No Response Body 回應時不帶內容
Response Headers 在 webhook 回應中夾帶額外的標頭

要留意的是,IP 白名單是在 n8n 收到請求之後才做驗證與過濾(認證檢查同理),如果 n8n 是架在反向代理後面,代理伺服器看到的來源 IP 會是代理本身,而不是真正的呼叫端,這時候需要另外設定代理跳數,細節放在後面自架環境的段落。

IP 白名單搭配簽章驗證是很值得做的組合。Stripe 文件建議兩層防護一起用:它的 webhook 事件來自一份固定的 IP 清單,可以設定伺服器或防火牆只接受這些位址的請求,再加上簽章驗證確認內容沒被竄改。

Wait 節點的 resume webhook:讓流程中途等一個回呼

前面談的都是「webhook 當觸發點」。還有一種情境是流程已經在跑,中間需要停下來等某個外部動作完成——等主管在信裡按下核准、等第三方系統處理完之後回呼通知、等使用者點開連結確認。

這時用的不是 Webhook 節點,而是 Wait 節點的「On Webhook Call」恢復模式。它會在執行當下產生一組專屬的網址,用 $execution.resumeUrl 這個變數取得,官方文件的說法是:可以把這組還沒被產生出來的網址送到任何需要的地方,例如第三方服務或一封 email。外部只要對這組網址發一次請求,暫停中的流程就會從 Wait 節點往下繼續跑。

Wait 節點在這個模式下支援的參數跟 Webhook 節點很接近:Authentication 一樣有 Basic Auth、Header Auth、JWT Auth 與 None;可以指定接受的 HTTP Method 與回應狀態碼;回應時機同樣有 Immediately、When Last Node Finishes 或交給 Respond to Webhook 節點;另外還有二進位資料處理、忽略機器人、IP 白名單、自訂回應標頭等選項。

「Limit Wait Time」是實務上一定要設的一個參數:它讓流程在等待超過某個時間長度或到達某個指定時間點之後自動恢復。沒設的話,只要對方永遠不回呼,這個執行就會一直掛在那裡。

有一個陷阱值得記下來:官方文件提醒,部分執行(partial execution)會改變 resume 網址,所以負責把網址送出去的那個節點,必須跟 Wait 節點在同一次執行裡執行。在編輯器裡單獨重跑某幾個節點來除錯時,很容易踩到這一點——送出去的是上一次執行的舊網址,回呼自然叫不醒現在這個流程。

常見錯誤與排查

狀況 可能原因 排查方向
呼叫測試網址完全沒反應 沒有先點「Listen for test event」,或超過 120 秒監聽時間已經過期 重新點擊「Listen for test event」,在時限內立刻送出請求
正式網址呼叫沒有觸發 工作流程還沒切成 Active,或外部服務登記的仍是測試網址 確認流程狀態為 Active,並確認第三方後台填的是正式網址(不含 -test)
同一路徑註冊失敗,出現路徑衝突訊息 n8n 對每一組路徑與 HTTP 方法的組合只允許註冊一個 webhook 取消發佈衝突的舊工作流程,或改用不同的 Path
只有 GET 或只有 POST 能用,其他方法回 404 Webhook 節點預設只接受單一 HTTP 方法 開啟節點設定裡的「Allow Multiple HTTP Methods」
n8n Cloud 上收到 524 逾時 webhook 沒有在 100 秒內回應 改用非同步模式先回 200;或拆成兩個 webhook,一個啟動處理、一個讓呼叫端輪詢狀態
設定了 IP 白名單卻擋到合法 IP n8n 架在反向代理後面,收到的來源 IP 是代理位址 設定環境變數 N8N_PROXY_HOPS 為代理的層數,讓 n8n 正確讀取原始來源 IP
簽章驗證永遠不通過 用了解析後的 JSON 而不是原始內容;或摘要編碼選錯(hex 與 Base64 混用) 開啟 Raw Body,把 $binary.data.data 從 Base64 解碼成 UTF-8 字串再算 HMAC;確認平台要的是 HEX 還是 BASE64
回應內容只有第一筆資料 Respond to Webhook 節點只執行一次,且只使用第一筆輸入項目 改用 All Incoming Items,或先把多筆資料整併成單一物件再回傳
同一筆資料被處理兩次 回應太慢導致來源判定失敗而重送 先回 2xx 再處理;並以事件 ID 或業務唯一鍵做去重
回應字串被包成 JSON 或陣列 預設的回應格式是 JSON 或陣列 Respond 選「When Last Node Finishes」,Response Data 選 First Entry JSON 並設定 Property Name,再用 Edit Fields 節點輸出字串

自架 n8n 的 webhook 網址設定

如果 n8n 是自架在本機(localhost)測試,webhook 的網址預設是內部位址,外部服務打不進來,需要用具備公開網址的通道工具才能讓網址對外可用;官方文件提醒本機環境需要以隧道(tunnel)模式運行 n8n,才能正常接收外部 webhook。

正式上線、架在自己的伺服器並搭配反向代理(Nginx、Caddy 等)時,常見情境是:n8n 內部跑在 5678 埠,但對外是走 443 埠的網域,這時單靠 N8N_PROTOCOL、N8N_HOST、N8N_PORT 這組基本變數組不出正確的對外網址,需要額外設定:

  • N8N_WEBHOOK_URL:直接指定完整的對外 webhook 網址,例如 https://n8n.example.com/。舊版使用的 WEBHOOK_URL 已被官方文件標示為棄用(deprecated),建議直接改用 N8N_WEBHOOK_URL。
  • N8N_PROXY_HOPS:設為代理的層數(常見值是 1),讓 n8n 能正確解析 X-Forwarded-For、X-Forwarded-Host、X-Forwarded-Proto 這幾個標頭,取得真正的來源 IP 與網域,IP 白名單功能才會準確。

另外要留意,測試網址與正式網址預設可能不是吃同一組網址設定:根據 n8n 社群論壇上的討論,測試網址預設會跟著編輯器本身的網址設定走,正式網址則吃 webhook 專用設定;如果只設定了其中一組變數,兩個網址顯示的網域可能會對不上。統一只用 N8N_PROTOCOL、N8N_HOST、N8N_PORT 這組基本變數,或兩邊都改用 N8N_WEBHOOK_URL,就能讓兩組網址回到同一個網域。想看更多 n8n 從安裝到實際部署的完整記錄,可參考〈n8n 自動化部署實作歷程〉。

高流量下的 webhook:queue mode 與並行控制

單一 n8n 實例處理零星的 webhook 沒有問題,但當同一個端點開始承受大量並行請求時,問題會從「流程對不對」變成「機器扛不扛得住」。n8n 對這個情境提供兩個方向的設定。

queue mode 與 webhook processors

在 queue mode 下,正式環境的工作流程執行會交給 worker 程序處理。對 webhook 來說,HTTP 請求先由主程序或 webhook 程序接收,實際執行再交給 worker——這個交接會帶來一些額外的延遲。

要再往上擴展,可以另外啟動 webhook processor。它是選用的擴展層,專門處理進來的 webhook 請求,讓 n8n 能處理大量並行請求;webhook 程序預設監聽的埠號與主程序相同(5678),前面再放一個負載平衡器分流。啟動方式是用 CLI 執行 n8n webhook,或用 Docker 跑同樣的指令並帶上 EXECUTIONS_MODE=queue

負載平衡器的路由規則有講究:/webhook/*/webhook-waiting/* 這兩條路徑導向 webhook 伺服器,其餘路徑(API、UI 資源、以及測試用的 /webhook-test/*)導向主程序。注意 /webhook-waiting/* 正是前面 Wait 節點 resume 網址會用到的路徑,漏掉這條的話,暫停中的流程就叫不醒。

官方明確建議不要把主程序加進負載平衡器的 webhook 分流池,因為主程序一旦承受 webhook 流量,編輯、瀏覽、操作 n8n 介面的效能都會跟著下降。搭配的環境變數是 N8N_DISABLE_PRODUCTION_MAIN_PROCESS,設為 true 就能讓所有正式環境的 webhook 執行都落在 webhook processor 上。

並行控制

另一個方向是限制同時執行的數量,用的是 N8N_CONCURRENCY_PRODUCTION_LIMIT。幾個重點:

  • 預設是停用的,也就是不限制並行數。
  • 只計入正式環境的執行,也就是由 webhook 或 trigger 節點啟動的那些;手動執行、子工作流程執行、錯誤處理執行與 CLI 啟動的執行都不算在內。
  • 達到上限時,後續的執行不會被拒絕,而是進入佇列排隊,等有容量釋出後依照 FIFO 順序處理。排隊中的執行不能重試,取消或刪除會把它移出佇列。
  • 在 queue mode 下,只要這個變數設成 -1 以外的值,它的優先級高於 –concurrency 旗標。

這個設定對 webhook 情境特別實用:外部平台重送或突然湧入大量事件時,並行上限讓 n8n 排隊消化而不是一次全開把記憶體吃爆。但也要記得它跟前面的 100 秒天花板是互相拉扯的——排隊等待的時間也算在呼叫端的逾時裡,所以非同步模式(先回 200 再處理)在有並行上限的環境下更重要。

安全性檢查清單

webhook 網址一旦外流,任何人都可以直接呼叫,正式上線前建議檢查以下幾點。

  • 呼叫端如果是自己的系統:至少開啟一種 Authentication(Header Auth 或 Basic Auth 是最常見的選擇),不要用 None 直接對外。
  • 呼叫端如果是 Stripe、GitHub、LINE 這類平台:一定要做 HMAC 簽章驗證,內建的 Authentication 在這個情境幫不上忙。驗證不過就回 401 並中止流程。
  • 有時間戳記可用時(例如 Stripe),加上時效檢查擋重放攻擊,容許值設成合理的分鐘數,不要設 0。
  • 如果呼叫端來源 IP 固定,加上 IP(s) Allowlist 多一層防護;架在反向代理後面記得先設定 N8N_PROXY_HOPS,否則白名單可能誤判。
  • 需要接受瀏覽器端直接呼叫時,Allowed Origins (CORS) 只填實際會用到的網域,避免開放為 *。
  • 如果 webhook 的回應內容會被當成網頁顯示,n8n 從 1.103.0 版起會把 HTML 格式的回應內容包在 iframe 沙箱裡執行,沙箱內的 JavaScript 無法存取上層視窗、本機儲存,也不能帶認證標頭,相對路徑的連結會失效,需要改用絕對網址。
  • 用 Only Run If 搭配表達式,過濾掉不預期的請求內容,減少無效執行次數;Ignore Bots 則可以擋掉連結預覽器造成的誤觸發。
  • 處理邏輯要能承受同一筆事件被送兩次,用事件 ID 或業務唯一鍵去重。

常見問題

n8n webhook 的測試網址可以直接當正式網址用嗎?

不建議。測試網址只在點擊「Listen for test event」後維持 120 秒監聽,這個機制是設計給開發除錯用的,並不是穩定的觸發管道。正式對外的服務一律要串接正式網址,並把工作流程切成 Active 狀態。

Webhook 節點跟 HTTP Request 節點差在哪?

Webhook 節點是「被動接收」:產生一組網址,等外部服務主動呼叫進來,用來當工作流程的觸發點。HTTP Request 節點則是「主動發送」:由 n8n 對外呼叫其他系統的 API,通常放在流程中段或後段,兩者方向相反,用途也不同。

本機(localhost)架的 n8n 可以直接收到外部 webhook 嗎?

不行。localhost 的網址外部服務打不進來,官方文件建議本機開發時以隧道模式啟動 n8n,取得一組可以對外的臨時網址,才能實際收到外部服務送來的 webhook 請求。

同一個 webhook 網址可以同時接受 GET 又接受 POST 嗎?

預設不行,Webhook 節點預設只認一種 HTTP Method。要同時接受多種方法,需要在節點設定裡開啟「Allow Multiple HTTP Methods」。

設了 Header Auth,還需要做簽章驗證嗎?

看呼叫端是誰。如果呼叫端是自己寫的系統,可以配合帶上指定標頭,Header Auth 就夠用。但 Stripe、GitHub、LINE 這類平台不會讓你在它們後台自訂認證標頭,它們送的是 HMAC 簽章,這種情況下 Header Auth 沒有著力點,必須自己做簽章驗證。

為什麼 HMAC 算出來的值跟標頭裡的永遠對不上?

最常見的兩個原因:一是用了 n8n 解析過的 JSON 而不是原始內容,重新序列化之後位元組已經不同;解法是開啟 Raw Body,把 $binary.data.data 從 Base64 解碼回 UTF-8 字串再算。二是摘要編碼選錯,GitHub 與 Stripe 要十六進位、LINE 與 Shopify 要 Base64,選錯的話算出來的字串長相完全不同。

webhook 流程要跑五分鐘,一定會逾時嗎?

不一定,關鍵在回應時機而不是流程長度。把 Webhook 節點的 Respond 設成「Using ‘Respond to Webhook’ Node」,並把 Respond to Webhook 節點放在耗時處理之前,呼叫端幾百毫秒內就拿到 200,後面的節點可以繼續跑五分鐘。會逾時的是「When Last Node Finishes」這種等到最後才回應的設定。

同一筆訂單被寫進資料庫兩次,是 n8n 的問題嗎?

通常不是。多數 webhook 來源在沒收到 2xx 回應時會自動重送,Stripe 在正式環境甚至會以指數退避重試最長三天。如果流程已經處理完但回應太慢,來源就會判定失敗而重送。解法有兩層:先回 2xx 再處理,以及用事件 ID 或訂單編號這類唯一鍵在流程開頭做去重。

n8n webhook 跟 Make(前身 Integromat)的 webhook 設定方式一樣嗎?

核心概念相同,都是產生網址等外部觸發,但介面與部分限制不同,例如免費方案的執行限制、逾時秒數都不一樣。想進一步比較兩個工具的差異,可參考〈n8n 和 Make 差在哪?自動化工具比較〉。

把這篇放進別人的河裡

ThreadsLINEX

river breath・河的呼吸

讀完不急著走。跟著河面呼吸三輪,把讀到的,沉下去。

吸 —— 讓念頭浮起

「唵」—— 圓滿俱足,我與河本為一體

Wudelay ・ 人生之河

同一條支流

深入了解 N8N: 工作流節點與部署選擇

2026-03-223 分鐘

我的自動化之旅 – N8N

2026-03-203 分鐘