n8n Webhook Authentication: Header Auth Done Right
n8n webhook header auth is the first defense for public endpoints, but the docs overlook three critical mistakes: exposing credentials in source control, leaving test URLs active, and trusting callbacks without verification.

n8n Webhook Authentication: Header Auth Done Right
n8n webhook authentication header auth is the first line of defense for a public endpoint, but the official docs gloss over the three critical mistakes that leak credentials into source control, leave the test URL dangling, or trust the caller without verifying the callback. This guide fixes each with real‑world patterns we learned the hard way.
One Secret, Two Directions: Authenticate Both the Incoming Webhook and the Callback
Takeaway: Use the same header auth credential for the webhook receiver and the HTTP node that calls back to your app, so the app can reject unsigned requests with a 401 instead of assuming the URL is secret.
When you expose an n8n webhook, two things happen: your app POSTs data to n8n, and often n8n calls back into your app to write a result (e.g., mark a post as published). The typical setup secures only the first leg. That’s a half‑locked door.
Here’s what we do: one pre‑shared bearer token, stored in a single n8n Header Auth credential, powers both directions.
On the app side (sending to n8n):
curl -X POST https://your-n8n.example.com/webhook/abc123 \
-H "X-Webhook-Token: your-secret-token" \
-H "Content-Type: application/json" \
-d '{"postId": 42}'The Webhook node is configured to expect X-Webhook-Token via a Header Auth credential. If the token doesn’t match, n8n returns a 401 and the workflow never starts.
On the n8n side (HTTP Request node calling back to your app):
HTTP Request Node
Method: PUT
URL: https://your-app.com/api/posts/42/status
Headers:
X-Webhook-Token: {{ $credentials.headerAuth.headerValue }}
Content-Type: application/json
Body: {"status": "published"}In your app’s callback endpoint:
// Express middleware example
app.put('/api/posts/:id/status', (req, res) => {
const token = req.headers['x-webhook-token'];
if (token !== process.env.WEBHOOK_SHARED_SECRET) {
return res.status(401).json({ error: 'Unauthorized' });
}
// ... update post
});Now the app doesn’t trust that the callback came from n8n just because it reached the endpoint—it verifies the shared secret. If you’re building the app side of this, our API development services can help you implement robust header verification and secret rotation.
The Webhook URL Is Not a Secret: Test vs Production and the ‘Inactive’ Trap
Takeaway: Half of all “webhook not firing” reports are due to using the wrong URL or an inactive workflow. Always switch to the production URL before toggling Active, and never rely on the test URL for anything beyond local debugging.
n8n gives every Webhook node two URLs—one for testing and one for production—and the difference trips up even experienced builders.
URL Type | Path Pattern | When It Listens |
|---|---|---|
Test | | Only while the Webhook node editor is open in the UI |
Production | | Only when the workflow is Active (toggle in top‑right corner) |
The n8n Webhook node documentation confirms these behaviors, but the consequences are rarely spelled out:
- If you POST the production URL of an inactive workflow, you get a 404 or a silent failure. No execution appears in the workflow’s Executions tab.
- If you close the node editor, the test URL stops listening immediately. Any POST to it after that gets a 404, with no error logged.
- The test URL never triggers the workflow if the workflow is Active—it’s only for the node’s Listen for Test Event button.
The fix: Once you’re done testing, copy the Production URL from the node, toggle the workflow Active, and configure your app to send to that URL. If you ever see “webhook not firing,” check the workflow’s Active toggle before anything else. For complex pipelines that rely on webhooks, our n8n automation agency sets up monitoring and alerting so you catch these issues before they reach production.
Credentials in Plaintext: Why Inline Headers Are a Security Time Bomb
Takeaway: Never paste an API key directly into an HTTP node’s header field. Those keys end up in the workflow JSON, which gets exported, committed to git, or shared in docs. Convert every inline key to a Header Auth credential before the workflow leaves your machine.
Here’s the mistake we made early on: we had a working workflow, so we exported it and added the JSON to an internal setup document. That document was later committed to a private repository. The exported workflow contained this:
{
"name": "HTTP Request",
"type": "n8n-nodes-base.httpRequest",
"parameters": {
"headerParameters": {
"parameters": [
{
"name": "X-Webhook-Token",
"value": "our-actual-production-secret"
}
]
}
}
}Inline headers are stored in plaintext in the workflow JSON. n8n’s credential store is encrypted and excluded from exports, but a raw header value is not. The community forum confirms that many users hit this when they try to share a workflow and suddenly realize their API keys are visible.
The permanent fix:
- Open the HTTP Request node and delete the inline header.
- Click Add Credential → Header Auth.
- Set the Header Name and Header Value to your secret.
- Save the credential and re‑select it in the node’s Authentication dropdown.
Now the exported workflow looks like this:
{
"name": "HTTP Request",
"type": "n8n-nodes-base.httpRequest",
"parameters": {
"authentication": "headerAuth",
"headerAuth": {
"id": "credential-id-reference"
}
}
}No secret, no plaintext. If you’ve already committed a key, rotate the secret immediately—finding every copy afterwards is far more expensive. For a full production‑ready workflow that uses credential‑based auth, check out our n8n workflow examples in the lab.
Setting Up n8n Webhook Authentication with Header Auth
Takeaway: The Header Auth credential is the cleanest way to authenticate machine‑to‑machine webhook calls. It’s a single pre‑shared token, no username/password and no periodic token refresh.
Here’s the step‑by‑step, from scratch.
1. Create a Header Auth Credential
- In n8n, go to Credentials → Add Credential.
- Search for Header Auth.
- Fill in:
Field | Value |
|---|---|
Name | A descriptive label, e.g. “Shared Webhook Secret” |
Header Name | |
Header Value | Your pre‑shared secret |
- Save.
2. Configure the Webhook Node
- Add a Webhook node to your workflow.
- Set Authentication to Header Auth.
- Select the credential you just created.
- Optionally, set an IP Allowlist to restrict callers further (not a substitute for authentication).
3. Test the Webhook Locally
With the node editor open, click Listen for Test Event, then send a matching request from your terminal:
curl -X POST https://your-n8n.example.com/webhook-test/abc123 \
-H "X-Webhook-Token: your-secret-token" \
-H "Content-Type: application/json" \
-d '{"test": true}'You should see the test event appear in the editor. If you get a 401, double‑check the header name and value exactly match the credential.
4. Deploy to Production
- Copy the Production URL from the node (starts with
/webhook/). - Toggle the workflow Active (top‑right corner).
- Update your app to send requests to the production URL with the same header.
Both the test and production URLs will now enforce the same header auth. Production runs appear in the workflow’s Executions tab.
FAQ
My webhook works in test mode but fails in production. What’s wrong?
Because the webhook only fires while the workflow is Active (the toggle in the top‑right corner). The test URL (/webhook-test/…) listens only when the Webhook node editor is open in the UI. For production, copy the /webhook/… URL, toggle the workflow Active, and send the request there. If you still see 404 or no execution, verify the workflow is Active and you’re not accidentally using the test URL after the listen window expired.
Can I use the same header auth credential for both the webhook and the callback?
Absolutely. Use the same header name and pre‑shared token in both the Webhook node’s credential (to authenticate incoming calls) and the HTTP Request node that calls back to your app. Your app should verify the presence of that header on the callback and reject it with a 401 if missing. This closes the loop and prevents an attacker from calling your app’s write‑back endpoint, even if they discover the n8n webhook URL.
What’s the safest way to manage API keys in n8n workflows?
Never paste an API key directly into an HTTP Request node’s header field. That value is stored in plaintext inside the workflow JSON, which gets exported, shared, and committed. Instead, create a Header Auth credential in n8n and reference it from the node. Credentials live in n8n’s encrypted store and are stripped from exports. If you’ve already committed an inline key, rotate the secret immediately and convert the node to use a credential.