# spatie/ssl-certificate and reading the properties of a TLS certificate in PHP

> This small PHP package fetches an SSL certificate by host name, from a file or from a string, and turns it into queryable properties: expiry dates as Carbon instances, issuer, algorithms, key size and fingerprints. The important caveat is in the package's own notice, that it does not check whether a certificate is signed by a trusted authority.

**spatie/ssl-certificate** — A class to validate SSL certificates

- Repository: https://github.com/spatie/ssl-certificate
- Website: https://spatie.be/open-source
- Stars: 742 · Forks: 137
- Language: PHP
- License: MIT
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/spatie-ssl-certificate

## Three ways in, one object out

The package is a single class, `Spatie\SslCertificate\SslCertificate`, and the whole API is the query methods on the instance it returns. The README puts it plainly: the class makes it easy to query the properties on an SSL certificate, and there are three options for fetching one.

```php
use Spatie\SslCertificate\SslCertificate;

// fetch the certificate using an url
$certificate = SslCertificate::createForHostName('spatie.be');

// or from a certificate file
$certificate = SslCertificate::createFromFile($pathToCertificateFile);

// or from a string
$certificate = SslCertificate::createFromString($certificateData);
```

That third option is the one that makes the package more useful than a one trick fetcher. Reading a certificate from a file or from a string means you can inspect certificates that are not attached to a live host: a certificate shipped with a deployment, one stored in a secret manager, one from a certificate chain in an internal PKI, or one you captured earlier. The same query methods apply to all three sources.

The instance is not a data object you read fields from. It is an object with behaviour, and the methods are grouped by what they answer. Validity is `isValid()`, which returns true if the certificate is currently valid. Dates come as Carbon instances from `expirationDate()` and `validFromDate()`, and there are two derived durations: `daysUntilExpirationDate()` for the gap between today and expiry, and `lifespanInDays()` for the gap between the valid-from and expiration dates.

The separation between the two durations is the detail that makes this class pleasant to use in a monitoring job. The lifespan is a property of the certificate, and the days remaining is a property of today, so a job that alerts on `daysUntilExpirationDate()` answers a question about the world, not about the certificate.

## The fluent download form exists for hosts you cannot resolve normally

The named constructor takes a host name, and it takes a port either in the host string or as a separate argument. The README shows both:

```php
$certificate = SslCertificate::createForHostName('spatie.be:443');
```

and describes this as a fluent style for specifying a specific port to connect to, which is a slightly confusing description because the fluent form is a separate API rather than a variation of the same call:

```php
SslCertificate::download()
   ->usingPort($customPort)
   ->forHost($hostName);
```

The fluent builder is where the interesting cases live. It can pin the connection to a particular IP address while still asking for a certificate belonging to a different host name, which is the standard technique for checking what certificate a server presents when DNS points somewhere else:

```php
SslCertificate::download()
   ->fromIpAddress($ipAddress)
   ->forHost($hostName);
```

And the same call works with an IPv6 address, which the README points out explicitly:

```php
SslCertificate::download()
    ->fromIpAddress('2a00:1450:4001:80e::200e')
    ->forHost('google.com');
```

That combination, a different IP address and a different host name, is also how you make the request ask for the right certificate when a server is behind a load balancer or a CDN that terminates TLS on a shared address.

The builder also accepts socket context options, which are passed to the underlying stream context:

```php
SslCertificate::download()
   ->withSocketContextOptions([
      'option' => 'value',
   ])
   ->forHost($hostName);
```

The README does not enumerate which options are useful here, so the practical value of that method is for a reader who already knows which socket option they need, such as a peer name override or a verification flag.

## The package does not check whether a certificate is trusted

This is the notice that should come before any other consideration of this library, and it is the package's own wording.

Currently, this package does not check if the certificate is signed by a trusted authority. We'll add this check soon in a next point release.

