Skip to content
← All articles

Russian Certificate Authorities and TLS: Trust Chain, Root Install, Broken Integrations

Short answer. In Russia the phrase "certificate authority" covers two unrelated things: a CA that issues TLS certificates for websites (the Ministry of Digital Development CA, also known as Russian Trusted CA) and an accredited e-signature CA used for signing documents. Only the first one has anything to do with your website. The practical problem with Russian TLS certificates is the trust chain: the root is absent from standard trust stores, and it has to be installed on servers, not just in browsers.

Why an international team runs into this at all

If you have never deployed a certificate in Russia, this topic still reaches you through the back door. It usually arrives as a failing integration rather than as a browser warning, which is exactly why it costs so much time to diagnose.

Typical entry points:

  • Your service calls a Russian bank, payment provider, logistics carrier or government API over HTTPS, and the call started failing with a certificate error that makes no sense.
  • You run a subsidiary, branch office or partner integration in Russia and their endpoints moved to a domestic certificate authority.
  • Your CI pipeline pulls an artifact or mirror hosted in Russia and the build breaks on TLS verification.
  • You own a site with Russian users and someone asked whether you should switch certificate authorities.
  • A customer reports that your site shows a security warning on their machine, and you cannot reproduce it anywhere.

In every one of those cases the certificate itself is fine. What is missing is a root certificate in the trust store of whatever is making the request. The rest of this article is about finding out which trust store, and fixing it in the right place.

Two different things called a certificate authority

Russian search traffic for "certificate authority" splits into two audiences that never overlap. One wants a padlock and https:// in the address bar. The other wants an electronic signature to file tax reports or sign contracts. The vocabulary is identical, the technology is not. Find your row before reading further.

What you needWhich kind of CAWhere to go
A padlock and https:// for your websiteWeb TLS CA issuing server certificates. In Russia: the Ministry of Digital Development CA, branded Russian Trusted CAThis article. To inspect a live certificate use the SSL checker
A browser says the site certificate is not trustedWeb TLS: a trust chain problem, not a certificate defectThis article plus the breakdown of ERR_CERT_AUTHORITY_INVALID
Your server or script fails when calling a Russian APIWeb TLS, but the failure is client side: the root is missing from the system storeThis article, the server installation section
Signing tax filings, contracts, participating in state procurementAccredited electronic signature CANot this article. You need an accredited CA, a cryptographic service provider such as CryptoPro CSP, and a hardware token such as Rutoken or JaCarta
Logging into a government portal with a signature certificateElectronic signatureNot this article: browser plugin, token driver, signature certificate
Running TLS with GOST cryptographic algorithmsA separate world at the intersection of the twoRequires GOST cryptography on both ends; a stock browser cannot negotiate it without extra software
If you came here for electronic signatures, none of the openssl commands below apply. A signature certificate lives on a token and inside a cryptographic provider, never on a web server, and it will never appear in a browser address bar. Everything that follows is about server TLS certificates.

Why the confusion is so persistent

Both objects are X.509 certificates. Both are issued by something called a certificate authority. Both have a validity period, a fingerprint, an issuer and a chain up to a root. Even the verbs match: revoke, renew, verify the chain. They diverge in three places, and three is enough:

  • Purpose encoded inside the certificate. A server TLS certificate carries serverAuth in its Extended Key Usage extension. A signature certificate carries document-signing purposes. A web server will not accept a signature certificate as a server certificate, and the reverse is equally impossible.
  • Who reads it. A TLS certificate is read by browsers and by every HTTP client: curl, mobile apps, someone else's backend. A signature certificate is read by a cryptographic provider at the moment a file is signed.
  • Where it lives. A TLS certificate is a file on a server next to its private key. A signature certificate normally lives on a hardware token from which the private key cannot be exported by design.
Accreditation rules, the list of accredited authorities and the technical requirements around Russian electronic signatures change over time. This article deliberately contains no decree numbers, effective dates or fee amounts, because they age faster than the text does. Verify against an official source before acting.
Diagram showing two different certificate authorities: a web TLS CA for server certificates and an accredited electronic signature CA
Two unrelated worlds behind one phrase: a server certificate and an electronic signature certificate.

