WordPress at Your Fingertips

How to Fix the WordPress Error 60: SSL certificate problem: certificate has expired

From September 30, 2021, HTTP API requests may stop working on WordPress sites that have a Let's Encrypt certificate installed.

For example, a request like this will result in an error:

$res = wp_remote_get( 'https://wp-kama.com/' );

if( is_wp_error( $res ) ){
	echo $res->get_error_message();
}

We get it:

cURL error 60: SSL certificate problem: certificate has expired

Such an error can be seen anywhere, for example:

  • In the admin panel when checking for WordPress updates.
  • When checking for plugin updates.
  • When accessing the api of any service. For example, will stop working plugin TinyPNG - JPEG, PNG & WebP image compression and a bunch of others that use any kind of requests.

Why do we see the error certificate has expired?

In brief, WP core has a file of root certificates /wp-includes/certificates/ca-bundle.crt which is used to check SSL of all requests created through HTTP API. In this file one of the root certificates has expired which was used to create a certificate for your site. Therefore, the request can not pass verification and generates this error.

More detailed explanation:

Without going into too much detail, a couple of words for non-specialists as to why the expiration of a DST Root CA X3 certificate will affect certificates issued by Let's Encrypt. Each system that validates a certificate has its own repository of trusted root certificates. The system will trust certificates that are signed using the private key of one of these root certificates during validation. Root certificates themselves normally have long expiration dates, are seldom changed and are not used when generating end subject certificates (in this case domain name certificates), instead a public key infrastructure implies using chains of trust - root certificates are used to sign intermediate certificates and already using them to sign end subject certificates (domain certificates). Furthermore, in order for a system to trust an end subject certificate, it must be able to trace the full chain from this certificate to one of the root certificates it trusts.

When Let's Encrypt appeared, its ISRG Root X1 root certificate (like any new root certificate) could not quickly find its way into the trusted certificate stores of a significant number of systems. At the same time, for the successful functioning of the project the issued certificates from the very beginning had to be trusted by the maximum number of systems "out of the box" (without any additional actions from the users of these systems). In this regard, for certificates Let's Encrypt began to use the chain of trust leading to the root certificate DST Root CA X3, which is recognized by most systems.

With the next WP update this error disappear, but here's what to do if you need the solution today, or if you do not plan to update WordPress, but need working HTTP requests.

Solution: cURL error 60: SSL certificate has expired

You need to update the content of /wp-includes/certificates/ca-bundle.crt file. You need to change it to the content of the https://curl.se/ca/cacert.pem file.

Changing the core file in this case is acceptable, because the next time you update WP, the problem will go away. See corresponding commit on GitHub.

It can be done manually

  1. Download this file https://curl.se/ca/cacert.pem.

  2. Update content of /wp-includes/certificates/ca-bundle.crt with content from the downloaded file.

  3. DONE! Everything should now work as before.

Or use the following code to perform this operation programmatically

Using the code is handy when you have the ability to run the code from the admin panel, for example using Code Snippets plugin.

  1. Add the following code into the themes functions.php file (or in Code Snippets plugin):

    /**
     * Goto http://yoursite.com/?update-wp-ca-bundle
     */
    if( isset( $_GET['update-wp-ca-bundle'] ) ){
    
    	$crt_file = ABSPATH . WPINC . '/certificates/ca-bundle.crt';
    	$new_crt_url = 'http://curl.haxx.se/ca/cacert.pem';
    
    	if( is_writable( $crt_file ) ){
    		$new_str = file_get_contents( $new_crt_url );
    
    		if( $new_str && strpos( $new_str, 'Bundle of CA Root Certificates' ) ){
    			$up = file_put_contents( $crt_file, $new_str );
    
    			echo $up ? 'OK: ca-bundle.crt updated' : 'ERROR: can`t put data to ca-bundle.crt';
    		}
    		else {
    			echo 'ERROR: can\'t download curl.haxx.se/ca/cacert.pem';
    		}
    	}
    	else {
    		echo 'ERROR: ca-bundle.crt not writable';
    	}
    
    	exit;
    }

    After use, delete this code.

  2. Visit http://YOURSITE.com/?update-wp-ca-bundle the page.

    Replace YOURSITE.com to your domain...

  3. DONE! Everything should now work as before.
7 comments
    Log In