<p><!-- ✅ EXISTING: Image + subtitle — UNCHANGED --><br />
<img class="alignnone wp-image-3924" style="width: 100%; height: auto;" src="https://flowgenius.in/wp-content/uploads/2025/12/Blog-15-Cluster-6.png" alt="" /></p>
<p style="text-align: center;">Step by Step Guide to solve n8n SSL Certificate Errors</p>
<p> </p>
<p><!-- ✅ EXISTING: Who this is for — UNCHANGED --></p>
<p style="margin-bottom: 2em; line-height: 1.9;"><b><strong>Who this is for: </strong></b>Developers or DevOps engineers running n8n in a development, staging, or internal‑only production environment that must use self‑signed TLS certificates. <strong>We cover this in detail in the</strong> n8n Installation Errors Guide</p>
<p><!-- ✅ EXISTING: Intro paragraph — UNCHANGED --></p>
<p style="margin-bottom: 2em; line-height: 1.9;">If n8n aborts with “certificate not trusted” on start‑up, add the self‑signed CA to the OS trust store <strong>or</strong> point Node to it via <code>NODE_EXTRA_CA_CERTS</code>, then configure n8n’s TLS variables (<code>N8N_TLS_CERTIFICATE</code>, <code>N8N_TLS_KEY</code>). Restart n8n and the error disappears.</p>
<p><!-- ✅ EXISTING: Quick Fix box — UNCHANGED --></p>
<div style="background: #f0f7ff; border-left: 4px solid #2563eb; padding: 16px 20px; margin: 24px 0; border-radius: 4px;">
<p style="margin: 0 0 8px 0; font-weight: bold; font-size: 15px;">Quick Fix: Works for 90% of Cases</p>
<p style="margin: 0 0 8px 0;">If n8n is throwing <code>self-signed certificate in chain</code> on startup, run this before starting n8n:</p>
<pre style="background: #1e293b; color: #e2e8f0; padding: 12px; border-radius: 4px; overflow-x: auto; margin: 8px 0;">export NODE_EXTRA_CA_CERTS=/path/to/your/ca.crt
n8n start</pre>
<p style="margin: 8px 0 0 0;">Not working? Your error might be node-level (HTTP Request node, Postgres, SMTP) — jump to the right section using the <strong>diagnosis guide below</strong>.</p>
</div>
<p> </p>
<p><!-- ==================================================== 🆕 NEW ADDITION #1 — Diagnosis Table Placed here: after Quick Fix box, before Section 1 Reason: helps users self-triage → reduces bounce rate ===================================================== --></p>
<h2 style="margin-bottom: 20px; line-height: 1.3;">Which SSL Error Do You Have? (Find Your Fix Fast)</h2>
<p style="margin-bottom: 1.5em; line-height: 1.9;">Not all n8n SSL errors are the same. Find your exact error in the table and jump to the right fix:</p>
<table style="border-collapse: collapse; width: 100%; margin-bottom: 2em;">
<thead>
<tr>
<th style="padding: 13px; border: 1px solid #e0e0e0; text-align: left; background: #f9f9f9;">Error Message</th>
<th style="padding: 13px; border: 1px solid #e0e0e0; text-align: left; background: #f9f9f9;">Where It Appears</th>
<th style="padding: 13px; border: 1px solid #e0e0e0; text-align: left; background: #f9f9f9;">Go To</th>
</tr>
</thead>
<tbody>
<tr>
<td style="padding: 13px; border: 1px solid #e0e0e0;"><code>self-signed certificate in chain</code></td>
<td style="padding: 13px; border: 1px solid #e0e0e0;">Terminal / n8n startup</td>
<td style="padding: 13px; border: 1px solid #e0e0e0;">Sections 2 & 3</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #e0e0e0;"><code>unable to verify the first certificate</code></td>
<td style="padding: 13px; border: 1px solid #e0e0e0;">Terminal / n8n startup</td>
<td style="padding: 13px; border: 1px solid #e0e0e0;">Sections 2 & 3</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #e0e0e0;"><code>SELF_SIGNED_CERT_IN_CHAIN</code> on HTTP Request node</td>
<td style="padding: 13px; border: 1px solid #e0e0e0;">Workflow execution log</td>
<td style="padding: 13px; border: 1px solid #e0e0e0;">Section 5b</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #e0e0e0;"><code>self-signed certificate</code> during <code>npm install -g n8n</code></td>
<td style="padding: 13px; border: 1px solid #e0e0e0;">Terminal during install</td>
<td style="padding: 13px; border: 1px solid #e0e0e0;">Section 5c</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #e0e0e0;">SSL error on Postgres / Supabase / MySQL node</td>
<td style="padding: 13px; border: 1px solid #e0e0e0;">Workflow execution log</td>
<td style="padding: 13px; border: 1px solid #e0e0e0;">Section 5d</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #e0e0e0;">SSL error on SMTP / Email Send node</td>
<td style="padding: 13px; border: 1px solid #e0e0e0;">Workflow execution log</td>
<td style="padding: 13px; border: 1px solid #e0e0e0;">Section 5e</td>
</tr>
</tbody>
</table>
<p style="margin-bottom: 2em; line-height: 1.9;">If you’re on <strong>n8n v1.42.0 or newer running in Docker</strong>, skip straight to <strong>Section 4b</strong> — the <code>/opt/custom-certificates</code> method requires no environment variables at all.</p>
<hr />
<p><!-- ==================================================== END OF NEW ADDITION #1 ===================================================== --></p>
<p><!-- ✅ EXISTING: Section 1 — UNCHANGED --></p>
<h2 style="margin-bottom: 45px; line-height: 1.3;">1. Why n8n Throws “certificate not trusted”</h2>
<p><strong>If you encounter any</strong> <a href="/proxy-configuration-error-n8n">proxy configuration error n8n</a><strong> resolve them before continuing with the setup.</strong></p>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>What you’ll see</strong> – n8n exits with errors such as:</p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">Error: self‑signed certificate in chain
Error: unable to verify the first certificate
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>Root cause</strong> – Node.js cannot verify the TLS chain because the CA that signed the server’s certificate is missing from its trust store.</p>
<blockquote style="margin: 0 0 2em 0; padding-left: 1em; border-left: 4px solid #e0e0e0;">
<p style="margin: 0; line-height: 1.9;"><strong>EEFA</strong>: Self‑signed certificates are acceptable only for internal/dev use. Production‑grade deployments should use a trusted CA (Let’s Encrypt, commercial CA) to avoid MITM risks.</p>
</blockquote>
<div style="margin: 55px 0;">
<hr />
</div>
<p><!-- ✅ EXISTING: Section 2 — UNCHANGED --></p>
<h2 style="margin-bottom: 45px; line-height: 1.3;">2. Import the Self‑Signed CA into the Host Trust Store</h2>
<p><strong>If you encounter any</strong> <a href="/docker-compose-configuration-error-n8n">docker compose configuration error n8n</a><strong> resolve them before continuing with the setup.</strong></p>
<h3 style="margin-bottom: 45px; line-height: 1.3;">2.1 Linux (Debian/Ubuntu)</h3>
<p style="margin-bottom: 2em; line-height: 1.9;"><em>Copy the CA bundle and update the system store.</em></p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;"># Copy the CA file
sudo cp my-ca.crt /usr/local/share/ca-certificates/
</pre>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;"># Refresh the trust store
sudo update-ca-certificates
</pre>
<h3 style="margin-bottom: 45px; line-height: 1.3;">2.2 macOS</h3>
<p style="margin-bottom: 2em; line-height: 1.9;"><em>Add the CA to the system keychain.</em></p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">sudo security add-trusted-cert \
-d -r trustRoot -k /Library/Keychains/System.keychain \
my-ca.crt
</pre>
<h3 style="margin-bottom: 45px; line-height: 1.3;">2.3 Windows (PowerShell)</h3>
<p style="margin-bottom: 2em; line-height: 1.9;"><em>Import the CA into the Local Machine Trusted Root store.</em></p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">Import-Certificate -FilePath .\my-ca.crt `
-CertStoreLocation Cert:\LocalMachine\Root
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>Verification</strong> – Run the following to confirm the CA is recognized:</p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">openssl s_client -connect <host>:443 -CAfile my-ca.crt
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;">You should see <code>Verify return code: 0 (ok)</code>.</p>
<div style="margin: 55px 0;">
<hr />
</div>
<p><!-- ✅ EXISTING: Section 3 — UNCHANGED --></p>
<h2 style="margin-bottom: 45px; line-height: 1.3;">3. Tell Node.js to Trust the Self‑Signed CA (OS‑store alternative)</h2>
<p style="margin-bottom: 2em; line-height: 1.9;">Set <code>NODE_EXTRA_CA_CERTS</code> <strong>before</strong> starting n8n.</p>
<h3 style="margin-bottom: 45px; line-height: 1.3;">3.1 Bash / Linux / macOS</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">export NODE_EXTRA_CA_CERTS=/path/to/my-ca.crt
</pre>
<h3 style="margin-bottom: 45px; line-height: 1.3;">3.2 Windows CMD</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">set NODE_EXTRA_CA_CERTS=C:\path\to\my-ca.crt
</pre>
<h3 style="margin-bottom: 45px; line-height: 1.3;">3.3 PowerShell</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">$env:NODE_EXTRA_CA_CERTS="C:\path\to\my-ca.crt"
</pre>
<blockquote style="margin: 0 0 2em 0; padding-left: 1em; border-left: 4px solid #e0e0e0;">
<p style="margin: 0; line-height: 1.9;"><strong>EEFA</strong>: Never use <code>NODE_TLS_REJECT_UNAUTHORIZED=0</code> in production—it disables verification entirely.</p>
</blockquote>
<div style="margin: 55px 0;">
<hr />
</div>
<p><!-- ✅ EXISTING: Section 4 — UNCHANGED --></p>
<h2 style="margin-bottom: 45px; line-height: 1.3;">4. Configure n8n TLS Termination (Self‑Signed Cert)</h2>
<h3 style="margin-bottom: 45px; line-height: 1.3;">4.1 Docker Compose – Service Definition</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">version: '3.8'
services:
n8n:
image: n8nio/n8n
ports:
- "443:5678"
</pre>
<h3 style="margin-bottom: 45px; line-height: 1.3;">4.2 Docker Compose – TLS & CA Environment</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;"> environment:
- N8N_TLS_CERTIFICATE=/certs/server.crt
- N8N_TLS_KEY=/certs/server.key
- NODE_EXTRA_CA_CERTS=/certs/my-ca.crt # optional if OS store used
</pre>
<h3 style="margin-bottom: 45px; line-height: 1.3;">4.3 Docker Compose – Read‑Only Volume Mount</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;"> volumes:
- ./certs:/certs:ro # mount certs read‑only
</pre>
<blockquote style="margin: 0 0 2em 0; padding-left: 1em; border-left: 4px solid #e0e0e0;">
<p style="margin: 0; line-height: 1.9;"><strong>Tip</strong>: Keeping the <code>certs</code> directory read‑only (<code>:ro</code>) prevents accidental modification from inside the container.</p>
</blockquote>
<h3 style="margin-bottom: 45px; line-height: 1.3;">4.4 Stand‑Alone npm / yarn Installation – .env File</h3>
<p style="margin-bottom: 2em; line-height: 1.9;">Create an <code>.env</code> file in the n8n root:</p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">N8N_TLS_CERTIFICATE=/opt/n8n/certs/server.crt
N8N_TLS_KEY=/opt/n8n/certs/server.key
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;">If you rely on <code>NODE_EXTRA_CA_CERTS</code>:</p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">NODE_EXTRA_CA_CERTS=/opt/n8n/certs/my-ca.crt
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;">Start n8n:</p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">npm run start # or simply: n8n start
</pre>
<h3 style="margin-bottom: 45px; line-height: 1.3;">4.5 Windows Service – Inline Environment</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">set N8N_TLS_CERTIFICATE=C:\n8n\certs\server.crt
set N8N_TLS_KEY=C:\n8n\certs\server.key
set NODE_EXTRA_CA_CERTS=C:\n8n\certs\my-ca.crt
n8n start
</pre>
<p><!-- ==================================================== 🆕 NEW ADDITION #2 — /opt/custom-certificates method Placed here: after 4.5, before Section 5 Reason: biggest content gap vs. competitors. This is the official Docker method missing from current post. ===================================================== --></p>
<div style="margin: 55px 0;">
<hr />
</div>
<h2 style="margin-bottom: 45px; line-height: 1.3;">4b. Mount Certificates via <code>/opt/custom-certificates</code> (n8n v1.42.0+ — Easiest Docker Method)</h2>
<p style="margin-bottom: 2em; line-height: 1.9;">If you’re running n8n v1.42.0 or newer in Docker, this is the cleanest approach. You don’t need any environment variables — n8n automatically detects and trusts every <code>.crt</code> file placed in the <code>/opt/custom-certificates</code> directory on startup.</p>
<h3 style="margin-bottom: 20px; line-height: 1.3;">Step 1 — Create a local folder and copy your CA file into it</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">mkdir -p ./pki
cp my-ca.crt ./pki/
</pre>
<h3 style="margin-bottom: 20px; line-height: 1.3;">Step 2 — Mount the folder into your <code>docker-compose.yml</code></h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">services:
n8n:
image: n8nio/n8n
ports:
- "443:5678"
volumes:
- n8n_data:/home/node/.n8n
- ./pki:/opt/custom-certificates:ro
</pre>
<h3 style="margin-bottom: 20px; line-height: 1.3;">Step 3 — Fix file permissions (run once after first start)</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">docker exec n8n chmod -R 644 /opt/custom-certificates
docker restart n8n
</pre>
<h3 style="margin-bottom: 20px; line-height: 1.3;">Step 4 — Confirm n8n loaded the certificate</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">docker logs n8n 2>&1 | grep -i cert
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;">You should see a line referencing your certificate file. If you see nothing, the file either has wrong permissions or is not valid PEM format.</p>
<blockquote style="margin: 0 0 2em 0; padding-left: 1em; border-left: 4px solid #e0e0e0;">
<p style="margin: 0 0 10px 0; line-height: 1.9;"><strong>Most common failure:</strong> Container starts but SSL errors persist even after mounting. This almost always means n8n can’t read the files because they’re owned by root inside the container. The <code>chmod 644</code> in Step 3 fixes this.</p>
<p style="margin: 0; line-height: 1.9;">If it still fails, verify your file is valid PEM format: <code>openssl x509 -in ./pki/my-ca.crt -text -noout</code></p>
</blockquote>
<blockquote style="margin: 0 0 2em 0; padding-left: 1em; border-left: 4px solid #e0e0e0;">
<p style="margin: 0; line-height: 1.9;"><strong>Note:</strong> This method handles certificates for outbound connections made by n8n’s core. If your workflows make HTTPS calls via the HTTP Request node that still fail, also set <code>NODE_EXTRA_CA_CERTS</code> pointing to the same CA file (see Section 3).</p>
</blockquote>
<div style="margin: 55px 0;">
<hr />
</div>
<p><!-- ==================================================== END OF NEW ADDITION #2 ===================================================== --></p>
<p><!-- ✅ EXISTING: Section 5 — UNCHANGED --></p>
<h2 style="margin-bottom: 45px; line-height: 1.3;">5. Verify the TLS Configuration</h2>
<h3 style="margin-bottom: 45px; line-height: 1.3;">5.1 Curl Test (skip hostname verification)</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">curl -k https://localhost:443/api/health
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>Expected output</strong> – <code>{"status":"ok","version":"X.Y.Z"}</code> (the <code>-k</code> flag bypasses hostname validation but confirms the endpoint is reachable).</p>
<h3 style="margin-bottom: 45px; line-height: 1.3;">5.2 Curl Test (use the CA – no <code>-k</code>)</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">curl --cacert my-ca.crt https://localhost:443/api/health
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>Expected output</strong> – Same JSON response <strong>without</strong> <code>-k</code>, proving the CA is trusted.</p>
<h3 style="margin-bottom: 45px; line-height: 1.3;">5.3 n8n HTTP Request Node</h3>
<p style="margin-bottom: 2em; line-height: 1.9;">Create a simple <strong>HTTP Request</strong> node targeting <code>https://localhost:443/api/health</code>. A successful execution (no “self‑signed certificate” error) confirms the in‑process trust chain.</p>
<table style="border-collapse: collapse; width: 100%; margin-bottom: 2em;">
<thead>
<tr>
<th style="padding: 13px; border: 1px solid #e0e0e0; text-align: left;">Verification Method</th>
<th style="padding: 13px; border: 1px solid #e0e0e0; text-align: left;">Expected Result</th>
</tr>
</thead>
<tbody>
<tr>
<td style="padding: 13px; border: 1px solid #e0e0e0;">curl -k (skip verification)</td>
<td style="padding: 13px; border: 1px solid #e0e0e0;">200 OK (does not prove trust)</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #e0e0e0;">curl –cacert my-ca.crt …</td>
<td style="padding: 13px; border: 1px solid #e0e0e0;">200 OK <strong>without</strong> <code>-k</code></td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #e0e0e0;">n8n HTTP Request node</td>
<td style="padding: 13px; border: 1px solid #e0e0e0;">No TLS error in execution log</td>
</tr>
</tbody>
</table>
<p><!-- ==================================================== 🆕 NEW ADDITION #3 — Node-level SSL errors (5b–5e) Placed here: after the verification table, before Section 6 Reason: covers 4 additional error variants people search for that currently have no answer on this page ===================================================== --></p>
<div style="margin: 55px 0;">
<hr />
</div>
<h2 style="margin-bottom: 45px; line-height: 1.3;">5b. Fix SSL Errors Inside the HTTP Request Node</h2>
<p style="margin-bottom: 2em; line-height: 1.9;">If n8n starts fine but you get <code>self-signed certificate in chain</code> when a workflow runs an HTTP Request node, the issue is different — n8n’s internal request engine doesn’t automatically inherit your OS trust store. You need to fix it at the process level.</p>
<p style="margin-bottom: 1em; line-height: 1.9;"><strong>Fix 1 — Per-node toggle (quick workaround, not recommended for production):</strong></p>
<p style="margin-bottom: 2em; line-height: 1.9;">In the HTTP Request node, click <strong>Options</strong> at the bottom → enable <strong>Ignore SSL Issues</strong>. This bypasses verification for that specific node only. Use this temporarily to confirm SSL is the actual problem.</p>
<p style="margin-bottom: 1em; line-height: 1.9;"><strong>Fix 2 — NODE_EXTRA_CA_CERTS (correct long-term fix):</strong></p>
<p style="margin-bottom: 2em; line-height: 1.9;">Set <code>NODE_EXTRA_CA_CERTS</code> in your environment before n8n starts (see Section 3 above). This makes every HTTP Request node in every workflow trust your CA automatically — no per-node toggle needed.</p>
<blockquote style="margin: 0 0 2em 0; padding-left: 1em; border-left: 4px solid #e0e0e0;">
<p style="margin: 0; line-height: 1.9;"><strong>Why does this happen even after fixing the startup error?</strong> The OS trust store fix (Section 2) makes your system trust the CA. But n8n’s HTTP engine reads <code>NODE_EXTRA_CA_CERTS</code> at process startup — not the OS store. If you only did Section 2 and skipped Section 3, startup works but in-workflow requests still fail.</p>
</blockquote>
<div style="margin: 55px 0;">
<hr />
</div>
<h2 style="margin-bottom: 45px; line-height: 1.3;">5c. Fix SSL Errors During <code>npm install -g n8n</code></h2>
<p style="margin-bottom: 2em; line-height: 1.9;">If you get a certificate error during the n8n installation itself (not at runtime), it means npm can’t reach the registry over your corporate or self-signed proxy.</p>
<p style="margin-bottom: 1em; line-height: 1.9;"><strong>Step 1 — Install with strict-ssl disabled (temporary):</strong></p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">npm install -g n8n --strict-ssl=false
</pre>
<p style="margin-bottom: 1em; line-height: 1.9;"><strong>Step 2 — Re-enable strict SSL immediately after:</strong></p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">npm config set strict-ssl true
</pre>
<p style="margin-bottom: 1em; line-height: 1.9;"><strong>Step 3 — Register your CA with npm permanently so future installs don’t need the flag:</strong></p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">npm config set cafile /path/to/my-ca.crt
</pre>
<div style="margin: 55px 0;">
<hr />
</div>
<h2 style="margin-bottom: 45px; line-height: 1.3;">5d. Fix SSL Errors on Postgres / Supabase / MySQL Nodes</h2>
<p style="margin-bottom: 2em; line-height: 1.9;">When a database node throws an SSL error, the fix lives inside the credential — not the server or environment config.</p>
<p style="margin-bottom: 1em; line-height: 1.9;"><strong>For Postgres / MySQL:</strong></p>
<ol style="margin-bottom: 2em; line-height: 1.9;">
<li>Go to <strong>Credentials</strong> → open your Postgres or MySQL credential</li>
<li>Find the <strong>SSL</strong> section</li>
<li>Set SSL to <code>allow</code> or <code>require</code></li>
<li>Under <strong>CA Certificate</strong>, paste the full contents of your <code>my-ca.crt</code> file</li>
<li>Save and re-run the workflow</li>
</ol>
<p style="margin-bottom: 1em; line-height: 1.9;"><strong>For Supabase:</strong></p>
<p style="margin-bottom: 2em; line-height: 1.9;">Supabase’s connection pooler (port 6543) uses a self-signed certificate in some regions. In your Postgres credential, set the <strong>CA Certificate</strong> field to Supabase’s root CA — downloadable from your Supabase project under <strong>Settings → Database → SSL Certificate</strong>.</p>
<div style="margin: 55px 0;">
<hr />
</div>
<h2 style="margin-bottom: 45px; line-height: 1.3;">5e. Fix SSL Errors on SMTP / Email Send Nodes</h2>
<p style="margin-bottom: 2em; line-height: 1.9;">Corporate SMTP servers (Microsoft Exchange, internal mail relays) frequently use self-signed or internally-signed certificates. If your Email Send node fails with an SSL error:</p>
<ol style="margin-bottom: 2em; line-height: 1.9;">
<li>In the <strong>Email Send</strong> node credentials, confirm <strong>SSL/TLS</strong> is set to <code>STARTTLS</code> or <code>SSL/TLS</code> depending on what your mail server requires (ask your sysadmin if unsure)</li>
<li>If the error continues, set <code>NODE_EXTRA_CA_CERTS</code> to your internal mail server’s CA certificate path (same method as Section 3) and restart n8n</li>
<li>For completely isolated internal environments with no external exposure, you can set <code>NODE_TLS_REJECT_UNAUTHORIZED=0</code> as a last resort — but <strong>only</strong> in internal-only setups. Never in production.</li>
</ol>
<div style="margin: 55px 0;">
<hr />
</div>
<p><!-- ==================================================== END OF NEW ADDITION #3 ===================================================== --></p>
<p><!-- ✅ EXISTING: Section 6 Troubleshooting Checklist — UNCHANGED --></p>
<h2 style="margin-bottom: 45px; line-height: 1.3;">6. Troubleshooting Checklist</h2>
<table style="border-collapse: collapse; width: 100%; margin-bottom: 2em;">
<thead>
<tr>
<th style="padding: 13px; border: 1px solid #e0e0e0; text-align: left;">✅ Done?</th>
<th style="padding: 13px; border: 1px solid #e0e0e0; text-align: left;">Step</th>
</tr>
</thead>
<tbody>
<tr>
<td style="padding: 13px; border: 1px solid #e0e0e0;">☐</td>
<td style="padding: 13px; border: 1px solid #e0e0e0;">CA file is PEM (<code>-----BEGIN CERTIFICATE-----</code>).</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #e0e0e0;">☐</td>
<td style="padding: 13px; border: 1px solid #e0e0e0;"><code>NODE_EXTRA_CA_CERTS</code> points to the <strong>exact</strong> CA file (not a directory).</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #e0e0e0;">☐</td>
<td style="padding: 13px; border: 1px solid #e0e0e0;"><code>N8N_TLS_CERTIFICATE</code> and <code>N8N_TLS_KEY</code> paths are readable by the n8n process.</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #e0e0e0;">☐</td>
<td style="padding: 13px; border: 1px solid #e0e0e0;">Docker volume mounts are <code>ro</code> and files exist inside the container (<code>docker exec <id> ls /certs</code>).</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #e0e0e0;">☐</td>
<td style="padding: 13px; border: 1px solid #e0e0e0;">No conflicting env vars (e.g., <code>NODE_TLS_REJECT_UNAUTHORIZED</code>).</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #e0e0e0;">☐</td>
<td style="padding: 13px; border: 1px solid #e0e0e0;">After any change, <strong>restart</strong> the n8n service/container (not just reload).</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #e0e0e0;">☐</td>
<td style="padding: 13px; border: 1px solid #e0e0e0;">Review logs (<code>docker logs n8n</code> or <code>journalctl -u n8n</code>) for lingering TLS errors.</td>
</tr>
</tbody>
</table>
<p style="margin-bottom: 2em; line-height: 1.9;">If a step fails, logs typically surface the missing file or a verification code such as <code>UNABLE_TO_VERIFY_LEAF_SIGNATURE</code> or <code>SELF_SIGNED_CERT_IN_CHAIN</code>.</p>
<div style="margin: 55px 0;">
<hr />
</div>
<p><!-- ✅ EXISTING: Section 7 Production Alternatives — UNCHANGED --></p>
<h2 style="margin-bottom: 45px; line-height: 1.3;">7. Production‑Ready Alternatives</h2>
<table style="border-collapse: collapse; width: 100%; margin-bottom: 2em;">
<thead>
<tr>
<th style="padding: 13px; border: 1px solid #e0e0e0; text-align: left;">Scenario</th>
<th style="padding: 13px; border: 1px solid #e0e0e0; text-align: left;">Recommended Action</th>
</tr>
</thead>
<tbody>
<tr>
<td style="padding: 13px; border: 1px solid #e0e0e0;">Public‑facing n8n instance</td>
<td style="padding: 13px; border: 1px solid #e0e0e0;">Use a <strong>trusted CA</strong> (Let’s Encrypt) and terminate TLS at a reverse proxy (Traefik, Nginx).</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #e0e0e0;">Multi‑node Kubernetes deployment</td>
<td style="padding: 13px; border: 1px solid #e0e0e0;">Store certificates in a <strong>Secret</strong>, mount into the pod, and set <code>NODE_EXTRA_CA_CERTS</code> via a ConfigMap.</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #e0e0e0;">High‑security environment</td>
<td style="padding: 13px; border: 1px solid #e0e0e0;">Enable <strong>mTLS</strong>: add <code>N8N_TLS_CLIENT_CERTIFICATE</code> and <code>N8N_TLS_CLIENT_KEY</code> variables, enforce client verification.</td>
</tr>
</tbody>
</table>
<p><!-- ==================================================== 🆕 NEW ADDITION #4 — FAQ Section Placed here: after Section 7 table, before Conclusion Reason: targets People Also Ask boxes + featured snippets ===================================================== --></p>
<div style="margin: 55px 0;">
<hr />
</div>
<h2 style="margin-bottom: 45px; line-height: 1.3;">Frequently Asked Questions</h2>
<h3 style="margin-bottom: 20px; line-height: 1.3;">What does “self-signed certificate in chain” mean in n8n?</h3>
<p style="margin-bottom: 2em; line-height: 1.9;">It means Node.js tried to verify a TLS certificate during an HTTPS connection but couldn’t find a trusted Certificate Authority (CA) that signed it. The certificate exists and is technically valid, but Node.js doesn’t recognise the entity that issued it. The fix is to tell Node.js about your CA using <code>NODE_EXTRA_CA_CERTS</code> or by importing the CA into your OS trust store — not to disable verification entirely.</p>
<h3 style="margin-bottom: 20px; line-height: 1.3;">Should I use NODE_TLS_REJECT_UNAUTHORIZED=0 to fix n8n SSL errors?</h3>
<p style="margin-bottom: 2em; line-height: 1.9;">No. Setting <code>NODE_TLS_REJECT_UNAUTHORIZED=0</code> disables all TLS certificate verification globally across the entire n8n process. This silences the error but means n8n will accept any certificate — including fraudulent ones from attackers — making it vulnerable to man-in-the-middle attacks. Use <code>NODE_EXTRA_CA_CERTS</code> instead. It keeps verification fully enabled while adding your specific CA to the trusted list.</p>
<h3 style="margin-bottom: 20px; line-height: 1.3;">Does n8n support custom CA certificates natively?</h3>
<p style="margin-bottom: 2em; line-height: 1.9;">Yes, since n8n v1.42.0. Mount a folder containing your <code>.crt</code> files to <code>/opt/custom-certificates</code> inside the Docker container. n8n automatically loads all valid PEM certificates in that directory at startup. No environment variables are required. See Section 4b for the full setup.</p>
<h3 style="margin-bottom: 20px; line-height: 1.3;">How do I fix SSL errors in n8n running in Docker?</h3>
<p style="margin-bottom: 2em; line-height: 1.9;">Two approaches: (1) Set <code>NODE_EXTRA_CA_CERTS=/certs/my-ca.crt</code> in your <code>docker-compose.yml</code> environment block and mount the certs folder as a volume. (2) On n8n v1.42.0+, mount your CA file to <code>/opt/custom-certificates</code> — n8n auto-loads it on startup. Method 2 is cleaner because it requires no environment variable changes and works across all n8n versions that support it.</p>
<h3 style="margin-bottom: 20px; line-height: 1.3;">What is NODE_EXTRA_CA_CERTS and how does it work in n8n?</h3>
<p style="margin-bottom: 2em; line-height: 1.9;"><code>NODE_EXTRA_CA_CERTS</code> is a Node.js environment variable that points to an additional CA certificate file. When set before n8n starts, Node.js appends that CA to its built-in trust list without replacing any existing trusted CAs. For n8n, this means all outgoing HTTPS connections — workflow HTTP Request nodes, webhooks, external API calls — will trust certificates signed by your CA.</p>
<h3 style="margin-bottom: 20px; line-height: 1.3;">Why does my n8n HTTP Request node still show SSL errors after fixing the startup error?</h3>
<p style="margin-bottom: 2em; line-height: 1.9;">This is one of the most common points of confusion. Importing the CA into your OS trust store (Section 2) fixes the startup error because n8n’s startup process respects the OS store. But the HTTP Request node’s internal engine reads <code>NODE_EXTRA_CA_CERTS</code> at process startup — not the OS store. If you only did Section 2 and skipped Section 3, startup works but in-workflow HTTPS requests still fail. The fix is to also set <code>NODE_EXTRA_CA_CERTS</code> explicitly in your environment and restart n8n.</p>
<div style="margin: 55px 0;">
<hr />
</div>
<p><!-- ==================================================== END OF NEW ADDITION #4 ===================================================== --></p>
<p><!-- ✅ EXISTING: Conclusion — UNCHANGED --></p>
<h2 style="margin-bottom: 45px; line-height: 1.3;">Conclusion</h2>
<p style="margin-bottom: 2em; line-height: 1.9;">Adding the self‑signed CA to the host trust store or exposing it via <code>NODE_EXTRA_CA_CERTS</code> resolves the “certificate not trusted” error by giving Node.js the chain it needs to validate TLS connections. Coupled with proper <code>N8N_TLS_CERTIFICATE</code> and <code>N8N_TLS_KEY</code> settings, n8n starts cleanly and can communicate securely with both internal services and external APIs. For production workloads, migrate to a trusted CA and consider proxy‑based termination or mTLS to meet higher security requirements.</p>