How the trust chain breaks with a Russian root

A certificate issued by the Russian national CA is an ordinary X.509 certificate. The validity dates are fine, the domain matches the SAN entry, the signature verifies. Exactly one thing is different: the root that terminates the chain is not part of the Mozilla, Chrome, Apple or Microsoft root programs. It is therefore absent from Firefox, Chrome, macOS, Windows, most Linux distributions and every stock Docker image.

The client cannot anchor the chain, and says so in its own dialect:

  • Chrome and Chromium browsersERR_CERT_AUTHORITY_INVALID. Detailed walkthrough: fixing that specific error.
  • FirefoxSEC_ERROR_UNKNOWN_ISSUER. Firefox ships its own trust store and does not consult the system store on Windows and Linux by default, so a system install does not fix it.
  • curl and server-side clientsSSL certificate problem: unable to get local issuer certificate, exit code 60.
  • JavaPKIX path building failed: unable to find valid certification path to requested target.
  • PythonCERTIFICATE_VERIFY_FAILED: unable to get local issuer certificate.

Five messages, one condition: no path to a trusted root. They read differently only because the stores are different.

The general mechanics of chain building, path validation and the usual breakage patterns are covered separately in incomplete certificate chain. Everything below is the Russian-specific part.

A certificate is never "valid" in the abstract. It is valid relative to one specific trust store on one specific client. Your browser, your curl, your JVM and your mobile app hold four different stores that never synchronise with each other.

What the visitor can do, and what the site owner can do

A visitor can install the root certificate into their system or browser. That is their action, on their machine, entirely outside your control. A site owner can do three things: serve the full chain, publish an honest installation guide, and — most importantly — decide in advance what share of the audience will never install anything.

Some Russian browser builds ship the root preinstalled; Yandex Browser and Atom are the usual examples. The hedge in that sentence is deliberate: the set of preinstalled roots changes between versions, and the only reliable way to know is to open the trusted root list in the specific build and search for Russian Trusted.

Russian CA versus international CA: what actually differs

PropertyRussian national CAInternational CA
Root of trustnational root plus an intermediateroot included in the Mozilla, Chrome, Apple and Microsoft programs
Preinstalled on clientsnot guaranteed; some Russian builds ship itessentially everywhere out of the box
FormatX.509 with standard extensionsX.509 with standard extensions
Validity checkingstandard, same commandsstandard
Issuance and renewal automationtypically portal based rather than ACME, so the usual certbot workflow does not applyACME everywhere, renewal happens in the background
Public Certificate Transparency logsdo not count on the issuance appearing in public CT logslogging is effectively mandatory for Chrome trust
Inventory via crt.sh and similarmay return nothing; keep your own registryworks
Best fitRussian audiences and environments where you control the trust storesuniversal and international audiences

One clarification that saves a lot of confusion: a Russian TLS certificate and GOST cryptography are not the same thing. The national CA issues ordinary X.509 certificates with widely supported signature algorithms, which is precisely why a normal browser accepts them the moment the root appears in its store. Verify what you actually hold with one command:

# signature algorithm and key parameters of a certificate file
openssl x509 -in russian_trusted_root_ca.cer -inform DER -noout -text \
  | grep -E 'Signature Algorithm|Public Key Algorithm|Public-Key'

# the same for whatever the live server actually serves
echo | openssl s_client -connect example.ru:443 -servername example.ru 2>/dev/null \
  | openssl x509 -noout -text | grep -E 'Signature Algorithm|Issuer:|Subject:'

If GOST algorithms show up in the output, you are not dealing with ordinary web TLS. In that case a root in the trust store is not enough — the client needs a cryptographic provider that implements those algorithms.

Installing the root on the server, not in the browser

The most expensive mistake in this area is assuming the Russian root only matters to end users. In practice your own servers break first. A browser at least shows a human a readable warning; a server-side integration just writes a line to a log, usually silently and usually at night.

