Linux  ·  high  ·  Day-to-day operations

"x509: certificate has expired or is not yet valid" / SSL certificate problem

Either a certificate in the chain really has expired, the machine's clock is wrong, or its CA bundle is too old to contain the issuing root.

What you see

curl, git, package managers and application clients all fail on HTTPS. Browsers on the same network may work fine, which points away from the remote server.

What is actually wrong

An expired leaf or intermediate, a clock far enough out to invalidate a valid certificate, or a ca-certificates package predating the root that signed it.

Codes and articles

x509: certificate has expiredSSL certificate problemcertificate verify failedunable to get local issuer certificateCERTIFICATE_VERIFY_FAILED

Fixes (2)

Fix the clock first
Shell as root15 minuteslow riskreversible

The clock is wrong. Everything else is a waste of time until it is not.

  1. Check the time and sync state.

    Shell
    timedatectl status
  2. Enable network time.

    Shell
    sudo timedatectl set-ntp truesudo systemctl enable --now systemd-timesyncd 2>/dev/null || sudo systemctl enable --now chronyd
  3. Force a sync and confirm.

    Shell
    sudo chronyc makestep 2>/dev/null || sudo systemctl restart systemd-timesyncdtimedatectl status
  4. If the clock resets every boot, the CMOS battery is dead — a VM with no NTP does the same.

  5. Retry the connection.

    Shell
    curl -sS -o /dev/null -w '%{http_code}\n' https://example.com
Confirm it workedtimedatectl reports synchronised and HTTPS works.
Shell
timedatectl show -p NTPSynchronized --value
If you need to undo itsudo timedatectl set-ntp false if the machine must keep its own time for some reason.
Inspect the chain and refresh the CA bundle
Shell as root25 minuteslow riskreversible

The clock is right. Find out which certificate is actually the problem before changing anything.

  1. Read the chain the server presents and every certificate's validity window.

    Shell
    echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -subject -issuer -dates
  2. See the full chain, which is where a missing or expired intermediate shows up.

    Shell
    echo | openssl s_client -connect example.com:443 -servername example.com -showcerts 2>/dev/null | grep -E 's:|i:'

    "unable to get local issuer certificate" almost always means the server is not sending its intermediate, not that your trust store is wrong. That is the server operator's bug.

  3. Update the trust store — an old bundle is the other common cause.

    Shell
    sudo apt-get update && sudo apt-get install --only-upgrade ca-certificates && sudo update-ca-certificates
  4. On RHEL-family the commands differ.

    Shell
    sudo dnf upgrade ca-certificates && sudo update-ca-trust extract
  5. To trust an internal CA, install its root properly rather than disabling verification.

    Shell
    sudo cp internal-ca.crt /usr/local/share/ca-certificates/sudo update-ca-certificates

    curl -k and GIT_SSL_NO_VERIFY make the error disappear by switching off the check that was protecting you. They are for one-off diagnosis, never for a fix.

  6. Verify against the store explicitly.

    Shell
    openssl verify -CApath /etc/ssl/certs <(echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509)
Confirm it workedcurl succeeds without -k.
Shell
curl -sS -o /dev/null -w '%{http_code}\n' https://example.com
If you need to undo itRemove the certificate from /usr/local/share/ca-certificates and re-run update-ca-certificates.

Where this stops. This write-up was written and checked by hand. It says what each step changes, how to confirm it worked and how to reverse it, and anything destructive is flagged before you reach it. If it does not match what your machine is doing, search the Support Centre for the exact code or message — and when something needs a person, get in touch.