So the class will happily parse a self signed certificate, a certificate signed by an authority your server does not trust, an expired one, or a certificate for a different domain, and none of those facts will be visible through `isValid()`. What `isValid()` means is narrower than the word suggests: the documentation states it returns true if the current date and time is between `validFromDate` and `expirationDate`. That is a date range check, nothing more.

There is one escape hatch for the expiry case, and it is documented. If you want to download certificates even if they are invalid, for example if they are expired, you can pass a `$verifyCertificate` boolean to the constructor as the third argument:

```php
$certificate = SslCertificate::createForHostName('expired.badssl.com', $timeoutInSeconds, false);
```

The second argument is a timeout in seconds, so the full signature is host, timeout, verify. With verification off, the download succeeds even for an expired certificate, which is what you want when your monitoring job is specifically about detecting expiry rather than about trusting the connection.

The trust check being described as coming soon is worth a second look, because the notice has been in the readme across several releases. The current version is 2.7.0, released on 2026-06-05, and the previous two were 2.6.10 from 2025-04-25 and 2.6.9 from 2025-01-31. The repository is not archived and the last push was on 2026-06-05. If a trust check is what you need, verify in the version you install rather than assuming it arrived, because the readme still describes it as pending.

## Exceptions separate a bad input from a failed download

The error handling is split into three named exceptions, and the split tells you where the failure happened.

If the given `ipAddress` is invalid, `Spatie\SslCertificate\Exceptions\InvalidIpAddress` is thrown. If the given `hostName` is invalid, `Spatie\SslCertificate\Exceptions\InvalidUrl` is thrown. If the `hostName` is valid but there was a problem downloading the certificate, `Spatie\SslCertificate\Exceptions\CouldNotDownloadCertificate` is thrown.

That last distinction is the one worth designing around. A malformed host name and an unreachable host are different operational problems, and a monitoring job that catches everything in one block loses the ability to tell a typo in configuration from a server that stopped accepting connections. A job that wants to alert on the first and ignore the second can catch `InvalidUrl` and `CouldNotDownloadCertificate` separately.

The `InvalidUrl` name is slightly wider than the description it accompanies, since it is tied to the `hostName` argument rather than to a URL in general. The download constructor takes a host name, and it is that value the exception refers to.

The package is also careful to note that the timeout is a parameter, which matters for a scanning or monitoring loop. A default timeout that is too long turns one unreachable host into a stall across the whole job, and the third argument position is where the timeout goes when you also need to disable verification.

## The property getters, and what each one actually returns

The query surface is wide enough that a class per property would be silly, so it is a set of getters on one object, and the README documents each with its example return value.

Identity and cryptography: `getIssuer()` returns the issuer name, with the example "Let's Encrypt Authority X3". `getSignatureAlgorithm()` returns the algorithm used for signing, with the example "RSA-SHA256". `getPublicKeyAlgorithm()` returns the public key algorithm and `getPublicKeySize()` returns the public key size. `getFingerprint()` returns a fingerprint for the certificate and `getFingerprintSha256()` returns the SHA256 fingerprint, which is the one you will usually want when you are pasting it into a ticket.

Names: `getDomain()` returns the primary domain name for the certificate, and `getAdditionalDomains()` returns the additional domain names as an array, for example `["spatie.be", "www.spatie.be]`. The documentation adds the detail that a domain returned by this method can start with `*`, meaning it is valid for all subdomains of that domain, which is how a wildcard certificate is represented.

Organisation: `getOrganization()` returns the organization belonging to the certificate, and `getSubjectOrganization()` returns the organization of the certificate's subject, both with the example "Spatie BVBA". The existence of two similar methods is the point, since the issuer organisation and the subject organisation are different fields and confusion between them is a classic source of misread reports.

Dates: `validFromDate()` and `expirationDate()` each return an instance of Carbon, and the class also exposes `isValid()`, `daysUntilExpirationDate()` and `lifespanInDays()`.

## Against shelling out to openssl, the difference is a typed object