What typically breaks:

  • a cron job pulling exchange rates or a reference dataset from a Russian portal;
  • a backend integration with a Russian bank or payment API;
  • a reporting export over HTTPS;
  • a Java connector to a document exchange system;
  • outbound webhooks you send to a partner whose endpoint uses a domestic certificate;
  • a CI job that fetches a dependency from a Russian mirror.

The symptom is always the same string in the log: unable to get local issuer certificate. The browser is irrelevant here — it has its own store, and the fact that the site opens fine on your laptop tells you nothing about the server.

AlmaLinux, RHEL, Rocky, CentOS, Fedora

# drop BOTH the root and the intermediate into the anchors directory
sudo cp russian_trusted_root_ca.cer /etc/pki/ca-trust/source/anchors/
sudo cp russian_trusted_sub_ca.cer  /etc/pki/ca-trust/source/anchors/

# rebuild the system bundle (accepts both PEM and DER)
sudo update-ca-trust extract

# confirm the anchor landed in the extracted store
trust list --filter=ca-anchors | grep -i -B2 'Russian Trusted'

# fallback check: search the generated bundle directly
grep -c 'BEGIN CERTIFICATE' /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem

The source/anchors/ directory accepts both PEM and DER files, so no conversion is required up front. Running update-ca-trust with no arguments does the same work as update-ca-trust extract.

Debian and Ubuntu

# this directory takes PEM ONLY and the .crt extension ONLY
sudo cp russian_trusted_root_ca.pem /usr/local/share/ca-certificates/russian_trusted_root_ca.crt
sudo cp russian_trusted_sub_ca.pem  /usr/local/share/ca-certificates/russian_trusted_sub_ca.crt
sudo update-ca-certificates

# if the file arrived in DER, convert it first
openssl x509 -inform DER -in root.cer -outform PEM -out russian_trusted_root_ca.crt

# verify
awk -v cmd='openssl x509 -noout -subject' \
  '/BEGIN/{c=cmd} c{print | c} /END/{close(c); c=0}' \
  /etc/ssl/certs/ca-certificates.crt | grep -i 'russian'

Two traps eat the most time here. First, update-ca-certificates only picks up files with the .crt extension. A file named root.pem or root.cer will sit in that directory doing absolutely nothing, and you get no error at all. Second, the content must be PEM; DER content under a .crt name fails just as silently. A successful run prints a line like 1 added, 0 removed — if that number is zero, nothing was installed.

Alpine and containers

# inside an Alpine container
apk add --no-cache ca-certificates
cp russian_trusted_root_ca.crt /usr/local/share/ca-certificates/
update-ca-certificates

# in the Dockerfile, so the fix survives an image rebuild
# COPY russian_trusted_root_ca.crt /usr/local/share/ca-certificates/
# RUN update-ca-certificates
Installing a root inside a running container lasts exactly until that container is recreated. Bake the certificate into the image with COPY, or mount a bundle from outside. Otherwise the integration breaks at the worst possible moment — during a routine image update, when nobody will think to look at certificates.
Diagram of a root certificate installed into the Linux system trust store alongside separate Java, Node.js and Python trust stores
The system store covers curl, PHP and Go, but not Java, Node.js or Python — those carry their own root lists.

Runtimes with their own trust store: Java, Node.js, Python

Installing the root system-wide is necessary but not sufficient. Several popular runtimes never look at the system store at all. This is the source of the classic complaint: curl works on the server, the application does not.

ClientUses the system storeExtra work required
curl, wgetyessystem install is enough
PHP (cURL and OpenSSL streams)usually yescheck curl.cainfo and openssl.cafile in php.ini: if a custom bundle is configured, extend that bundle instead
Go on Linuxyessystem install is enough
.NET on Linuxyessystem install is enough
Java (JVM)no — its own cacerts fileimport with keytool
Node.jsno — a compiled-in root listNODE_EXTRA_CA_CERTS environment variable
Python: requests, httpxno — the certifi bundleSSL_CERT_FILE, REQUESTS_CA_BUNDLE or an explicit verify argument
Python: ssl, urllibusually yesuses OpenSSL default paths, no extra work
BrowsersChrome and Edge yes; Firefox noFirefox needs the root in its own store

Java: importing into cacerts

# Java 9 and newer keep the store at $JAVA_HOME/lib/security/cacerts
sudo keytool -importcert -trustcacerts -noprompt \
  -alias russian-trusted-root \
  -file /etc/pki/ca-trust/source/anchors/russian_trusted_root_ca.cer \
  -keystore "$JAVA_HOME/lib/security/cacerts" -storepass changeit

# confirm the alias exists
keytool -list -keystore "$JAVA_HOME/lib/security/cacerts" -storepass changeit \
  | grep -i russian

The default store password is changeit; substitute yours if it was changed. Java 8 keeps the file elsewhere: $JAVA_HOME/jre/lib/security/cacerts. And the important part: a JDK upgrade replaces cacerts wholesale, taking your import with it. For anything critical, keep a separate truststore file and point the JVM at it with -Djavax.net.ssl.trustStore, so upgrades cannot silently undo the fix.

Node.js and Python

# Node.js reads this once at process start, PEM only
NODE_EXTRA_CA_CERTS=/etc/ssl/certs/russian_trusted_root_ca.pem node app.js

# Python: locate the certifi bundle that requests actually uses
python3 -c "import certifi; print(certifi.where())"

# the right fix is an explicit bundle, not editing certifi in place
export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
export REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt
python3 -c "import requests; print(requests.get('https://example.ru').status_code)"
Do not append the root to the certifi bundle file itself. The next package upgrade wipes the edit, and the failure returns with no change anywhere in your code — which makes it extremely hard to trace. Set the bundle through an environment variable in the service unit file or in the container environment.

How not to verify the install: the gosuslugi.ru trap

Do not test your root installation by curling gosuslugi.ru. The portal restricts requests originating from data centre address ranges, so a request from your server is refused in a way that looks like a certificate failure even though the certificate and the trust store are perfectly fine. Test against a host that does not filter data centre addresses instead — sberbank.ru works well for this.

This trap is expensive precisely because it is convincing. You have just installed the root, you test against the most obvious government site, and you get refused. From there people start reinstalling the certificate, rebuilding the bundle and hunting for a typo in a path, while the actual problem sits in a completely different layer.

The discriminator is simple: find out whether the TLS handshake completed at all.

What you observeTLS handshakeDiagnosis
SSL certificate problem: unable to get local issuer certificate, curl exit code 60did not completeroot missing from the store, or the client reads a different store
SSL certificate problem: certificate has expired, exit code 60did not completethe server certificate expired; unrelated to root installation
An HTTP response arrived — 403, 429 or a challenge page — curl exit code 0completed successfullythe certificate is fine; you were blocked by IP reputation or a WAF
Connection timed out or Connection reset, exit code 28, 35 or 56never started or was cutnetwork, filtering or missing egress — not a certificate problem
openssl s_client prints Verify return code: 0 (ok) but the application still failscompleteddifferent stores: openssl used the system one, the application uses its own (Java, Node.js, certifi)
# 1. TLS only, no HTTP: inspect the certificate and the trust path
echo | openssl s_client -connect sberbank.ru:443 -servername sberbank.ru 2>/dev/null \
  | grep -E 'Verify return code|subject=|issuer='

# 2. verbose curl shows exactly which step failed
curl -sv -o /dev/null https://sberbank.ru 2>&1 \
  | grep -Ei 'SSL certificate|issuer|subjectAltName|HTTP/'

# 3. the curl exit code is the fastest discriminator: 0 versus 60
curl -sS -o /dev/null https://sberbank.ru; echo "curl exit=$?"

How to read it: if openssl s_client reached the Verify return code line, TLS did its job and the question is no longer about certificates. If curl exited 0 and handed you an HTTP status — even a 403 — the certificate was accepted by definition, because no HTTP response is possible without a completed handshake.

Diagnosing the chain: openssl s_client and Verify return code

# how many certificates the server actually sends
echo | openssl s_client -connect example.ru:443 -servername example.ru -showcerts 2>/dev/null \
  | grep -c 'BEGIN CERTIFICATE'

# who signed what: subject and issuer per chain level
echo | openssl s_client -connect example.ru:443 -servername example.ru -showcerts 2>/dev/null \
  | grep -E '^ *[0-9]+ s:|^ *[0-9]+ i:'

# the verdict of path validation
echo | openssl s_client -connect example.ru:443 -servername example.ru 2>/dev/null \
  | grep 'Verify return code'

The codes you will actually meet in this scenario:

  • 0 (ok) — a path was built from your system store. Good, on this machine specifically.
  • 20 (unable to get local issuer certificate) — no issuer was found locally for the topmost certificate the server sent.
  • 21 (unable to verify the first certificate) — usually means the server sent the leaf certificate only.
  • 2 (unable to get issuer certificate) — same family: issuer not found.
  • 10 (certificate has expired) — validity, not trust; nothing to do with the CA.
  • 19 (self signed certificate in certificate chain) — a self-signed certificate somewhere in the chain.

Telling "missing intermediate" apart from "missing root"

Two different diagnoses with nearly identical error text. Two commands separate them.

  1. Count the certificates in the server response. If grep -c 'BEGIN CERTIFICATE' returns 1, the server is sending only the leaf. That is the site owner's problem and it is fixed in the server config, not on the client.
  2. If two or more arrive and the error persists, the topmost certificate sent is signed by a root that is not in the store. That is fixed on the client side by installing the root.
  3. Test the hypothesis directly by supplying the root explicitly and seeing whether the verdict changes.
# point openssl at the root file explicitly
echo | openssl s_client -connect example.ru:443 -servername example.ru \
  -CAfile /etc/pki/ca-trust/source/anchors/russian_trusted_root_ca.cer 2>/dev/null \
  | grep 'Verify return code'

If the verdict becomes 0 (ok) with -CAfile and stays broken without it, the diagnosis is unambiguous: the certificate and chain are correct and the root simply is not installed system-wide. This is the fastest check in the whole workflow and it resolves most disputed cases in seconds.

If the server is sending only the leaf, here is the fix on the site owner's side:

# nginx: ssl_certificate must point at a "leaf + intermediates" file
cat leaf.crt sub_ca.crt > /etc/nginx/ssl/example.ru.fullchain.crt
nginx -t && systemctl reload nginx

# Apache: leaf in SSLCertificateFile, intermediates in SSLCertificateChainFile
apachectl configtest && systemctl reload httpd

Order matters: leaf first, then intermediates from the bottom up. Do not append the root — it belongs in the client's store, not in the server response. More on assembling chains in incomplete certificate chain, and the general inspection workflow in how to check an SSL certificate.

Terminal output of openssl s_client with Verify return code values explained while checking a Russian CA chain
Verify return code is the shortest path from symptom to diagnosis: 21 usually means a missing intermediate, 20 a missing root.

What it means if you own the site

Choosing a certificate authority here is a product decision, not a technical one. Both options work identically at the protocol level; what differs is how many of your users see a warning.

When a Russian certificate makes sense

  • The audience is entirely Russian and you are willing to maintain a clear root installation guide.
  • An internal or corporate perimeter where you manage the workstations and can push the root centrally.
  • An international CA is unavailable to your organisation or domain for any reason.
  • The service already requires users to install extra software, so the root folds into an existing onboarding step rather than adding new friction.

When it does not

  • You have international visitors, partner integrations or payment gateways.
  • The product is a public API called by other people's servers: they will not have the root, and you cannot fix their trust stores in principle.
  • The site lives on organic search: some crawlers and third-party checkers simply will not reach the content.
  • You are not prepared to staff a support flow around "please install this root" — that is ongoing load, not a one-off task.
The rule of thumb: the more consumers your site has whose trust store you do not control, the more expensive a non-standard root becomes. For a public API it is almost always unacceptable; for an internal portal it is almost always fine.

Dual issuance: international plus domestic

The idea sounds reasonable — serve the Russian certificate to clients that have the root and the international one to everybody else. It does not work, and the reason is worth understanding before you design around it.

During the TLS handshake a client never tells the server which roots it trusts. It sends the hostname in the SNI extension and a list of supported algorithms, and that is all. The server physically cannot select a certificate based on the client's trust store, because that information is not in the protocol. Multiple ssl_certificate directives in nginx select by key type — RSA versus ECDSA — not by client trust.

