The short answer
A certificate trust store is the list of root CA certificates a program is willing to treat as a starting point for trust. The catch is that there is no single list per machine. As of August 2026, Chrome carries its own Chrome Root Store on every platform except iOS, Firefox uses Mozilla's bundled list, Windows and macOS each maintain their own, and Java, Node.js, Python and curl all consult separate bundles again. A certificate that validates in your browser can still be rejected by an application on the same computer, because the two never consulted the same list.
On this page
- What is a certificate trust store?
- Which programs keep their own trust store?
- Why does the same certificate pass in Chrome and fail in Java?
- How to check what your system actually trusts
- Trust store problem, or a missing intermediate?
- How to add a root certificate to a trust store
- What this means when you buy a certificate
- FAQ
Most of the time none of this matters, because the major lists overlap almost completely. It starts to matter at the edges — a root added to the public programs recently, a build server running a JDK nobody has touched in three years, a container image trimmed down until the CA bundle went with it. What follows is how the lists differ, how to read the contents of the ones on your own machine, and how to tell this failure apart from the far more common one it gets mistaken for.
What is a certificate trust store?
A certificate trust store is a collection of root CA certificates that a program treats as trust anchors. When a TLS client validates a server certificate, it builds a path from that certificate up through any intermediates to a root, and the check only succeeds if that root is already in the store. Nothing about the path is trusted on its own merits; the store is what makes the end of it authoritative.
Path validation itself is specified in RFC 5280, and the specification is careful to treat the set of trust anchors as an input to the algorithm rather than part of it. That distinction is the whole subject of this article. Two clients can implement identical, correct path validation and still disagree, because they were handed different inputs.
A root gets into a store by being accepted into a root program — the vetting process run by Microsoft, Apple, Mozilla, Google and the Android project. Each program keeps its own criteria and its own inclusion timeline, and each ships its results on its own release schedule. Six programs, six schedules, and a certificate that has to satisfy all of them.
Which programs keep their own trust store?
More than people expect. Both major browsers ship their own list rather than deferring to the operating system, both mobile platforms maintain their own, and the common server-side runtimes each carry a bundle of their own as well. A typical developer laptop is running at least six independent lists, and a build server usually adds a few more inside container images.
| Validator | Store it uses | When it changes |
|---|---|---|
| Chrome | Chrome Root Store, on Windows, macOS, Linux, ChromeOS and Android. On iOS it uses the system store instead, because Apple's platform rules require it. | With Chrome |
| Firefox | Mozilla's own list, bundled with the browser on every platform | With Firefox |
| Safari, and all of iOS | Apple root store, in the keychain | With the OS |
| Windows apps, .NET, PowerShell | Microsoft Trusted Root Program store, viewable in certlm.msc. Windows can also retrieve a root on demand, which is why the visible list looks far shorter than the program itself. | With Windows Update |
| Java | A cacerts keystore inside each installed JDK, under $JAVA_HOME/lib/security | Only when you update the JDK |
| Node.js | A Mozilla-derived bundle compiled into the binary. Node v22.19 and v24.6 added --use-system-ca (and NODE_USE_SYSTEM_CA=1) to consult the OS store as well. | With the Node release |
| Python (requests, urllib3) | The certifi bundle, installed per virtual environment | When you upgrade certifi |
| curl and OpenSSL on Linux | The distribution's CA bundle, usually from the ca-certificates package | With the package manager |
| Android apps | The system store. Apps targeting API level 24 and above do not trust user-installed CAs unless the developer opts in through a network security configuration. | With the OS |
Two rows in that table cause most of the surprises. Java's is the one people forget exists at all, because it updates on a schedule that has nothing to do with the operating system underneath it. Windows' is the one people misread, because the on-demand retrieval means an empty-looking list is not evidence that a root is missing.
Why does the same certificate pass in Chrome and fail in Java?
Because the two consulted different lists, and one of them did not contain the root. There are only three ways that happens: the root was never in that list, the list is an old copy that predates the root, or the program was pointed at a different file entirely. All three produce a near-identical error message, and they have completely different fixes.
The stale case is worth dwelling on, because it is the one that survives a whole afternoon of debugging. A build agent pinned to an old JDK is validating against a cacerts file that was assembled when that JDK was built. The operating system beneath it has been patched continuously and knows about the root perfectly well. Nothing on the machine is misconfigured in any way an audit would flag, and the certificate is entirely valid. The file is just old.
The third cause shows up most often in containers. A minimal base image frequently ships without the ca-certificates package, which leaves OpenSSL with no trust anchors at all — so every TLS connection from that image fails, not just yours. If a program cannot reach any well-known public site either, stop looking at your certificate and go and look at the image.
How to check what your system actually trusts
Ask each runtime directly rather than reasoning about what it should be using. Every store on a machine can be enumerated from the command line in one line, and the counts they report will not match each other. Seeing three different numbers on one machine is usually the moment the problem stops being mysterious.
One line per store
# OpenSSL / curl — where the system bundle lives, and how many roots it holds
openssl version -d
grep -c 'BEGIN CERTIFICATE' /etc/ssl/certs/ca-certificates.crt
# Node.js (v22.19+ / v24.6+) — compare the compiled-in list with the OS list
node -e "const t=require('tls');console.log('bundled', t.getCACertificates('bundled').length)"
node -e "const t=require('tls');console.log('system ', t.getCACertificates('system').length)"
# Python — which bundle requests will actually read
python3 -c "import certifi; print(certifi.where())"
# Java 9 and later — the JDK's own keystore
keytool -list -cacerts | head
# What the server is really sending, with no client-side help
openssl s_client -connect example.com:443 -showcertsRunning these together on one Linux machine while preparing this article, Node's compiled-in list, the distribution's ca-certificates bundle and the certifi bundle in a Python environment each reported a different number of root certificates — three files, three counts, one computer. The absolute numbers are not worth quoting, because they shift with every distribution, runtime version and corporate image. The disagreement is the point, and it reproduces anywhere you care to try it.
The last command is the one to reach for first when a server is involved. openssl s_client does not go and fetch anything the server failed to send, so what it prints is exactly what the server offered — which is what separates the next question.
Trust store problem, or a missing intermediate?
Look at which clients fail. A missing intermediate certificate fails in curl, mobile clients and Java while desktop browsers stay green, because browsers can fetch the absent intermediate themselves and paper over the gap. A trust store problem does the opposite: one runtime fails while everything else on the same machine is perfectly happy.
Getting this the wrong way round is expensive. Adding a root to one machine's trust store to work around what is really a missing intermediate does make that machine work, which is exactly why the mistake sticks — the ticket closes, and every other client in the world carries on failing. The server-side fix is covered in detail in our guide to the SSL certificate chain and how to repair a broken one.
There is a third outcome worth naming, because it gets treated as a trust store issue more often than it should. If every client fails, the certificate itself is the problem — expired, issued for a different hostname, or self-signed. No amount of editing trust stores is the right response to that, and doing it anyway buries a real defect under a local workaround.
How to add a root certificate to a trust store
Once per store that needs it, using that store's own tooling. There is no command that updates all of them together, which is the practical cost of running a private CA: every list on every machine is a separate deployment target, and one missed runtime produces a failure that looks like a certificate problem.
| Store | How to add a root |
|---|---|
| Debian / Ubuntu | Copy the PEM into /usr/local/share/ca-certificates/ with a .crt extension, then run update-ca-certificates |
| RHEL / Fedora | Copy into /etc/pki/ca-trust/source/anchors/, then run update-ca-trust |
| Windows | certlm.msc → Trusted Root Certification Authorities, or certutil -addstore -f Root ca.crt. Group Policy for fleets. |
| macOS | Keychain Access → System, then set the certificate to Always Trust |
| Java | keytool -importcert -cacerts -alias internal-root -file ca.crt — repeat for every JDK on the machine |
| Node.js | NODE_EXTRA_CA_CERTS=/path/ca.pem, or --use-system-ca on v22.19+ / v24.6+ to pick up the OS store |
| Python | REQUESTS_CA_BUNDLE or SSL_CERT_FILE pointing at a bundle that includes your root |
| Android | Ship a network security configuration that names the root as a trust anchor — installing it in Settings will not reach apps |
Before you add one
A root in a trust store is trusted for every certificate it ever signs, for any hostname, until someone removes it. Adding your own internal CA to machines you administer is a reasonable engineering decision. Accepting a root that arrived with a piece of software, or that someone sent you to make an error go away, hands whoever holds that key the ability to present a valid-looking certificate for any site at all. Keep a record of which roots you added and where, and take them out when the reason for them ends.
What this means when you buy a certificate
The distribution problem is the part you are handing over. A root belonging to an established public CA is already present in the Microsoft, Apple, Mozilla, Chrome and Android programs, and it reaches devices through ordinary updates that nobody at your end has to arrange. Your server sends the leaf and the intermediates, and the client already holds the anchor.
Issue from your own CA instead and every list in the table above becomes yours to maintain, on every machine that will ever connect, for as long as the CA exists. That is a sensible trade for internal service-to-service traffic where you control both ends. It is a poor one for anything the public reaches, which is what a publicly trusted SSL certificate exists to avoid. The same reasoning, in more detail, is in our write-up on when a self-signed certificate is and isn't appropriate.
One practical check before committing to any CA: confirm its roots are carried by all the programs your users actually run, not only the desktop browsers. Mobile and Java clients are where a thin root distribution shows up first, and it shows up as your problem rather than the CA's.
Choosing a certificate for a public service
If the clients you need to satisfy include phones, Java applications and things you will never see, a certificate from a publicly trusted CA removes the distribution work described on this page. Our SSL certificate range covers the validation levels, and the comparison in which SSL certificate do I need walks through picking between them.
Related reading
- SSL certificate chain explained — the other half of path validation, and the failure this one is most often confused with.
- What a certificate authority is — how a root earns its place in these lists, and what removes it again.
- Self-signed certificates — the case where every trust store on the network becomes your responsibility.