n8n Self-Signed Certificate Error

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 httpsAgent that 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.

Leave a Comment

Your email address will not be published. Required fields are marked *