
Step by Step Guide to solve n8n Self-Signed Certificate Error
Who this is for: n8n developers and ops engineers who need to call APIs that use self‑signed TLS certificates, both in local development and production deployments. We cover this in detail in the n8n Authentication Errors Guide.
Quick Diagnosis
The TLS handshake fails because Node.js does not trust the self‑signed certificate presented by the target service.
- Dev‑only quick fix:
export N8N_TLS_REJECT_UNAUTHORIZED=0 # or NODE_TLS_REJECT_UNAUTHORIZED=0
- Production‑grade fix: Import the self‑signed CA into n8n’s trusted store or configure the HTTP Request node (or a Function node) to use a custom
httpsAgentthat trusts the certificate.
1. Why n8n Throws “self‑signed certificate” Errors
If you encounter any proxy authentication required resolve them before continuing with the setup.
| Symptom | Underlying Cause |
|---|---|
| Error: self‑signed certificate | Node.js rejects the TLS handshake because the server’s cert chain cannot be verified against a trusted root CA. |
| TLS handshake timeout | Verification failure aborts the connection before any data is exchanged, common in minimal‑CA Docker images. |
Where it Happens: All nodes that make outbound HTTP/HTTPS calls (HTTP Request, Webhook, OAuth2, etc.).
n8n relies on the Node.js TLS stack; any unknown root causes a rejection.
2. Immediate Development‑Only Work‑around
If you encounter any database connection auth error resolve them before continuing with the setup.
⚠️ EEFA (Expert‑Level Enterprise & Production Advice) – Disabling verification opens the door to man‑in‑the‑middle attacks. Use only on isolated dev machines or CI pipelines that never handle production data.
# Linux/macOS export N8N_TLS_REJECT_UNAUTHORIZED=0
# Windows PowerShell $env:N8N_TLS_REJECT_UNAUTHORIZED = "0"
Effect: All outbound HTTPS requests from n8n skip verification.
Drawback: No protection against forged certificates anywhere in the workflow.
3. Production‑Ready Solutions
3.1. Trust the Self‑Signed CA Globally (Docker / Kubernetes)
Step 1 – Export the CA certificate from the target service (e.g., ca.crt).
Step 2 – Copy the CA into the image
FROM n8nio/n8n:latest COPY ca.crt /usr/local/share/ca-certificates/ca.crt
Step 3 – Update the system trust store
RUN update-ca-certificates
Re‑build and redeploy the container. Node.js now trusts the self‑signed CA automatically.
3.2. Per‑Node Certificate Trust (HTTP Request Node)
When you cannot modify the host trust store, add custom TLS options directly on the node.
Context: Load the CA file inside the node.
const fs = require('fs');
const ca = fs.readFileSync('/data/ca.crt');
Context: Return an httpsAgent that uses the loaded CA.
const https = require('https');
return {
httpsAgent: new https.Agent({
ca,
rejectUnauthorized: true, // keep verification on
}),
};
Paste the two snippets (in order) into the **“Options”** field of the HTTP Request node (available from n8n v0.210+). The request will now succeed using the supplied CA.
3.3. Selective Trust via Environment Variables
Add extra CAs without touching the system bundle.
| Variable | Value | Effect |
|---|---|---|
| N8N_TLS_CA_FILE | Path to CA bundle (PEM) | Node adds this CA to its trusted roots. |
| N8N_TLS_CERTIFICATE | Path to client cert (PEM) | Enables mutual TLS. |
| N8N_TLS_KEY | Path to client key (PEM) | Private key for mutual TLS. |
Docker‑Compose example:
services:
n8n:
image: n8nio/n8n
environment:
- N8N_TLS_CA_FILE=/data/ca-additional.crt
volumes:
- ./ca-additional.crt:/data/ca-additional.crt:ro
Now only the mounted CA is trusted in addition to the default roots.
4. Step‑by‑Step Troubleshooting Checklist
| Step | Action |
|---|---|
| 1 | Verify the remote cert chain: openssl s_client -showcerts -connect host:443. Look for self‑signed. |
| 2 | Ensure the CA file is readable: ls -l /data/ca.crt. Permissions should allow the n8n user to read. |
| 3 | Test with curl inside the container: curl -v https://host. It should succeed if the CA is trusted. |
| 4 | Confirm Node’s trust list: node -e "console.log(require('tls').rootCertificates.includes('…'))" (replace … with a fingerprint). |
| 5 | In the failing HTTP Request node, add the custom TLS code from **3.2**. |
| 6 | Scan logs for UNABLE_TO_VERIFY_LEAF_SIGNATURE. Absence indicates the issue is resolved. |
If any step fails, revisit the previous one—file‑system permissions are the most common blocker.
5. Code Example: Custom HTTPS Agent in a Function Node
When a workflow needs to call a self‑signed API from a **Function** node, use a custom https.Agent.
1️⃣ Load the CA and create the agent
const https = require('https');
const fs = require('fs');
const ca = fs.readFileSync('/data/ca.crt');
const agent = new https.Agent({ ca, rejectUnauthorized: true });
2️⃣ Define request options
const options = {
hostname: 'api.internal.example',
port: 443,
path: '/v1/data',
method: 'GET',
agent,
};
3️⃣ Perform the request and return the result
return new Promise((resolve, reject) => {
const req = https.request(options, (res) => {
let body = '';
res.on('data', (chunk) => (body += chunk));
res.on('end', () => resolve([{ json: JSON.parse(body) }]));
});
req.on('error', reject);
req.end();
});
The Function node now securely fetches data from a service that uses a self‑signed certificate.
6. When to Use Which Approach
| Situation | Recommended Fix |
|---|---|
| Local development on a laptop | Set N8N_TLS_REJECT_UNAUTHORIZED=0 (quickest). |
| Docker‑Compose environment shared by multiple services | Add the CA to the container’s trust store (update-ca-certificates). |
| Kubernetes cluster with strict security policies | Mount the CA as a ConfigMap and use N8N_TLS_CA_FILE. |
| Only a single node needs the cert (e.g., one HTTP Request) | Use the node‑level custom TLS options (section 3.2). |
| Mutual TLS required | Set N8N_TLS_CERTIFICATE and N8N_TLS_KEY. |
8. Next Steps
- Securely store custom CA files using secret management (HashiCorp Vault, AWS Secrets Manager).
- Automate certificate rotation with a sidecar that reloads the CA bundle without restarting n8n.
- Enable the HTTP Request node “Reject Unauthorized” toggle for per‑request security control (available in n8n v0.220+).
Conclusion
TLS handshake failures in n8n stem from Node.js refusing self‑signed certificates.
- In development, disabling verification (
N8N_TLS_REJECT_UNAUTHORIZED=0) provides a fast, temporary bypass. - For production, import the CA globally, mount it via env vars, or apply per‑node custom TLS options—each preserving full verification while granting trust to the required certificate.
Implementing one of these hardened solutions eliminates the “self‑signed certificate” error without compromising security, ensuring reliable, production‑grade integrations with internal services that use private TLS certificates.



