Troubleshooting PHP cURL Error 60: SSL Certificate Problem: Unable to Get Local Issuer Certificate

By Admin • Updated September 4, 2026 • 140 Reads
Fix PHP cURL error 60 SSL certificate problem unable to get local issuer certificate
PHP cURL Error 60 SSL Certificate Resolution

You are in the middle of building a checkout flow with Stripe or PayPal, integrating WhatsApp Cloud API, or testing OpenAI endpoints in your PHP application. You fire your first HTTP test request, and everything crashes with an abrupt fatal exception: cURL error 60: SSL certificate problem: unable to get local issuer certificate. On Windows development stacks (like XAMPP, WAMP, Laragon, or custom IIS setups), this error halts all outbound HTTPS communication.

In this comprehensive troubleshooting guide, we will walk through the internal mechanics of the SSL/TLS certificate verification chain, explain why local PHP installations cannot verify trusted issuers out of the box, demonstrate why the common StackOverflow "quick fix" creates severe security liabilities, and provide a tested, step-by-step resolution for Windows, cPanel, and Linux environments.

1. How the TLS/SSL Certificate Chain of Trust Works

When your PHP script initiates an HTTPS request using libcurl, it does not simply transmit data blindly down a network wire. It engages in a strict cryptographic handshake to verify that the remote server is genuine and that the connection cannot be intercepted or tampered with by an eavesdropper.

