n8n · public webhooks
How to expose n8n webhooks with HTTPS, authentication and spam protection using Caddy or Traefik as a reverse proxy in a production setup.
Every n8n automation that receives external data needs a public webhook with HTTPS, whether it is for EvolutionAPI, a website form or a third-party service.
Required configuration
In docker-compose.yml, always set:
environment:
N8N_HOST: n8n.exemplo.com
N8N_PROTOCOL: https
WEBHOOK_URL: https://n8n.exemplo.com/
WEBHOOK_URL is what n8n uses to build the public webhook URL in workflows. Without it, n8n shows the local IP and webhooks do not work.
Reverse proxy with Caddy
Caddy handles the Let’s Encrypt certificate automatically:
n8n.exemplo.com {
reverse_proxy n8n-main:5678
# Block the editor on the public network (optional)
@editor path / /workflow* /credentials* /executions*
basicauth @editor {
admin $2a$14$...hash_bcrypt
}
}
Webhook authentication
In n8n, each webhook node lets you choose:
- None (public): only use it if the URL is secret enough
- Basic Auth (username + password)
- Header Auth (validate
X-API-Key, for example) - JWT (validate a bearer token)
For EvolutionAPI, the standard is None with a secret URL (/webhook/abc-123-supersecreto).
Spam protection
If the webhook is public (form, OAuth callback):
- Rate limiting in Caddy:
@webhook path /webhook/* rate_limit @webhook 30r/m - Cloudflare in front: the free plan already blocks known bots
- IF node validating the User-Agent or origin before processing
Useful headers
n8n exposes in the webhook node:
$json.headers: all request headers$json.query: parsed query string$json.body: body (JSON or form-data)
To read the real IP (behind Caddy/Cloudflare), use $json.headers['x-forwarded-for'] instead of x-real-ip.
Next steps
Last updated: