Skip to content
ssldays

Guide

Electron app TLS certificate errors

Electron certificate expired errors appear when Chromium rejects notAfter on your API HTTPS endpoint. Verify live TLS on the API host, renew, reload, and retest without bypassing webSecurity.

Updated May 2, 2026 802 words

An electron certificate expired symptom is Chromium inside your desktop shell blocking fetch, axios, or BrowserWindow loads with NET::ERR_CERT_DATE_INVALID or certificate errors—while a misconfigured dev build might still “work” with bad TLS settings.

Desktop releases outlive web deploys. Users on older app versions continue calling the same API hostname; if that listener’s leaf passes notAfter, every installed binary fails at once without an app store redeploy.

Electron inherits Chrome certificate validation, not Node’s NODE_EXTRA_CA_CERTS alone for renderer traffic. Separate main-process Node HTTPS from webContents loads when triaging.

First response

Record Electron version, app.getVersion(), target URL, and whether the failure is in a BrowserWindow, webview, or main-process net.request. Screenshot the error code (ERR_CERT_*) from devtools if enabled.

Collect whether webSecurity is disabled or certificate-error handlers call event.preventDefault()—those mask production risk. Capture OS date and timezone; Chromium compares UTC notAfter.

Reproduce in production mode (electron . with packaged asar) because dev servers often use https://localhost with different trust rules. Note corporate proxy or Zscaler on the affected machine.

If the shell loads third-party SDKs in nested webview tags, record each origin separately—analytics or auth iframes can fail TLS while your API works. Electron session.clearCache() does not substitute for server renewal.

Capture whether app.commandLine.appendSwitch('ignore-certificate-errors') appears in main-process startup code paths shipped to beta channels.

What this error means

TLS in the renderer uses the same PKIX path as Chrome. After the server sends certificates, Chromium validates signatures, builds a chain to the platform trust store, checks temporal validity, then compares SAN/CN to the host in the URL bar equivalent.

Expired leaf or intermediate fails before JavaScript runs. Custom protocols (app://) are separate; https://api failures are almost always server-presented material.

Preload scripts do not bypass TLS. Main process can use Node https with custom ca—a common footgun where renderer fails but a health check script passes with bundled corporate roots.

Related mobile patterns appear in Flutter certificate verify failed incidents; root cause is still listener TLS.

net.request and renderer fetch can diverge when you set session.setCertificateVerifyProc in main process. Document any custom verify proc returning 0 for known fingerprints—after legitimate renewal with new key, those procs must update pins or all channels fail despite valid public dates.

Offline-first apps queuing requests while laptops sleep may burst failures when waking after cert expiry midnight UTC. Support logs showing simultaneous errors across customers usually indicate server-side notAfter, not client corruption.

Common causes

API gateway cert renewed but CDN edge still serves previous notAfter. Staging certificate accidentally pointed at by production VITE_API_URL in a shipped .env baked into the build.

Corporate MDM installs extra roots on laptops while customer machines use default trust—your QA passes, users fail until you serve a public chain.

Self-signed or internal CA used during development leaked into electron-builder environment files for a channel that end users download.

certificate-error handler permanently accepts an old certificate object; after renewal, some Electron versions cache bad associations until app restart—still, fix the server first.

Partial intermediate chain after nginx ssl_certificate update without fullchain path serves an expired cross-sign nobody notices until Android and Electron align on stricter chain building.

Auto-update channels and code signature CRLs are unrelated to API TLS but surface at the same release freeze. Assign one engineer to API listener certs while another validates electron-updater feed URLs so certificate work on the product API is not delayed by unrelated packaging tasks.

Confirm from the outside

OpenSSL on the API host users call

API=api.yourapp.com
echo | openssl s_client -connect "$API:443" -servername "$API" 2>/dev/null \
  | openssl x509 -noout -dates -subject -issuer

curl from a neutral network

curl -vI --connect-timeout 10 "https://$API/health" 2>&1 | grep -Ei 'expire|SSL certificate'

Electron main-process probe (temporary diagnostic)

const { net } = require("electron");
const req = net.request("https://api.yourapp.com/health");
req.on("response", (res) => console.log(res.statusCode));
req.on("error", (err) => console.error(err));
req.end();

Compare renderer fetch failure with main-process net and external curl. Divergence hints at custom CA only in one path.

DevTools

Load the URL in the window, open devtools Network, inspect TLS connection info if available; note certificate dates in the viewer.

Fix checklist

  1. Confirm expired material on the public API hostname with OpenSSL from outside VPN.
  2. Renew on the HTTPS terminator for that hostname including full intermediate chain.
  3. Reload or deploy edge configs; purge CDN custom cert slots if used.
  4. Remove production use of certificate-error bypass handlers except tightly scoped dev builds.
  5. Ship no ignore-certificate-errors switches in packaged production binaries.
  6. Re-run curl, then Electron production build, from a clean user profile machine.
  7. Update release notes if users must upgrade for unrelated API URL changes—TLS fix needs no client update when hostname unchanged.
  8. Add API hostname to live TLS monitoring.

Package production builds with environment-specific API base URLs from secure CI variables, not checked-in .env.production files copied from developer laptops. After server renewal, run smoke tests on Windows and macOS installer artifacts because platform trust stores differ even when the API hostname is shared.

If you terminate TLS on localhost in dev using webpack-dev-server HTTPS, document that those certificates must never be referenced by staging URLs embedded in QA builds. Separate signing identities reduce the chance that an internal mkcert root appears in customer crash dumps labeled as expired.

Support playbooks should ask users for the app version and OS build before assuming a server side expiry, but when OpenSSL already shows a past date, skip lengthy client cleanup steps. After renewal, have support trigger one forced reload in the app window rather than asking users to clear all browsing data, which does not fix server certificates anyway. Document the API hostname you renewed in the customer facing status page so desktop and web teams align on scope.

When to use automation

Desktop clients do not warn operators before expiry; they fail overnight for all installs. Monitor the API hosts your Electron shell calls with external TLS checks on the same SAN customers resolve.

Combine cert expiry alerts with staging channels that hit pre-production hostnames, so bad deploys surface before stable auto-update. RDAP on marketing domains helps when API certs depend on domain-validated reissues.

Desktop telemetry buckets TLS errors separately from HTTP 4xx—wire dashboards so certificate incidents page platform owners before support volume spikes. Include updater feed URLs and deep-link API hosts in the same monitor group; marketing site renewals do not cover them. Nightly packaged builds against production API sandboxes catch expiry regressions before store submission.

Frequently asked questions

Can I bundle my own CA in the app?

You may ship corporate roots for intentional private APIs; public SaaS should use publicly trusted chains instead of per-app CA pinning unless you operate a deliberate pinning program with rotation discipline.

Why does Chrome work but Electron fails?

Different URLs, proxies, or extensions. Compare exact hostname and whether Electron uses system proxy settings (session.setProxy). Also check clock skew.

Is `webSecurity: false` a workaround?

It weakens same-origin protections and does not consistently skip cert validation for all APIs. It is not an acceptable production fix for expiry.

Do auto-updaters use separate TLS?

Yes—electron-updater hits release endpoints with their own certificates. Failures there block patches even when your product API works; monitor those hostnames too.

See what the server presents right now

Free live certificate check — no signup.