My n8n webhooks are not arriving

The test URL works and the production one does nothing, or the address n8n gives you points at your own machine. Two different problems, both fixable.

What you are seeing

The webhook URL n8n shows you starts with localhost or an internal address, and the service you pasted it into cannot reach it.

Or the URL looks right, testing works while you watch it, and nothing arrives once you stop watching.

Why it happens

Problem one: n8n is telling people the wrong address

n8n has no way of knowing what address the outside world sees. It builds the webhook URL it shows you from what you told it, and if you told it nothing, it says localhost, because that is where it is listening.

That URL is not decoration. It is what you hand to Stripe or GitHub or your form tool, and they will try to reach it exactly as written.

Problem two: the test URL and the production URL are different things

The test URL only exists while you have the editor open and have clicked to listen for an event. It is a temporary listener for building, and it stops the moment you stop watching.

The production URL only works when the workflow is switched on. An inactive workflow returns nothing at its production address, and this catches almost everybody once.

Problem three, the quiet one: nothing can reach your machine anyway

A home connection usually has no public address, no open port and no certificate. The webhook never gets close enough to n8n for n8n to be the problem.

The development tunnel that n8n offers is for building, not for running. It is not a stable address, it is not meant to be one, and pointing a live integration at it is a slow way to discover that.

How to fix it

Tell n8n its real address

Set these to the address the outside world uses, restart, and reopen the workflow. The URL shown in the editor should change to the public one. If it did not, the variables are not reaching the process.

services:
  n8n:
    image: docker.n8n.io/n8nio/n8n:1.70.0
    environment:
      - N8N_HOST=n8n.example.com
      - N8N_PROTOCOL=https
      - N8N_PORT=5678
      - WEBHOOK_URL=https://n8n.example.com/
      - N8N_EDITOR_BASE_URL=https://n8n.example.com/
      - N8N_PROXY_HOPS=1          # you are behind exactly one proxy
    ports:
      - "127.0.0.1:5678:5678"

Put something in front of it that has a certificate

n8n should not be the thing facing the internet. Terminate the certificate in front and pass the request through. Caddy is the shortest version of this, because the certificate is automatic.

n8n.example.com {
    reverse_proxy 127.0.0.1:5678
}

Then check the three things that are not the address

Switch the workflow on. The production URL does nothing while it is inactive.

Use the production URL, not the test one. They differ by one path segment and they behave completely differently.

Confirm the request reaches you at all. Send one yourself from somewhere outside your network, and watch the logs of whatever is in front of n8n. If it never appears there, the problem is in front of n8n and no n8n setting will fix it.

# From a machine that is not on your network
curl -i -X POST https://n8n.example.com/webhook/your-path -d '{"hello":"world"}'

Stopping it happening again

Give it a permanent address once

A name that does not change, a certificate that renews itself, and the address variables set to match. Every webhook problem after that is a real one about the workflow rather than about plumbing.

If your address changes, remember that every integration you have already registered is still pointing at the old one. Nothing tells you. They just stop.

The other ways this goes wrong

The setup that avoids most of them, written out in full