Lý thuyết
40 phút
Bài 7/15

SSL & Domain Setup

Secure n8n với HTTPS sử dụng Let's Encrypt, custom domain configuration và SSL best practices

🔐 SSL & Domain Setup

Security Lock

HTTPS không còn optional - browsers cảnh báo HTTP sites và nhiều APIs require HTTPS. Bài này shows how to secure n8n properly.

Why SSL is Critical

Without SSL:

Text
1❌ Browsers show "Not Secure" warning
2❌ Credentials sent in plain text
3❌ Webhooks may not work (APIs require HTTPS)
4❌ No trust from users/clients
5❌ SEO penalty
6❌ Many integrations refuse HTTP

With SSL:

Text
1✅ Encrypted communication
2✅ No browser warnings
3✅ All webhooks work
4✅ Professional appearance
5✅ API integrations work
6✅ Required for OAuth callbacks

Domain Setup

DNS Configuration:

Text
1RECORD TYPE HOST VALUE
2───────────────────────────────────────────
3A n8n YOUR_SERVER_IP
4CNAME n8n your-app.railway.app
5 (for PaaS)

Verify DNS:

Bash
1# Check A record
2dig n8n.yourdomain.com A
3
4# Check propagation
5nslookup n8n.yourdomain.com
6
7# Or use online tool
8# https://dnschecker.org

Method 1: Let's Encrypt + Certbot

With Nginx (Manual):

Bash
1# Install certbot
2sudo apt install certbot python3-certbot-nginx
3
4# Get certificate
5sudo certbot --nginx -d n8n.yourdomain.com
6
7# Auto-renewal is configured automatically
8# Test with:
9sudo certbot renew --dry-run

With Docker Nginx:

yaml
1# docker-compose.yml
2services:
3 nginx:
4 image: nginx:alpine
5 ports:
6 - "80:80"
7 - "443:443"
8 volumes:
9 - ./nginx.conf:/etc/nginx/nginx.conf:ro
10 - ./certbot/conf:/etc/letsencrypt:ro
11 - ./certbot/www:/var/www/certbot:ro
12 depends_on:
13 - n8n
14
15 certbot:
16 image: certbot/certbot
17 volumes:
18 - ./certbot/conf:/etc/letsencrypt
19 - ./certbot/www:/var/www/certbot

Initial Certificate Request:

Bash
1# Create directories
2mkdir -p certbot/conf certbot/www
3
4# Start nginx first (for challenge)
5docker compose up -d nginx
6
7# Request certificate
8docker compose run --rm certbot certonly \
9 --webroot \
10 --webroot-path=/var/www/certbot \
11 --email your@email.com \
12 --agree-tos \
13 --no-eff-email \
14 -d n8n.yourdomain.com
15
16# Restart nginx with SSL config
17docker compose restart nginx

Nginx Configuration:

nginx
1# nginx.conf
2
3events {
4 worker_connections 1024;
5}
6
7http {
8 # HTTP - Redirect to HTTPS
9 server {
10 listen 80;
11 server_name n8n.yourdomain.com;
12
13 # Let's Encrypt challenge
14 location /.well-known/acme-challenge/ {
15 root /var/www/certbot;
16 }
17
18 # Redirect all other traffic
19 location / {
20 return 301 https://$host$request_uri;
21 }
22 }
23
24 # HTTPS
25 server {
26 listen 443 ssl http2;
27 server_name n8n.yourdomain.com;
28
29 # SSL certificates
30 ssl_certificate /etc/letsencrypt/live/n8n.yourdomain.com/fullchain.pem;
31 ssl_certificate_key /etc/letsencrypt/live/n8n.yourdomain.com/privkey.pem;
32
33 # SSL settings
34 ssl_protocols TLSv1.2 TLSv1.3;
35 ssl_prefer_server_ciphers off;
36 ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
37
38 # HSTS
39 add_header Strict-Transport-Security "max-age=63072000" always;
40
41 # Proxy to n8n
42 location / {
43 proxy_pass http://n8n:5678;
44 proxy_http_version 1.1;
45
46 # WebSocket support
47 proxy_set_header Upgrade $http_upgrade;
48 proxy_set_header Connection "upgrade";
49
50 # Headers
51 proxy_set_header Host $host;
52 proxy_set_header X-Real-IP $remote_addr;
53 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
54 proxy_set_header X-Forwarded-Proto $scheme;
55
56 # Timeouts
57 proxy_connect_timeout 300;
58 proxy_send_timeout 300;
59 proxy_read_timeout 300;
60
61 # Buffer settings
62 proxy_buffering off;
63 proxy_request_buffering off;
64 }
65 }
66}