The realistic alternative is to run `openssl x509` and parse its output, or to use PHP's own stream functions and the `openssl_x509_parse` function that comes with the extension. Both work, and both are what this package sits on top of in some form.

The difference in approach is the shape of the result. The OpenSSL route gives you a bag of text fields, or an array from `openssl_x509_parse` with keys like `validFrom_time_t` and `subject`, and you deal with formats and types yourself. This package turns that into an object with named methods, and the date methods return Carbon instances rather than Unix timestamps, which is the part that actually saves work in a Laravel style application. Calling `->daysUntilExpirationDate()->format('Y-m-d')` is different from computing the difference between a timestamp and now by hand.

The other difference is that the package is opinionated about fetching. `createForHostName` handles the socket, the SNI handshake and the retrieval, and the fluent builder adds the port and IP overrides. Doing that with raw stream functions means writing the same connection code and getting the same edge cases right, in particular that you want the peer certificate for a host name, and that the request should not silently fall back to a different certificate.

The cost is a dependency and a trust gap. The package does not verify the authority, and it will not tell you that a certificate is for a different domain than the one you asked about beyond exposing `getDomain()` for you to compare yourself. If your use case is a monitoring job that alerts on expiry and records metadata, that is exactly the right shape. If your use case is deciding whether to trust a connection, you need the verification the notice says is not there yet, or you need to use the OpenSSL path and do the chain validation yourself.

## Conclusion

spatie/ssl-certificate fits a PHP application that needs to know when a host's certificate expires, or that needs to record the issuer and algorithms of the certificates it talks to, without shelling out to openssl. It is the wrong tool if you need to decide whether a certificate should be trusted, because the package states it does not check that the certificate is signed by a trusted authority. Before you rely on it, verify three things: whether the trust check that the notice promises has landed in the version you install, since the notice still describes it as a future release, whether the Carbon dependency fits the rest of your stack, and whether the download path handles the hosts you actually monitor, since the library reaches the peer over a socket and a host that requires SNI or a specific port needs the fluent form. The code is MIT licensed, the current release is 2.7.0 from 2026-06-05, and the last push was on the same day.

## FAQ

### How do I install spatie/ssl-certificate?

Install it with Composer: `composer require spatie/ssl-certificate`. The package is published on Packagist as `spatie/ssl-certificate` and is MIT licensed.

### How do I check when a certificate expires with spatie/ssl-certificate?

Call `expirationDate()` for a Carbon instance of the expiry date and `daysUntilExpirationDate()` for the number of days between today and that date. `isValid()` returns true only when the current date and time falls between `validFromDate()` and `expirationDate()`.

### Does spatie/ssl-certificate verify that a certificate is signed by a trusted authority?

No. The package carries an important notice stating that it currently does not check if the certificate is signed by a trusted authority, and says the check will be added in a next point release. `isValid()` is a date range check only.

### How do I fetch a certificate for a host on a custom port or IP address?

Use the fluent form: `SslCertificate::download()->usingPort($customPort)->forHost($hostName)`, or `->fromIpAddress($ipAddress)->forHost($hostName)` to pin the connection to a specific address. Both IPv4 and IPv6 addresses are supported, and socket context options can be added with `withSocketContextOptions`.

### What happens when the certificate cannot be downloaded?

An invalid host name raises `Spatie\SslCertificate\Exceptions\InvalidUrl`, an invalid IP address raises `InvalidIpAddress`, and a valid host that cannot be reached raises `CouldNotDownloadCertificate`. A third constructor argument accepts a timeout in seconds and a boolean that disables certificate verification, which is how you download an expired certificate.

## Sources

- [License: MIT](https://github.com/spatie/ssl-certificate/blob/main/LICENSE)
- [Project website](https://spatie.be/open-source)
- [README](https://github.com/spatie/ssl-certificate/blob/main/README.md)
- [Releases](https://github.com/spatie/ssl-certificate/releases)
- [spatie/ssl-certificate on GitHub](https://github.com/spatie/ssl-certificate)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/spatie-ssl-certificate
