A self-hosted n8n instance with a publicly accessible URL is a potential entry point for unauthorized access. Since workflow credentials often contain sensitive API keys, database passwords, and OAuth tokens, securing your instance should be a top priority before going live.

1. Enable n8n Built-in Authentication

n8n supports two main authentication modes for the UI and API:

  • Email + Password (Default): Built-in user accounts. Set N8N_USER_MANAGEMENT_DISABLED=false and configure the owner account on first launch.
  • LDAP: Enterprise feature for connecting to Active Directory or OpenLDAP for SSO login.

2. Enforce HTTPS-Only Access

Never run production n8n over plain HTTP. Add the following flags to your Docker environment to redirect all traffic to HTTPS:

N8N_PROTOCOL=https
WEBHOOK_URL=https://n8n.yourdomain.com/
N8N_SSL_KEY=/path/to/privkey.pem
N8N_SSL_CERT=/path/to/fullchain.pem

If you are using Nginx as a reverse proxy (recommended), let Nginx handle SSL termination and pass plain HTTP internally to n8n on port 5678.

3. Restrict Access by IP Whitelist

For n8n instances that should only be accessed by your team (not public webhook endpoints), you can restrict access using Nginx's allow/deny directives:

location / {
    allow 203.0.113.45;   # Your office IP
    allow 198.51.100.0/24; # VPN subnet
    deny all;
    proxy_pass http://127.0.0.1:5678;
}

4. Secure Webhook Endpoints

Webhook URLs are intentionally public-facing to receive events. Protect them using:

  • HMAC Signature Validation: Verify the X-Signature header in a Code Node using the crypto library.
  • Static Token: Require a secret token in query parameters (e.g., ?token=abc123) and validate it in an IF node.

🛡️ Best Practice: Use a dedicated, separate n8n instance for public-facing webhooks, and keep your internal workflow execution instance on a private subnet accessible only via VPN. This limits the blast radius if a webhook is ever compromised.

5. Environment Variable Security

Never hardcode credentials in docker-compose.yml files. Always use a .env file referenced via Docker's env_file directive, and add .env to your .gitignore immediately:

# docker-compose.yml
services:
  n8n:
    env_file: .env  # Load from separate .env file