Auto-Renewal:

Bash
1# Add to crontab
20 0 1,15 * * docker compose run --rm certbot renew && docker compose restart nginx

Method 2: Traefik (Automatic SSL)

docker-compose.yml:

yaml
1version: "3.8"
2
3services:
4 traefik:
5 image: traefik:v2.10
6 container_name: traefik
7 restart: unless-stopped
8 command:
9 - "--api.dashboard=false"
10 - "--providers.docker=true"
11 - "--providers.docker.exposedbydefault=false"
12 - "--entrypoints.web.address=:80"
13 - "--entrypoints.websecure.address=:443"
14 - "--entrypoints.web.http.redirections.entrypoint.to=websecure"
15 - "--entrypoints.web.http.redirections.entrypoint.scheme=https"
16 - "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
17 - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
18 - "--certificatesresolvers.letsencrypt.acme.email=your@email.com"
19 - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
20 ports:
21 - "80:80"
22 - "443:443"
23 volumes:
24 - /var/run/docker.sock:/var/run/docker.sock:ro
25 - traefik_letsencrypt:/letsencrypt
26 networks:
27 - web
28
29 n8n:
30 image: n8nio/n8n
31 container_name: n8n
32 restart: unless-stopped
33 labels:
34 - "traefik.enable=true"
35 - "traefik.http.routers.n8n.rule=Host(`n8n.yourdomain.com`)"
36 - "traefik.http.routers.n8n.entrypoints=websecure"
37 - "traefik.http.routers.n8n.tls.certresolver=letsencrypt"
38 - "traefik.http.services.n8n.loadbalancer.server.port=5678"
39 environment:
40 - N8N_HOST=n8n.yourdomain.com
41 - N8N_PROTOCOL=https
42 - WEBHOOK_URL=https://n8n.yourdomain.com/
43 # ... other env vars
44 volumes:
45 - n8n_data:/home/node/.n8n
46 networks:
47 - web
48 - internal
49
50volumes:
51 traefik_letsencrypt:
52 n8n_data:
53
54networks:
55 web:
56 external: true
57 internal:

Create External Network:

Bash
1docker network create web

Method 3: Cloudflare (Easiest)

Setup Steps:

Text
11. Add domain to Cloudflare
22. Update nameservers at registrar
33. Wait for DNS propagation (up to 24h)
44. Enable SSL/TLS: Full (strict)
55. Add A record: n8n → your_server_ip
66. Enable proxy (orange cloud)

Cloudflare Settings:

Text
1SSL/TLS MODE: Full (strict)
2
3Edge Certificates:
4├── Always Use HTTPS: ON
5├── Minimum TLS Version: 1.2
6├── Opportunistic Encryption: ON
7└── TLS 1.3: ON
8
9Origin Certificates:
10└── Generate for your server

With Cloudflare Origin Certificate:

Bash
1# 1. Generate origin certificate in Cloudflare dashboard
2# 2. Download cert.pem and key.pem
3# 3. Place in ./ssl/ directory
4
5# nginx.conf
6ssl_certificate /etc/nginx/ssl/cert.pem;
7ssl_certificate_key /etc/nginx/ssl/key.pem;

Cloudflare + n8n Headers:

nginx
1# Trust Cloudflare IPs
2set_real_ip_from 103.21.244.0/22;
3set_real_ip_from 103.22.200.0/22;
4# ... add all Cloudflare IPs
5real_ip_header CF-Connecting-IP;

