Artur
Artur
Founder

n8n Webhook Node: Triggers, Auth & Responding (2026)

June 15, 2026

n8n-webhookn8n-webhook-noderespond-to-webhookwebhook-authentication

TL;DR: The n8n Webhook node turns a workflow into an HTTP endpoint. Pick one HTTP method (or enable Allow Multiple Methods), require auth on anything public (Header Auth is the default for machine-to-machine, JWT when you can verify a token), and decide how you respond before you build anything else. Use "Respond Immediately" for fire-and-forget, "When Last Node Finishes" for short syncs, and the dedicated Respond to Webhook node when you need real control over status codes, headers, or returning data mid-workflow. The single biggest gotcha is the Test URL vs Production URL split: the production endpoint only exists when the workflow is active. If your endpoint dies behind a reverse proxy instead, that is an infrastructure problem, covered in the Nginx post, not a node config problem.

The two URLs that trip everyone up

The Webhook node generates a Test URL and a Production URL, and they behave differently. This is the number one source of "my webhook works in the editor but 404s in production" tickets.

The Test URL only listens after you click "Listen for test event" (or run the workflow manually while it is inactive), and it stays live for 120 seconds. It exists so you can see the incoming payload directly in the editor canvas while building. Once those 120 seconds pass, that URL stops responding.

The Production URL only works when the workflow is active. Flip the Active toggle on, and the production webhook registers. With the production URL, n8n does not paint data onto the canvas; you inspect runs under the Executions tab instead. So the rule is simple: build and debug against the Test URL, then activate the workflow and point the real caller at the Production URL. The paths differ too (the test path includes webhook-test), so swapping one for the other without updating the URL is its own failure mode.

HTTP methods and the path

The Webhook node accepts a single HTTP method by default: you choose GET, POST, PUT, PATCH, DELETE, or HEAD, and only that method hits the endpoint. If you need one endpoint to answer multiple methods (say a GET for verification and a POST for the payload, which several platforms require), open the node settings and turn on Allow Multiple HTTP Methods. Do not create three near-identical workflows to handle three verbs.

The Path field is what makes the URL unique. By default it is a generated UUID, which is fine and slightly obscures the endpoint. You can override it with something readable like stripe-events or intake-form. You can also use path parameters with the :name syntax, for example order/:id, and read that segment back from the node output.

Reading the incoming data

Everything the caller sends lands on the node's output JSON, split into predictable keys: body, headers, query, and params. So a POST body field shows up at {{ $json.body.email }}, a query string value at {{ $json.query.token }}, the request headers at {{ $json.headers }}, and a path parameter at {{ $json.params.id }}. A typical webhook output item looks like this:

{
  "headers": {
    "content-type": "application/json",
    "user-agent": "Stripe/1.0",
    "stripe-signature": "t=1718000000,v1=abc123..."
  },
  "params": {},
  "query": {
    "source": "checkout"
  },
  "body": {
    "event": "payment_succeeded",
    "amount": 4900,
    "customer_email": "buyer@example.com"
  }
}

If you only ever need one field, the node's Property Name option lets you return a single JSON key instead of the whole structure, but leaving it on the default (return everything) is usually the saner choice. You will want those headers later for signature verification, and throwing them away early just means rebuilding the node when requirements change.

Authentication: pick one, do not skip it

Any webhook URL you hand to a third party is a public endpoint, and an unauthenticated one is an open door for anyone who guesses the path. The Webhook node gives you three real options plus "None."

  • Header Auth is the default choice for machine-to-machine calls. You define a header name and value (stored as a credential), and n8n rejects any request missing that exact header. This is the right pattern for internal services, cron callers, and most SaaS platforms that let you set a custom header or secret.

  • Basic Auth sends a username and password in the Authorization header. It works, but it is the weakest option and only worth using when the calling system literally supports nothing else.

  • JWT Auth verifies a signed JSON Web Token using either a passphrase or a PEM key. Use this when the caller already issues JWTs (an identity provider, your own backend) and you want cryptographic verification rather than a shared secret you have to rotate manually.

Our blunt recommendation: Header Auth for almost everything, JWT when the caller speaks JWT, Basic Auth never unless forced, and "None" only for a strictly internal endpoint that is unreachable from the public internet. One nuance the node's built-in auth does not cover is payload signature verification (Stripe, GitHub, Shopify all sign their requests with HMAC). For that you keep the webhook auth as Header Auth or None, then add a Code node right after the trigger to recompute the signature from the raw body and compare it. That is the only way to be sure the request actually came from the platform and was not tampered with in transit.

The three response modes

How the Webhook node replies is set by its Respond option, and getting this wrong produces either timeouts on the caller's side or workflows that silently never finish.

Respond Immediately returns a 200 and the message "Workflow got started" the instant the request arrives, then runs the rest of the workflow in the background. Use this for fire-and-forget ingestion: form submissions, event logging, anything where the caller does not need a computed answer. It is also the safest default for slow workflows, because the caller never waits.

When Last Node Finishes holds the connection open and returns whatever the final executed node outputs. This is convenient for short, synchronous workflows that finish in a second or two, but it is a trap for anything slower. The caller (and any reverse proxy or load balancer in front of n8n) has its own timeout, and a workflow that takes 40 seconds will get its connection cut even though the run completes inside n8n.