So in practice dual issuance always means separate entry points rather than automation inside a single server:

  • Separate hosts. The main domain on an international certificate, a dedicated subdomain or mirror on the domestic one, with an explicit link and explanation.
  • Separate addresses by geography. DNS hands different users different IP addresses, each front-end holding its own certificate. It works, but it adds a whole infrastructure layer and a new class of incidents.
  • Separate channels. Public website on an international certificate, internal and partner integrations on the domestic one. This is usually both the cheapest and the most honest arrangement.

Expiry and monitoring without ACME

An expired certificate breaks the connection regardless of which CA issued it. With Russian certificates the odds of missing a renewal are higher for two reasons: there is usually no ACME automation renewing in the background, and there is usually no public CT log entry, which is how many teams unknowingly inventory their certificate estate. Two familiar safety nets disappear at once.

# validity dates, subject and issuer in one command
echo | openssl s_client -connect example.ru:443 -servername example.ru 2>/dev/null \
  | openssl x509 -noout -dates -subject -issuer

# will it expire within 14 days? exit code 1 means yes, 0 means no
echo | openssl s_client -connect example.ru:443 -servername example.ru 2>/dev/null \
  | openssl x509 -noout -checkend 1209600

The -checkend form is convenient in cron: it returns a ready-made exit code, so no date parsing is required.

On enterno.io the SSL monitor checks validity on a schedule and alerts ahead of time — by default a warning at 14 days and a critical alert at 3 days. It is configured under monitoring, with notifications to Telegram and email.

One caveat specific to domestic certificates: keep "expiring" and "not trusted" as separate signals in your monitoring. An external checker without your root will report a trust failure, and that failure masks the real expiry date. Read the validity date from the certificate independently of the path-validation verdict, or you will get a false alarm instead of a genuine two-week warning.

Related reading: SSL certificate monitoring and what to do when a certificate has already expired.

Timeline of SSL certificate expiry monitoring with a warning at 14 days and a critical alert at 3 days
Without ACME, renewal becomes a calendar task — a two-week alert turns an emergency into planned work.

Symptom, cause, check, fix

SymptomLikely causeCheckFix
Site opens in Yandex Browser but not in Chromethe root ships only in some buildssearch for Russian Trusted in each browser's trusted root listpublish an installation guide; for a public site, reconsider the CA choice
curl on the server: unable to get local issuer certificateroot missing from the system bundletrust list or a search through ca-certificates.crtupdate-ca-trust or update-ca-certificates
curl works, the Java service fails with PKIX path building failedthe JVM has its own cacertskeytool -list filtered by aliaskeytool -importcert into cacerts, or a dedicated truststore
Python: CERTIFICATE_VERIFY_FAILED while curl succeedsrequests reads certifi, not the system storepython3 -c "import certifi; print(certifi.where())"set SSL_CERT_FILE and REQUESTS_CA_BUNDLE
Everything broke again after an image updatethe root was installed inside a running containerinspect the anchors directory inside the new containermove the install into the Dockerfile
Java stopped trusting after a JDK upgradethe upgrade replaced cacertskeytool -list — the alias is gonere-import, or switch to your own truststore via a JVM flag
Verify return code: 21 and only one certificate in the responsethe server sends the leaf onlygrep -c 'BEGIN CERTIFICATE'assemble a fullchain file and reload the web server
403 from the server, 200 from a laptopdata centre address filtering, not a certificate issuecompare curl exit codes: 0 versus 60test against a host that does not filter data centre ranges
Certificate not found on crt.shissuance is not logged in public CTcompare against the live server response via s_clientmaintain your own certificate registry

How to check it on enterno.io

  • SSL checker — chain, issuer, validity, protocol and cipher suite. Start here: the issuer field immediately tells you whether the root is domestic or international.
  • SSL error reference — when the browser already shows a specific error code.
  • Security scanner — headers, HSTS, the HTTPS redirect and mixed content.
  • Monitoring — an SSL monitor tracking validity, with a warning at 14 days and a critical alert at 3 days, delivered to Telegram.
  • HTTP header checker — the redirect chain from http to https and what the server really returns.

