title: Axios/HTTPS certificate expired error only on Windows
body: <p>I am currently working on a VSCode extension that internally makes some API calls to a HTTPS protected endpoint. On Linux and Mac OS the extension works as expected but on Windows machines <code>axios</code>, the internal HTTP client used to make the API calls, is rejecting those requests due to the certificates being expired. When I access the API endpoint though via Firefox, Chrome and even Edge the certificates seems find.</p>
  <p>I have upgraded Node to 16.14.0 and also to 17.6.0 but the problem still remains. As the API is only accessible through our VPN, with my VPN activated of course, I used <a href="https://testssl.sh/" rel="nofollow noreferrer">testssl.sh</a> to verify that the whole trust-chain is still valid:</p>
  <pre><code> Testing protocols via sockets except NPN+ALPN                                                                                                                                
                                                                                                                                                                                
   SSLv2      not offered (OK)                                                                                                                                                  
   SSLv3      not offered (OK)                                                                                                                                                  
   TLS 1      not offered                                                                                                                                                       
   TLS 1.1    not offered                                                                                                                                                       
   TLS 1.2    offered (OK)                                                                                                                                                      
   TLS 1.3    offered (OK): final                                                                                                                                               
   NPN/SPDY   not offered                                                                                                                                                       
   ALPN/HTTP2 h2, http/1.1 (offered)                                                                                                                                            
                                                                                                                                                                                
   Testing cipher categories                                                                                                                                                    
                                                                                                                                                                                
   NULL ciphers (no encryption)                  not offered (OK)                                                                                                               
   Anonymous NULL Ciphers (no authentication)    not offered (OK)                                                                                                               
   Export ciphers (w/o ADH+NULL)                 not offered (OK)                                                                                                               
   LOW: 64 Bit + DES, RC[2,4] (w/o export)       not offered (OK)                                                                                                               
   Triple DES Ciphers / IDEA                     not offered                                                                                                                    
   Obsolete CBC ciphers (AES, ARIA etc.)         offered                                                                                                                        
   Strong encryption (AEAD ciphers)              offered (OK)                                                                                                                   
  
  
   Testing robust (perfect) forward secrecy, (P)FS -- omitting Null Authentication/Encryption, 3DES, RC4 
  
   PFS is offered (OK)          TLS_AES_256_GCM_SHA384 TLS_CHACHA20_POLY1305_SHA256 ECDHE-RSA-AES256-GCM-SHA384 ECDHE-RSA-AES256-SHA384 ECDHE-RSA-AES256-SHA
                                DHE-RSA-AES256-GCM-SHA384 DHE-RSA-AES256-SHA256 DHE-RSA-AES256-SHA TLS_AES_128_GCM_SHA256 ECDHE-RSA-AES128-GCM-SHA256 ECDHE-RSA-AES128-SHA256
                                ECDHE-RSA-AES128-SHA DHE-RSA-AES128-GCM-SHA256 DHE-RSA-AES128-SHA256 DHE-RSA-AES128-SHA 
   Elliptic curves offered:     prime256v1 secp384r1 secp521r1 X25519 X448 
   DH group offered:            HAProxy (2048 bits)
  
   Testing server preferences 
  
   Has server cipher order?     yes (OK) -- TLS 1.3 and below
   Negotiated protocol          TLSv1.3
   Negotiated cipher            TLS_AES_256_GCM_SHA384, 253 bit ECDH (X25519)
   Cipher order
      TLSv1.2:   ECDHE-RSA-AES128-GCM-SHA256 ECDHE-RSA-AES256-GCM-SHA384 DHE-RSA-AES128-GCM-SHA256 DHE-RSA-AES256-GCM-SHA384 ECDHE-RSA-AES128-SHA256 ECDHE-RSA-AES128-SHA
                 ECDHE-RSA-AES256-SHA384 ECDHE-RSA-AES256-SHA DHE-RSA-AES128-SHA256 DHE-RSA-AES128-SHA DHE-RSA-AES256-SHA256 DHE-RSA-AES256-SHA 
      TLSv1.3:   TLS_AES_256_GCM_SHA384 TLS_CHACHA20_POLY1305_SHA256 TLS_AES_128_GCM_SHA256 
  ...
   Certificate Validity (UTC)   65 &gt;= 30 days (2022-02-01 08:18 --&gt; 2022-05-02 08:18)
  ...
  </code></pre>
  <p>The other certificates in the trustchain are also valid and not expired at all.</p>
  <p>Axios is used within an own <code>HttpClient</code> class that looks like this:</p>
  <pre class="lang-js prettyprint-override"><code>export class HttpClient {
  
      private readonly instance: AxiosInstance;
  
      constructor() {
          this.instance = axios.create();
      }
  
      async asyncGet&lt;T&gt;(url: string): Promise&lt;Response&lt;T&gt;&gt; {
          return this.instance.get&lt;T&gt;(url)
              .then(axiosResponse =&gt; {
                  ...
              })
              .catch(error =&gt; {
                  console.error(error);
                  throw error;
              });
      }
  }
  </code></pre>
  <p>I also tried the <code>https</code></p>
  <pre class="lang-js prettyprint-override"><code>const options: https:RequestOptions = {
      hostname: '...',
      port: 443,
      path: '/...',
      method: 'GET'
  };
  const request = https.request(url, options, (res: IncomingMessage) =&gt; {
      console.log(`statusCode: ${res.statusCode ? res.statusCode : 'undefined'}`);
      res.on('data', d =&gt; {
          process.stdout.write(d as string);
      }
  });
  request.on('error', error =&gt; {
      console.error(error);
  };
  request.end();
  </code></pre>
  <p>analog to the samples given in the <a href="https://nodejs.dev/learn/making-http-requests-with-nodejs" rel="nofollow noreferrer">NodeJS documentation</a> and <code>tls</code></p>
  <pre class="lang-js prettyprint-override"><code>const socket = tls.connect(443, hostname, undefined, () =&gt; {
      console.log('Connected to ' + hostname);
      console.log('TLS.connect', socket.authorized ? 'authorized' : 'unauthorized');
      console.log('Cipher: ' + JSON.stringify(socket.getCipher()));
      console.log('Protocol: ' + JSON.stringify(socket.getProtocol()));
      const peerCert: tls.DetailedPeerCertificate = socket.getPeerCertificate(true);
      console.log(`Peer-Cert ${JSON.stringify(peerCert.subject)} - valid from: ${peerCert ? peerCert.valid_from : 'invalid'} valid till: ${peerCert ? peerCert.valid_to : 'invalid'}`);
      const issuerCert = peerCert.issuerCertificate;
      console.log(`issuer-Cert ${JSON.stringify(issuerCert.subject)} - valid from: ${issuerCert ? issuerCert.valid_from : 'invalid'} valid till: ${issuerCert ? issuerCert.valid_to : 'invalid'}`);
      const rootCert = issuerCert.issuerCertificate;
      console.log(`root-Cert ${JSON.stringify(rootCert.subject)} - valid from: ${rootCert ? rootCert.valid_from : 'invalid'} valid till: ${rootCert ? rootCert.valid_to : 'invalid'}`);
  });
  </code></pre>
  <p>as proposed in <a href="https://stackoverflow.com/a/48549393/1377895">this answer</a>.</p>
  <p>Both <code>axios</code> and <code>https</code> return an error like this:</p>
  <pre class="lang-json prettyprint-override"><code>{
    &quot;message&quot;: &quot;certificate has expired&quot;,
    &quot;name&quot;: &quot;Error&quot;,
    &quot;stack&quot;: &quot;Error: certificate has expired\n\tat TLSSocket.onConnectSecure (_tls_wrap.js:1497:34)\n\tat TLSSocket.emit (events.js:315:20)\n\tat TLSSocket._finishInit (_tls_wrap.js:932:8)\n\tat TLSWrap.onhandshakedone (_tls_wrap.js:706:12)\n\tat TLSWrap.callbackTrampoline (internal/async_hooks.js:131:14)&quot;,
    &quot;config&quot;: {
      &quot;transitional&quot;: {
        &quot;silentJSONParsing&quot;: true,
        &quot;forcedJSONParsing&quot;: true,
        &quot;clarifyTimeoutError&quot;: false
      },
      &quot;transformRequest&quot;: [
        &quot;null&quot;
      ],
      &quot;transformResponse&quot;: [
        &quot;null&quot;
      ],
      &quot;timeout&quot;: 0,
      &quot;xsrfCookieName&quot;: &quot;XSRF-TOKEN&quot;,
      &quot;xsrfHeaderName&quot;: &quot;X-XSRF-TOKEN&quot;,
      &quot;maxContentLength&quot;: -1,
      &quot;maxBodyLength&quot;: -1,
      &quot;headers&quot;: {
        &quot;Accept&quot;: &quot;application/json, text/plain, */*&quot;,
        &quot;user-Agent&quot;: &quot;axios/0.26.0&quot;
      },
      &quot;method&quot;: &quot;get&quot;,
      &quot;url&quot;: &quot;https://...&quot;
    },
    &quot;code&quot;: &quot;CERT_HAS_EXPIRED&quot;,
    &quot;status&quot;: null
  }
  </code></pre>
  <p>with a more human-readable stacktrace:</p>
  <pre><code>Error: certificate has expired
      at TLSSocket.onConnectSecure (_tls_wrap.js:1497:34)
      at TLSSocket.emit (events.js:315:20)
      at TLSSocket._finishInit (_tls_wrap.js:932:8)
      at TLSWrap.onhandshakedone (_tls_wrap.js:706:12)
      at TLSWrap.callbackTrampoline (internal/async_hooks.js:131:14)
  </code></pre>
  <p>while for <code>tls</code> I get the following output:</p>
  <pre><code>Connected to ...
  TLS.connect authorized
  Cipher: {&quot;name&quot;:&quot;TLS_CHACHA20_POLY1305_SHA256&quot;,&quot;standardName&quot;:&quot;TLS_CHACHA20_POLY1305_SHA256&quot;,&quot;version&quot;:&quot;TLSv1/SSLv3&quot;}
  Protocol: &quot;TLSv1.3&quot;
  Peer-Cert {&quot;CN&quot;: &quot;*....&quot;} - valid from: Feb  1 08:18:23 2022 GMT valid till: May  2 08:18:22 2022 GMT
  issuer-Cert {&quot;C&quot;:&quot;US&quot;,&quot;O&quot;:&quot;Let's Encrypt&quot;,&quot;CN&quot;:&quot;R3&quot;} - valid from Sep  4 00:00:00 2020 GMT valid till: Sep 15 16:00:00 2025 GMT
  root-Cert {&quot;C&quot;:&quot;US&quot;,&quot;O&quot;:&quot;Internet Security Research Group&quot;,&quot;CN&quot;:&quot;ISRG Root X1&quot;} - valid from: Jan 20 19:14:03 2021 GMT valid till: Sep 30 18:14:03 2024 GMT
  </code></pre>
  <p>So <code>tls</code> seems to be able to connect to the API server and perform the SSL/TLS handshake, but <code>https</code> and <code>axios</code> somehow fail.</p>
  <p>I also stumbled upon <a href="https://stackoverflow.com/questions/69403020/axios-certificate-has-expired-on-valid-certificate">this question</a> here, which seems to be related, but as I am already on the latest NodeJS release (as well as any dependency used in the extension is on the most recent version) and this error only occurs on Windows (mostly 10, unsure if and how many users actually use Windows 11) machines I think the question deserves its own spot here on SO.</p>
  <p>In order to rule out a lack of common supported ciphers between the Windows and Node.js based <code>tls</code> implementation and the nginx managed server side, I also checked the available ciphers in Node via <code>node -p crypto.constants.defaultCoreCipherList</code> which returns a list like this:</p>
  <pre><code>TLS_AES_256_GCM_SHA384
  TLS_CHACHA20_POLY1305_SHA256
  TLS_AES_128_GCM_SHA256
  ECDHE-RSA-AES128-GCM-SHA256
  ECDHE-ECDSA-AES128-GCM-SHA256
  ECDHE-RSA-AES256-GCM-SHA384
  ECDHE-ECDSA-AES256-GCM-SHA384
  DHE-RSA-AES128-GCM-SHA256
  ECDHE-RSA-AES128-SHA256
  DHE-RSA-AES128-SHA256
  ECDHE-RSA-AES256-SHA384
  DHE-RSA-AES256-SHA384
  ECDHE-RSA-AES256-SHA256
  DHE-RSA-AES256-SHA256
  HIGH
  !aNULL
  !eNULL
  !EXPORT
  !DES
  !RC4
  !MD5
  !PSK
  !SRP
  !CAMELLIA
  </code></pre>
  <p>which indicates that enough ciphers would overlap between client and server.</p>
  <p>Why do I still get a <code>certificate expired</code> error on Windows machines with axios/https
  when Linux and MacOS work just fine with these settings and <code>tls</code> is able to connect to the remote API sucessfully even on Windows machines?</p>