Here is how the trust validation process unfolds behind the scenes:

  1. Server Certificate Delivery: The remote server (such as api.stripe.com) sends its public digital SSL/TLS certificate to your PHP script.
  2. Intermediate Certificate Chain: That server certificate was digitally signed by an intermediate Certificate Authority (CA), which was signed by a globally trusted Root Certificate Authority (such as DigiCert, Let's Encrypt, or Sectigo).
  3. Local Issuer Inspection: To verify this signature chain, your local server environment must compare the root signature against a local database of known, verified Root Certificate Authorities (known as a CA certificate bundle).

If your PHP installation cannot find a valid CA bundle on your local hard drive—or if your php.ini directives point to an empty or non-existent path—cURL cannot complete the third step. Because it cannot prove the remote server is who it claims to be, cURL terminates the network connection immediately with Error 60: Unable to get local issuer certificate.

Critical Security Warning: Never Disable SSL Verification in Production

When developers search for cURL error 60, hundreds of forum threads suggest adding these two lines:

// NEVER USE THIS IN PRODUCTION CODE:
curl_setopt(\$ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt(\$ch, CURLOPT_SSL_VERIFYHOST, 0);

While this silences the error message, it completely disables encryption verification. It instructs your PHP backend to transmit secret API keys, credit card tokens, and customer passwords to any server that responds, making your application completely vulnerable to Man-in-the-Middle (MITM) attacks, DNS poisoning, and public Wi-Fi eavesdropping. The proper solution is to install the verified root CA bundle.

2. Why Windows, XAMPP, and WAMP Suffer from This Error

On Linux operating systems (like Ubuntu, Debian, and CentOS), trusted root certificates are managed system-wide by the OS package manager (under /etc/ssl/certs/ca-certificates.crt). When PHP runs on Linux, it automatically reads the operating system's global certificate store.

Windows, however, stores its certificates in the binary Windows Certificate Store (accessible via certmgr.msc). Because the open-source OpenSSL and libcurl libraries compiled into PHP cannot natively query the Windows registry store, PHP requires a standalone plain-text PEM certificate file on disk. Most local Windows development packages (including XAMPP and WAMP) ship without this file pre-configured, resulting in cURL error 60 on the very first API call.

3. Complete Step-by-Step Fix for Windows (XAMPP & WAMP)

Step 1: Download the Official Mozilla Root CA Bundle

The official cURL maintainers periodically extract the full database of trusted root certificates approved by Mozilla. You can download the latest verified bundle directly from the official cURL distribution site:

Save the cacert.pem file into a permanent, non-temporary directory inside your PHP installation. Recommended standard paths:

  • XAMPP: C:\xampp\php\extras\ssl\cacert.pem
  • WAMP: C:\wamp64\bin\php\php8.2.x\extras\ssl\cacert.pem
  • Laragon: C:\laragon\bin\php\php-8.2.x\cacert.pem

Step 2: Update php.ini Directives

Locate your active php.ini configuration file. If you are unsure where your active php.ini resides, run this command in your command prompt or terminal:

php --ini

Open php.ini in your editor (such as VS Code or Notepad++) and search for the following two configuration keys:

  1. curl.cainfo (under the [curl] section)
  2. openssl.cafile (under the [openssl] section)

Uncomment both directives by removing any leading semicolon (;) and provide the absolute path to your downloaded cacert.pem file:

[curl]
; A default value for the CURLOPT_CAINFO option. This is required
; for cURL calls over HTTPS to external APIs.
curl.cainfo = "C:/xampp/php/extras/ssl/cacert.pem"

[openssl]
; The location of a Certificate Authority (CA) file on the local machine
; used to verify peer certificates with OpenSSL functions.
openssl.cafile = "C:/xampp/php/extras/ssl/cacert.pem"

Important Syntax Note: In php.ini on Windows, always use forward slashes (/) or escaped double backslashes (C:\\xampp\\...). Using unescaped single backslashes may cause PHP to misinterpret characters like \p or \e as escape codes.

Step 3: Restart Apache or Your PHP Service

For PHP to reload its configuration, you must restart your web server process:

  • XAMPP Users: Open the XAMPP Control Panel. Click Stop next to Apache, wait 3 seconds, and click Start.
  • WAMP Users: Left-click the WampServer system tray icon and select Restart All Services.
  • CLI Workers: If you are running PHP scripts via terminal or CLI, simply close and re-open your terminal window.

4. Fixing cURL Error 60 on Linux (Ubuntu, Debian, CentOS, RHEL)

If this error occurs on a Linux server or cloud VPS instance, it indicates your operating system's certificate authority packages are out of date or have become corrupted. Run the following commands to update and regenerate the system bundle:

On Ubuntu and Debian:

# Update package list and reinstall ca-certificates
sudo apt-get update
sudo apt-get install --reinstall ca-certificates

# Force regeneration of the trusted certificates symlinks
sudo update-ca-certificates --fresh

# Restart PHP-FPM and your web server
sudo systemctl restart php8.2-fpm
sudo systemctl restart nginx # or sudo systemctl restart apache2

On CentOS, AlmaLinux, Rocky Linux, or RHEL:

sudo yum reinstall ca-certificates
sudo update-ca-trust force-enable
sudo update-ca-trust extract
sudo systemctl restart php-fpm
sudo systemctl restart httpd

5. Verification: Writing a Robust Production cURL Diagnostic Script

To verify that your PHP installation can now perform verified, encrypted handshakes without any security compromises, save and run this diagnostic test script:

<?php
// curl_diagnostic_test.php
header('Content-Type: application/json');

\$test_endpoint = 'https://api.github.com/zen';

\$ch = curl_init(\$test_endpoint);
curl_setopt_array(\$ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT        => 10,
    CURLOPT_USERAGENT      => 'AOneInfo-SSL-Verifier/1.0',
    // Strict SSL Peer & Host verification:
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_SSL_VERIFYHOST => 2,
]);

\$response = curl_exec(\$ch);
\$err_num  = curl_errno(\$ch);
\$err_msg  = curl_error(\$ch);
\$http_code = curl_getinfo(\$ch, CURLINFO_HTTP_CODE);
curl_close(\$ch);

if (\$err_num === 0 && \$http_code === 200) {
    echo json_encode([
        'status'         => 'SUCCESS',
        'message'        => 'SSL Handshake verified successfully with trusted Certificate Authority!',
        'http_status'    => \$http_code,
        'remote_payload' => \$response
    ], JSON_PRETTY_PRINT);
} else {
    echo json_encode([
        'status'       => 'ERROR',
        'curl_errno'   => \$err_num,
        'curl_message' => \$err_msg,
        'http_status'  => \$http_code
    ], JSON_PRETTY_PRINT);
}

When you run this script, seeing "status": "SUCCESS" confirms that your PHP engine has successfully loaded the trusted Certificate Authority bundle. You can now connect securely to Stripe, PayPal, Google Cloud, and any external REST API with total peace of mind.