Related walkthroughs: cannot verify server certificate, DV, OV and EV certificate types, self-signed certificates, checking site availability from Russia and abroad.

FAQ

Is a Russian web CA the same as an electronic signature CA?

No. Different organisations, different certificates, different technology — only the Russian phrase is shared. A web TLS CA issues server certificates for websites. An accredited electronic signature CA issues certificates for signing documents; those work with a cryptographic provider and a hardware token and are never installed on a web server.

I have an electronic signature certificate on a token. Can I use it for my website?

No. Its Extended Key Usage declares a different purpose, the private key cannot be exported from the token by design, and a web server will not accept it as a server certificate. A website needs a separate TLS certificate with the serverAuth purpose.

Why does curl fail on the server while the browser opens the site fine?

Because those are two different trust stores. Your browser already has the root, either because you installed it or because the build shipped with it. Nobody touched the system bundle on the server. Install the root on the server separately, and then check whether your runtime uses its own store on top of that.

Is installing the root alone enough, without the intermediate?

If the server sends the intermediate itself, the root is enough. If it does not, the client has nowhere to obtain it. The safer approach is to do both: put the root and the intermediate into the anchors directory, and separately fix the fullchain on the server so you do not depend on every client's configuration.

How do I confirm the root actually landed in the system store?

On RHEL-family systems: trust list --filter=ca-anchors filtered by name. On Debian and Ubuntu: search the generated /etc/ssl/certs/ca-certificates.crt. The most reliable test is comparing openssl s_client with and without -CAfile: if the verdict is 0 (ok) only with the flag, the root is not installed system-wide.

Should I put a domestic certificate on a public API?

Usually not. A public API is called by other people's servers, whose trust stores you neither control nor can repair. Every one of those clients hits a path validation error that only they can fix. For public integrations this is an expensive choice.

Can CT logs help me track domestic certificate issuance?

Do not rely on it. Certificate Transparency logging obligations come from browser root programs, and certificates outside those programs are not covered. Replace crt.sh style inventory with your own registry plus monitoring based on what servers actually serve.

Does installing the root weaken my security posture?

Adding any root to a trust store means trusting that authority to vouch for any hostname, so treat it as a real decision rather than a formality. Install it on the machines that genuinely need to reach those endpoints, keep the change in configuration management so it is auditable, and prefer a dedicated truststore for a single service over a system-wide change when the scope is narrow.

Checklist

  • You have identified which kind of CA you actually need: web TLS for a site, or electronic signature for documents.
  • You know what share of your audience and which integrations lack the domestic root.
  • The server sends a full chain: leaf plus intermediates, with the BEGIN CERTIFICATE count greater than one.
  • Root and intermediate are installed in the system store on every server that talks to Russian endpoints.
  • The installation is captured in the Dockerfile or image configuration, not applied by hand inside a running container.
  • Runtimes with their own store have been handled: Java, Node.js, Python with requests.
  • You verified the install against a host that does not filter data centre addresses, not against gosuslugi.ru.
  • You can tell curl exit code 60 apart from an HTTP 403 delivered at exit code 0.
  • Expiry monitoring exists with at least a two-week lead time, and it separates "expiring" from "not trusted".
  • Renewal is on a calendar: ACME auto-renewal is usually unavailable here.

Check your certificate and its chain now: run an SSL check on enterno.io and enable expiry monitoring with Telegram alerts. If a browser is already showing an error, start from the SSL error reference.

Check your website right now

Check your site's SSL →
More articles: SSL/TLS
SSL/TLS
Expired SSL Certificate: How to Fix NET::ERR_CERT_DATE_INVALID
15.04.2026 · 692 views
SSL/TLS
SSL Certificate Chain: How It Works, How to Verify It and How to Fix an Incomplete Chain
15.04.2026 · 641 views
SSL/TLS
Weak Cipher Suites: How to Find and Disable Insecure TLS Ciphers
15.04.2026 · 610 views
SSL/TLS
SSL Handshake Failed: Root Causes and Step-by-Step Diagnosis
15.04.2026 · 607 views