Using Respond to Webhook Node is the mode you want whenever the response actually matters. It hands control to a separate Respond to Webhook node placed anywhere downstream, so you decide the exact status code, headers, and body the caller receives. This is also what lets you return early (validate, respond 400, then keep processing) or send a custom success shape.

The Respond to Webhook node

The Respond to Webhook node only does anything when the Webhook node's Respond mode is set to "Using Respond to Webhook Node." Set the trigger to that mode first, or this node is ignored.

Its Respond With option controls the body: All Incoming Items, First Incoming Item, JSON, Text, Binary, Redirect, or No Data. You also set the Response Code (so you can return 201 Created or 422 Unprocessable Entity deliberately instead of a blanket 200) and Response Headers (for CORS headers, Content-Type, or anything the caller expects). A clean acknowledgement response configured as JSON looks like this:

{
  "respondWith": "json",
  "responseCode": 200,
  "responseBody": {
    "status": "received",
    "id": "={{ $json.body.order_id }}",
    "queued": true
  },
  "responseHeaders": {
    "entries": [
      { "name": "Content-Type", "value": "application/json" },
      { "name": "X-Powered-By", "value": "n8n" }
    ]
  }
}

A useful pattern: put one Respond to Webhook node on a validation-failure branch that returns 400 with an error body, and another on the happy path that returns 200 with the result. The caller gets a real, meaningful HTTP contract instead of always seeing success. If you find yourself building the same respond-and-acknowledge logic across many workflows, that response handling is a good candidate to factor out into a sub-workflow you call from each trigger.

Handling binary uploads

File uploads work, but they need explicit handling. When a caller sends multipart/form-data or a raw file body, enable the Binary Data option (older versions) or use the node's binary settings so n8n stores the uploaded file as a binary property instead of trying to parse it as JSON. The file then travels through the workflow as binary data you can pass to a storage node, an S3 upload, or an email attachment.

For raw payloads that are not standard form data (XML, a raw JSON string you want byte-for-byte, or a signed body you need to verify), turn on the Raw Body option. This preserves the exact bytes the caller sent rather than letting n8n parse and reserialize them, which is essential for HMAC signature checks where even reordering JSON keys breaks the comparison.

Common mistakes

The failures we see repeatedly are nearly all configuration, not bugs:

  • Testing against the Test URL, then never activating the workflow, so the Production URL returns 404. Activate the workflow.

  • Using "When Last Node Finishes" on a workflow that takes longer than the caller's timeout, then blaming n8n for the dropped connection. Switch to Respond Immediately or the Respond to Webhook node.

  • Leaving auth on "None" for a public endpoint. Anyone who learns the path can trigger it.

  • Discarding headers with an aggressive Property Name setting, then having nothing to verify a webhook signature against.

  • Webhooks that work locally and fail behind a reverse proxy. That is not the node; it is missing proxy headers and websocket config, which is a different problem entirely. See the Nginx reverse proxy post for the exact headers that fix it.

Get the response mode and auth decided before you wire up a single downstream node. Those two choices shape everything that follows, and retrofitting them onto a finished workflow is the slow way to learn the lesson.

If you are wiring real webhooks into production and want a second set of eyes on the auth, response, and error handling, that is the kind of thing we do at n8n Logic. No pressure: most of what you need is above.

FAQ

What is the difference between the test and production webhook URL in n8n?

The Test URL only listens after you click "Listen for test event" and stays active for 120 seconds, showing incoming data directly on the editor canvas. The Production URL only works when the workflow is active and logs runs under the Executions tab instead of the canvas. They also have different paths, so you cannot swap one string for the other.

How do I secure an n8n webhook?

Set the Webhook node's Authentication to Header Auth (a custom header name and secret value), JWT Auth if the caller issues signed tokens, or Basic Auth as a last resort. Never leave a public endpoint on "None." For platforms that sign their payloads (Stripe, GitHub), keep Header Auth or None and add a Code node that verifies the HMAC signature against the raw body.

How do I return a custom response from an n8n webhook?

Set the Webhook node's Respond mode to "Using Respond to Webhook Node," then add a Respond to Webhook node downstream. That node lets you set the exact Response Code, Response Headers, and body (JSON, Text, Binary, Redirect, or No Data), so you can return 201, 400, or any custom shape the caller expects.

Why does my n8n webhook time out?

Almost always because the Respond mode is "When Last Node Finishes" on a workflow that runs longer than the caller's (or reverse proxy's) timeout. The connection gets cut even though the run completes inside n8n. Switch to "Respond Immediately" for slow processing, or use the Respond to Webhook node to acknowledge early.

Can one n8n webhook accept both GET and POST?

Yes. By default the node accepts a single method, but turning on "Allow Multiple HTTP Methods" in the node settings lets one endpoint answer several verbs. This is required by platforms that send a GET for verification and a POST for the actual payload.

How do I receive a file upload through an n8n webhook?

Enable the node's binary data handling so the uploaded file is stored as a binary property rather than parsed as JSON, then pass that binary data to a storage, email, or upload node. For raw, unparsed payloads (XML or signed bodies), also enable the Raw Body option to preserve the exact bytes.


n8n Webhook Node: Triggers, Auth & Responding (2026) | n8nlogic