~ / blog / make-n8n-webhooks-safe-again
misc2026-03-17

Make N8N Webhooks Safe Again

Connect your Homelab N8N Server with ngrok and secure it with an nginx whitelist proxy

#N8n#Webhooks#Automations#nginx#ngrok

Do you want to trigger your n8n workflows via Telegram or Discord? Here is how I solved it — without exposing the entire n8n UI to the internet.


The Problem #

Running n8n at home is great. But the moment you want to trigger workflows from outside — via a Telegram bot, a Discord slash command, or any external service — you need n8n to be reachable from the internet.

The naive approach: just forward port 5678 or throw ngrok directly in front of n8n. Problem: that exposes everything. The entire n8n UI, all endpoints, all workflows. Any scanner that hits your ngrok URL has full visibility of what's running.


The Architecture #

The fix is a proxy layer between ngrok and n8n that acts as a strict whitelist:

Internet → ngrok tunnel → nginx proxy → n8n (localhost:5678)

Only explicitly allowed webhook paths get forwarded. Everything else gets a 403. The n8n UI is never reachable from outside.


Setup #

1. n8n running locally #

n8n runs on localhost:5678. Nothing exposed publicly yet.

2. nginx as whitelist proxy #

Install nginx and create a config that only forwards specific webhook paths:

server {
    listen 8080;

    # Block everything by default
    location / {
        return 403;
    }

    # Only allow specific webhook paths
    location = /webhook/550e8400-e29b-41d4-a716-446655440000 {
        proxy_pass http://localhost:5678;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Each allowed webhook gets its own location = /webhook/<uuid> block. Exact match (=) means no path traversal tricks.

3. ngrok points at nginx, not n8n #

ngrok http 8080

ngrok now tunnels to port 8080 (nginx), not 5678 (n8n). The tunnel URL hits the proxy first.

4. UUIDs as path obfuscation #

In n8n, when you create a webhook node, set the path to a UUID:

/webhook/550e8400-e29b-41d4-a716-446655440000

Generate one with:

uuidgen
# or
python3 -c "import uuid; print(uuid.uuid4())"

A UUID has 2^122 possible values. No scanner is going to find that path.


Why This Works #

Attack vectorResult
Scanner hits /403 Forbidden
Scanner enumerates /webhook/*403 on every path
Someone finds the ngrok URLStill only sees 403
Known UUID pathForwarded to n8n ✓
n8n UI (/)Never reachable

Adding a new webhook #

  1. Create webhook node in n8n, set path to a new UUID
  2. Add a location block to nginx config:
location = /webhook/<your-new-uuid> {
    proxy_pass http://localhost:5678;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
}
  1. Reload nginx:
sudo nginx -t && sudo systemctl reload nginx

Triggering from Telegram / Discord #

Use the ngrok URL + the UUID path as your webhook endpoint:

https://<your-ngrok-id>.ngrok-free.app/webhook/550e8400-e29b-41d4-a716-446655440000

Paste that into your Telegram bot's webhook config or Discord bot's interaction URL. Only that exact path works — everything else is dead.


Simple setup, solid security posture for a homelab. No auth tokens, no firewall rules — just a UUID that nobody will ever guess.

← back to blog