self hosting
N8N Custom Domain Setup
A custom domain for self-hosted n8n needs DNS, reverse proxy routing, HTTPS, and endpoint environment variables that make editor and webhook URLs use the same public host.
Match your incident first
Start with the symptom you can prove
Test URL works, production URL returns 404
First check: Open the Webhook node and compare the active production URL with the external app callback URL.
Wrong fix to avoid: Do not rebuild the whole workflow before proving whether the active production endpoint exists.
Verify: Activate the workflow, send a POST to the production URL, and confirm one new production execution appears.
Production URL shows localhost, container hostname, or http instead of public HTTPS
First check: Check the public URL shown in n8n and compare it with WEBHOOK_URL and reverse proxy forwarded headers.
Wrong fix to avoid: Do not paste the internal Docker service name into external SaaS callback settings.
Verify: The Webhook node displays the public HTTPS domain and an external curl reaches n8n without redirect loops.
External app times out but n8n logs show no execution
First check: Run an external curl smoke test and inspect reverse proxy access logs for the same timestamp.
Wrong fix to avoid: Do not rotate credentials when the request never reaches n8n.
Verify: Proxy access log and n8n execution log both show the same request.
- Use when
- self-hosted, custom domain
- First check
- Copy the callback URL shown in n8n and compare it exactly with the provider app redirect URL.
- Time to check
- 5-10 minutes
- Next step
- Match the symptom, then run the verification checks.
Independent third-party notes. n8n is a trademark of its owner and is referenced only for compatibility and troubleshooting context.
Quick Answer
A custom domain for self-hosted n8n needs DNS, reverse proxy routing, HTTPS, and endpoint environment variables that make editor and webhook URLs use the same public host.
Does this match your symptom?
OAuth callback or credentials are blocking execution
The OAuth login does not return to n8n, credentials reconnect but still fail, or provider scopes and callback URLs do not match production.
First check: Copy the callback URL shown in n8n and compare it exactly with the provider app redirect URL.
Version awareness
Last reviewed 2026-05-21
Key Facts
- DNS
- The domain must point to the server or load balancer that handles public traffic.
- Host setting
- N8N_HOST should match the public host name.
- Webhook setting
- WEBHOOK_URL should match the public base URL used by external services.
- HTTPS
- Public domains should serve n8n over HTTPS.
Production Diagnostic Matrix
Turn checks into a brief| Exact symptom or log | Likely cause | First check | Wrong fix to avoid | Verification |
|---|---|---|---|---|
| Test URL works, production URL returns 404 | Workflow is not active, caller is using the test URL, or the production path was changed after activation. | Open the Webhook node and compare the active production URL with the external app callback URL. | Do not rebuild the whole workflow before proving whether the active production endpoint exists. | Activate the workflow, send a POST to the production URL, and confirm one new production execution appears. |
| Production URL shows localhost, container hostname, or http instead of public HTTPS | WEBHOOK_URL, N8N_HOST, N8N_PROTOCOL, or proxy headers do not match the public domain. | Check the public URL shown in n8n and compare it with WEBHOOK_URL and reverse proxy forwarded headers. | Do not paste the internal Docker service name into external SaaS callback settings. | The Webhook node displays the public HTTPS domain and an external curl reaches n8n without redirect loops. |
| External app times out but n8n logs show no execution | DNS, firewall, Cloudflare, reverse proxy, or path routing blocks the request before it reaches n8n. | Run an external curl smoke test and inspect reverse proxy access logs for the same timestamp. | Do not rotate credentials when the request never reaches n8n. | Proxy access log and n8n execution log both show the same request. |
| Workflow receives payload but caller gets wrong response or hangs | Respond to Webhook is missing, configured for the wrong response mode, or waits for a branch that does not finish. | Check the Webhook node response mode and the Respond to Webhook node path. | Do not add arbitrary Wait nodes to hide timeout behavior. | Caller receives the intended status code and body within the provider timeout window. |
| Webhook path works internally but fails through reverse proxy | Proxy location, base path, body size, TLS termination, or forwarded proto/host headers are wrong. | Compare direct container access with the public reverse proxy route and check forwarded headers. | Do not disable HTTPS verification as a proxy troubleshooting shortcut. | Public HTTPS request preserves method, path, body, and host all the way to n8n. |
Recommended Steps
- Create DNS records for the n8n subdomain.
- Route the domain through your reverse proxy or hosting layer.
- Configure HTTPS for the public domain.
- Set N8N_HOST to the domain host.
- Set WEBHOOK_URL to the public HTTPS base URL.
- Open the editor and copy a webhook URL to confirm the domain is correct.
Verification
- The editor loads at the custom domain.
- Webhook URLs use the custom domain.
- A third-party service can call the production webhook successfully.
First Commands / Checks
curl -i -X POST https://automation.example.com/webhook/example-path \
-H "content-type: application/json" \
-d '{"smokeTest":true}' docker compose ps docker compose logs n8n --tail=100 docker compose exec n8n printenv WEBHOOK_URL N8N_HOST N8N_PROTOCOL N8N_EDITOR_BASE_URL Safe Copyable Config
location / {
proxy_pass http://n8n:5678;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
} N8N_HOST=automation.example.com
N8N_PROTOCOL=https
WEBHOOK_URL=https://automation.example.com/
GENERIC_TIMEZONE=UTC
DB_TYPE=postgresdb
DB_POSTGRESDB_HOST=postgres
DB_POSTGRESDB_DATABASE=n8n
DB_POSTGRESDB_USER=n8n_app
DB_POSTGRESDB_PASSWORD=REDACTED_CHANGE_ME
N8N_ENCRYPTION_KEY=REDACTED_GENERATE_ONCE Warnings
- Changing only DNS is not enough if n8n still generates localhost or internal webhook URLs.
- Domain changes can break integrations that already stored old webhook URLs.