n8n Configuration for SSL

Environment Variables:

Bash
1# Required for HTTPS
2N8N_HOST=n8n.yourdomain.com
3N8N_PORT=5678
4N8N_PROTOCOL=https
5
6# Critical for webhooks!
7WEBHOOK_URL=https://n8n.yourdomain.com/
8
9# Editor URL
10N8N_EDITOR_BASE_URL=https://n8n.yourdomain.com/
11
12# If behind proxy
13N8N_TRUST_PROXY=true
Common Mistake

If webhooks return wrong URL, check WEBHOOK_URL environment variable!

Testing SSL

Online Tools:

Text
1SSL Labs: https://www.ssllabs.com/ssltest/
2Security Headers: https://securityheaders.com

Command Line:

Bash
1# Check certificate
2openssl s_client -connect n8n.yourdomain.com:443 -servername n8n.yourdomain.com
3
4# Check expiry
5echo | openssl s_client -connect n8n.yourdomain.com:443 2>/dev/null | openssl x509 -noout -dates
6
7# Test with curl
8curl -vI https://n8n.yourdomain.com

n8n Webhook Test:

Bash
1# Test webhook endpoint
2curl -X POST https://n8n.yourdomain.com/webhook-test/your-webhook-path \
3 -H "Content-Type: application/json" \
4 -d '{"test": "data"}'

Troubleshooting

Certificate Not Working:

Bash
1# Check nginx logs
2docker logs n8n-nginx
3
4# Common issues:
5# 1. Certificate path wrong
6# 2. Certificate not yet issued
7# 3. DNS not propagated
8# 4. Port 80/443 blocked

Mixed Content Warnings:

Text
1Problem: Page loads but shows warnings
2Cause: Some resources loaded over HTTP
3
4Fix:
51. Ensure N8N_PROTOCOL=https
62. Check WEBHOOK_URL starts with https://
73. Clear browser cache

Webhooks Return HTTP URL:

Bash
1# Check environment
2docker exec n8n env | grep -E "WEBHOOK|N8N"
3
4# Ensure these are set:
5WEBHOOK_URL=https://n8n.yourdomain.com/
6N8N_PROTOCOL=https

Certificate Renewal Failed:

Bash
1# Manual renewal
2docker compose run --rm certbot renew --force-renewal
3
4# Check certbot logs
5docker compose logs certbot
6
7# Verify port 80 is accessible
8curl http://n8n.yourdomain.com/.well-known/acme-challenge/test

Security Best Practices

Headers to Add:

nginx
1# Security headers
2add_header X-Frame-Options "SAMEORIGIN" always;
3add_header X-Content-Type-Options "nosniff" always;
4add_header X-XSS-Protection "1; mode=block" always;
5add_header Referrer-Policy "strict-origin-when-cross-origin" always;
6add_header Content-Security-Policy "default-src 'self' https:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';" always;

Rate Limiting:

nginx
1# Add to nginx http block
2limit_req_zone $binary_remote_addr zone=n8n_limit:10m rate=10r/s;
3
4# In server block
5location / {
6 limit_req zone=n8n_limit burst=20 nodelay;
7 # ... proxy config
8}

Bài Tập Thực Hành

SSL Challenge

Secure your n8n instance:

  1. Register domain or use subdomain
  2. Configure DNS A record
  3. Choose SSL method (Certbot/Traefik/Cloudflare)
  4. Deploy SSL configuration
  5. Test with SSL Labs (aim for A+)
  6. Verify webhooks work over HTTPS

Share your SSL Labs score! 🔒

Key Takeaways

Remember
  • 🔐 HTTPS is required - Not optional in 2024
  • 🌐 WEBHOOK_URL - Must match your public URL
  • ♻️ Auto-renew - Certificates expire in 90 days
  • 🛡️ Security headers - Add them all
  • 🧪 Test regularly - SSL Labs, curl, webhooks

Tiếp Theo

Bài tiếp theo: Backup & Restore - Comprehensive backup strategies cho workflows, credentials, và database.