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=falseand 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-Signatureheader in a Code Node using thecryptolibrary. - 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