Step by Step Guide to solve n8n SSL Certificate Errors
Who this is for: Developers or DevOps engineers running n8n in a development, staging, or internal‑only production environment that must use self‑signed TLS certificates. We cover this in detail in the n8n Installation Errors Guide
If n8n aborts with “certificate not trusted” on start‑up, add the self‑signed CA to the OS trust store or point Node to it via NODE_EXTRA_CA_CERTS, then configure n8n’s TLS variables (N8N_TLS_CERTIFICATE, N8N_TLS_KEY). Restart n8n and the error disappears.
Quick Fix: Works for 90% of Cases
If n8n is throwing self-signed certificate in chain on startup, run this before starting n8n:
export NODE_EXTRA_CA_CERTS=/path/to/your/ca.crt
n8n start
Not working? Your error might be node-level (HTTP Request node, Postgres, SMTP) — jump to the right section using the diagnosis guide below.
Which SSL Error Do You Have? (Find Your Fix Fast)
Not all n8n SSL errors are the same. Find your exact error in the table and jump to the right fix:
| Error Message |
Where It Appears |
Go To |
self-signed certificate in chain |
Terminal / n8n startup |
Sections 2 & 3 |
unable to verify the first certificate |
Terminal / n8n startup |
Sections 2 & 3 |
SELF_SIGNED_CERT_IN_CHAIN on HTTP Request node |
Workflow execution log |
Section 5b |
self-signed certificate during npm install -g n8n |
Terminal during install |
Section 5c |
| SSL error on Postgres / Supabase / MySQL node |
Workflow execution log |
Section 5d |
| SSL error on SMTP / Email Send node |
Workflow execution log |
Section 5e |
If you’re on n8n v1.42.0 or newer running in Docker, skip straight to Section 4b — the /opt/custom-certificates method requires no environment variables at all.
1. Why n8n Throws “certificate not trusted”
If you encounter any proxy configuration error n8n resolve them before continuing with the setup.
What you’ll see – n8n exits with errors such as:
Error: self‑signed certificate in chain
Error: unable to verify the first certificate
Root cause – Node.js cannot verify the TLS chain because the CA that signed the server’s certificate is missing from its trust store.
EEFA: Self‑signed certificates are acceptable only for internal/dev use. Production‑grade deployments should use a trusted CA (Let’s Encrypt, commercial CA) to avoid MITM risks.
2. Import the Self‑Signed CA into the Host Trust Store
If you encounter any docker compose configuration error n8n resolve them before continuing with the setup.
2.1 Linux (Debian/Ubuntu)
Copy the CA bundle and update the system store.
# Copy the CA file
sudo cp my-ca.crt /usr/local/share/ca-certificates/
# Refresh the trust store
sudo update-ca-certificates
2.2 macOS
Add the CA to the system keychain.
sudo security add-trusted-cert \
-d -r trustRoot -k /Library/Keychains/System.keychain \
my-ca.crt
2.3 Windows (PowerShell)
Import the CA into the Local Machine Trusted Root store.
Import-Certificate -FilePath .\my-ca.crt `
-CertStoreLocation Cert:\LocalMachine\Root
Verification – Run the following to confirm the CA is recognized:
openssl s_client -connect <host>:443 -CAfile my-ca.crt
You should see Verify return code: 0 (ok).
3. Tell Node.js to Trust the Self‑Signed CA (OS‑store alternative)
Set NODE_EXTRA_CA_CERTS before starting n8n.
3.1 Bash / Linux / macOS
export NODE_EXTRA_CA_CERTS=/path/to/my-ca.crt
3.2 Windows CMD
set NODE_EXTRA_CA_CERTS=C:\path\to\my-ca.crt
3.3 PowerShell
$env:NODE_EXTRA_CA_CERTS="C:\path\to\my-ca.crt"
EEFA: Never use NODE_TLS_REJECT_UNAUTHORIZED=0 in production—it disables verification entirely.
4. Configure n8n TLS Termination (Self‑Signed Cert)
4.1 Docker Compose – Service Definition
version: '3.8'
services:
n8n:
image: n8nio/n8n
ports:
- "443:5678"
4.2 Docker Compose – TLS & CA Environment
environment:
- N8N_TLS_CERTIFICATE=/certs/server.crt
- N8N_TLS_KEY=/certs/server.key
- NODE_EXTRA_CA_CERTS=/certs/my-ca.crt # optional if OS store used
4.3 Docker Compose – Read‑Only Volume Mount
volumes:
- ./certs:/certs:ro # mount certs read‑only
Tip: Keeping the certs directory read‑only (:ro) prevents accidental modification from inside the container.
4.4 Stand‑Alone npm / yarn Installation – .env File
Create an .env file in the n8n root:
N8N_TLS_CERTIFICATE=/opt/n8n/certs/server.crt
N8N_TLS_KEY=/opt/n8n/certs/server.key
If you rely on NODE_EXTRA_CA_CERTS:
NODE_EXTRA_CA_CERTS=/opt/n8n/certs/my-ca.crt
Start n8n:
npm run start # or simply: n8n start
4.5 Windows Service – Inline Environment
set N8N_TLS_CERTIFICATE=C:\n8n\certs\server.crt
set N8N_TLS_KEY=C:\n8n\certs\server.key
set NODE_EXTRA_CA_CERTS=C:\n8n\certs\my-ca.crt
n8n start
4b. Mount Certificates via /opt/custom-certificates (n8n v1.42.0+ — Easiest Docker Method)
If you’re running n8n v1.42.0 or newer in Docker, this is the cleanest approach. You don’t need any environment variables — n8n automatically detects and trusts every .crt file placed in the /opt/custom-certificates directory on startup.
Step 1 — Create a local folder and copy your CA file into it
mkdir -p ./pki
cp my-ca.crt ./pki/
Step 2 — Mount the folder into your docker-compose.yml
services:
n8n:
image: n8nio/n8n
ports:
- "443:5678"
volumes:
- n8n_data:/home/node/.n8n
- ./pki:/opt/custom-certificates:ro
Step 3 — Fix file permissions (run once after first start)
docker exec n8n chmod -R 644 /opt/custom-certificates
docker restart n8n
Step 4 — Confirm n8n loaded the certificate
docker logs n8n 2>&1 | grep -i cert
You should see a line referencing your certificate file. If you see nothing, the file either has wrong permissions or is not valid PEM format.
Most common failure: Container starts but SSL errors persist even after mounting. This almost always means n8n can’t read the files because they’re owned by root inside the container. The chmod 644 in Step 3 fixes this.
If it still fails, verify your file is valid PEM format: openssl x509 -in ./pki/my-ca.crt -text -noout
Note: This method handles certificates for outbound connections made by n8n’s core. If your workflows make HTTPS calls via the HTTP Request node that still fail, also set NODE_EXTRA_CA_CERTS pointing to the same CA file (see Section 3).
5. Verify the TLS Configuration
5.1 Curl Test (skip hostname verification)
curl -k https://localhost:443/api/health
Expected output – {"status":"ok","version":"X.Y.Z"} (the -k flag bypasses hostname validation but confirms the endpoint is reachable).
5.2 Curl Test (use the CA – no -k)
curl --cacert my-ca.crt https://localhost:443/api/health
Expected output – Same JSON response without -k, proving the CA is trusted.
5.3 n8n HTTP Request Node
Create a simple HTTP Request node targeting https://localhost:443/api/health. A successful execution (no “self‑signed certificate” error) confirms the in‑process trust chain.
| Verification Method |
Expected Result |
| curl -k (skip verification) |
200 OK (does not prove trust) |
| curl –cacert my-ca.crt … |
200 OK without -k |
| n8n HTTP Request node |
No TLS error in execution log |
5b. Fix SSL Errors Inside the HTTP Request Node
If n8n starts fine but you get self-signed certificate in chain when a workflow runs an HTTP Request node, the issue is different — n8n’s internal request engine doesn’t automatically inherit your OS trust store. You need to fix it at the process level.
Fix 1 — Per-node toggle (quick workaround, not recommended for production):
In the HTTP Request node, click Options at the bottom → enable Ignore SSL Issues. This bypasses verification for that specific node only. Use this temporarily to confirm SSL is the actual problem.
Fix 2 — NODE_EXTRA_CA_CERTS (correct long-term fix):
Set NODE_EXTRA_CA_CERTS in your environment before n8n starts (see Section 3 above). This makes every HTTP Request node in every workflow trust your CA automatically — no per-node toggle needed.
Why does this happen even after fixing the startup error? The OS trust store fix (Section 2) makes your system trust the CA. But n8n’s HTTP engine reads NODE_EXTRA_CA_CERTS at process startup — not the OS store. If you only did Section 2 and skipped Section 3, startup works but in-workflow requests still fail.
5c. Fix SSL Errors During npm install -g n8n
If you get a certificate error during the n8n installation itself (not at runtime), it means npm can’t reach the registry over your corporate or self-signed proxy.
Step 1 — Install with strict-ssl disabled (temporary):
npm install -g n8n --strict-ssl=false
Step 2 — Re-enable strict SSL immediately after:
npm config set strict-ssl true
Step 3 — Register your CA with npm permanently so future installs don’t need the flag:
npm config set cafile /path/to/my-ca.crt
5d. Fix SSL Errors on Postgres / Supabase / MySQL Nodes
When a database node throws an SSL error, the fix lives inside the credential — not the server or environment config.
For Postgres / MySQL:
- Go to Credentials → open your Postgres or MySQL credential
- Find the SSL section
- Set SSL to
allow or require
- Under CA Certificate, paste the full contents of your
my-ca.crt file
- Save and re-run the workflow
For Supabase:
Supabase’s connection pooler (port 6543) uses a self-signed certificate in some regions. In your Postgres credential, set the CA Certificate field to Supabase’s root CA — downloadable from your Supabase project under Settings → Database → SSL Certificate.
5e. Fix SSL Errors on SMTP / Email Send Nodes
Corporate SMTP servers (Microsoft Exchange, internal mail relays) frequently use self-signed or internally-signed certificates. If your Email Send node fails with an SSL error:
- In the Email Send node credentials, confirm SSL/TLS is set to
STARTTLS or SSL/TLS depending on what your mail server requires (ask your sysadmin if unsure)
- If the error continues, set
NODE_EXTRA_CA_CERTS to your internal mail server’s CA certificate path (same method as Section 3) and restart n8n
- For completely isolated internal environments with no external exposure, you can set
NODE_TLS_REJECT_UNAUTHORIZED=0 as a last resort — but only in internal-only setups. Never in production.
6. Troubleshooting Checklist
| ✅ Done? |
Step |
| ☐ |
CA file is PEM (-----BEGIN CERTIFICATE-----). |
| ☐ |
NODE_EXTRA_CA_CERTS points to the exact CA file (not a directory). |
| ☐ |
N8N_TLS_CERTIFICATE and N8N_TLS_KEY paths are readable by the n8n process. |
| ☐ |
Docker volume mounts are ro and files exist inside the container (docker exec <id> ls /certs). |
| ☐ |
No conflicting env vars (e.g., NODE_TLS_REJECT_UNAUTHORIZED). |
| ☐ |
After any change, restart the n8n service/container (not just reload). |
| ☐ |
Review logs (docker logs n8n or journalctl -u n8n) for lingering TLS errors. |
If a step fails, logs typically surface the missing file or a verification code such as UNABLE_TO_VERIFY_LEAF_SIGNATURE or SELF_SIGNED_CERT_IN_CHAIN.
7. Production‑Ready Alternatives
| Scenario |
Recommended Action |
| Public‑facing n8n instance |
Use a trusted CA (Let’s Encrypt) and terminate TLS at a reverse proxy (Traefik, Nginx). |
| Multi‑node Kubernetes deployment |
Store certificates in a Secret, mount into the pod, and set NODE_EXTRA_CA_CERTS via a ConfigMap. |
| High‑security environment |
Enable mTLS: add N8N_TLS_CLIENT_CERTIFICATE and N8N_TLS_CLIENT_KEY variables, enforce client verification. |
Frequently Asked Questions
What does “self-signed certificate in chain” mean in n8n?
It means Node.js tried to verify a TLS certificate during an HTTPS connection but couldn’t find a trusted Certificate Authority (CA) that signed it. The certificate exists and is technically valid, but Node.js doesn’t recognise the entity that issued it. The fix is to tell Node.js about your CA using NODE_EXTRA_CA_CERTS or by importing the CA into your OS trust store — not to disable verification entirely.
Should I use NODE_TLS_REJECT_UNAUTHORIZED=0 to fix n8n SSL errors?
No. Setting NODE_TLS_REJECT_UNAUTHORIZED=0 disables all TLS certificate verification globally across the entire n8n process. This silences the error but means n8n will accept any certificate — including fraudulent ones from attackers — making it vulnerable to man-in-the-middle attacks. Use NODE_EXTRA_CA_CERTS instead. It keeps verification fully enabled while adding your specific CA to the trusted list.
Does n8n support custom CA certificates natively?
Yes, since n8n v1.42.0. Mount a folder containing your .crt files to /opt/custom-certificates inside the Docker container. n8n automatically loads all valid PEM certificates in that directory at startup. No environment variables are required. See Section 4b for the full setup.
How do I fix SSL errors in n8n running in Docker?
Two approaches: (1) Set NODE_EXTRA_CA_CERTS=/certs/my-ca.crt in your docker-compose.yml environment block and mount the certs folder as a volume. (2) On n8n v1.42.0+, mount your CA file to /opt/custom-certificates — n8n auto-loads it on startup. Method 2 is cleaner because it requires no environment variable changes and works across all n8n versions that support it.
What is NODE_EXTRA_CA_CERTS and how does it work in n8n?
NODE_EXTRA_CA_CERTS is a Node.js environment variable that points to an additional CA certificate file. When set before n8n starts, Node.js appends that CA to its built-in trust list without replacing any existing trusted CAs. For n8n, this means all outgoing HTTPS connections — workflow HTTP Request nodes, webhooks, external API calls — will trust certificates signed by your CA.
Why does my n8n HTTP Request node still show SSL errors after fixing the startup error?
This is one of the most common points of confusion. Importing the CA into your OS trust store (Section 2) fixes the startup error because n8n’s startup process respects the OS store. But the HTTP Request node’s internal engine reads NODE_EXTRA_CA_CERTS at process startup — not the OS store. If you only did Section 2 and skipped Section 3, startup works but in-workflow HTTPS requests still fail. The fix is to also set NODE_EXTRA_CA_CERTS explicitly in your environment and restart n8n.
Conclusion
Adding the self‑signed CA to the host trust store or exposing it via NODE_EXTRA_CA_CERTS resolves the “certificate not trusted” error by giving Node.js the chain it needs to validate TLS connections. Coupled with proper N8N_TLS_CERTIFICATE and N8N_TLS_KEY settings, n8n starts cleanly and can communicate securely with both internal services and external APIs. For production workloads, migrate to a trusted CA and consider proxy‑based termination or mTLS to meet higher security requirements.