Skip to content

Switch from certifi to truststore for default SSL verification#209

Merged
Kludex merged 3 commits into
mainfrom
truststore-v2
Jun 1, 2026
Merged

Switch from certifi to truststore for default SSL verification#209
Kludex merged 3 commits into
mainfrom
truststore-v2

Conversation

@Kludex

@Kludex Kludex commented May 11, 2026

Copy link
Copy Markdown
Member

Summary

Backports encode/httpx#3409 - default SSL verification now uses the system trust store via truststore instead of certifi's bundled CA list.

Behavior: when a user calls httpx2.Client() (or any verify=True default), the SSL context is now truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT). This means certificates added to the OS keychain (macOS) / cert store (Windows) / system trust roots (Linux) are honored, which matches what browsers and curl do.

Closes #104.

Important

Stacked on top of #208 (Python 3.10+ requirement). truststore requires Python 3.10. Merge #208 first.

Changes

  • src/httpx2/httpx2/_config.py: default verify=True path imports truststore instead of certifi and constructs truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT). The SSL_CERT_FILE/SSL_CERT_DIR env-var escape hatches are preserved.
  • src/httpx2/pyproject.toml deps: certifi -> truststore>=0.10.
  • tests/httpx2/test_config.py: removed 3 tests that asserted certifi-specific behavior (test_load_ssl_config_verify_existing_file, test_load_ssl_config_verify_directory, test_load_ssl_with_keylog). truststore's SSLContext doesn't expose keylog_filename, and asserting against certifi.where() no longer represents the default path. Unused certifi, typing, Path imports removed.

Test plan

  • uv run pytest: 1634 passed, 1 skipped, 0 failed.
  • scripts/check clean.

AI Disclaimer

This PR was developed with the assistance of either Claude or Codex. I've reviewed and verified the changes.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2d71f4ef82

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

# Default case...
ctx = ssl.create_default_context(cafile=certifi.where())
# Default case: rely on the system trust store via `truststore`.
ctx = truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve default SSL verify_flags in truststore contexts

Switching the default branch to truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT) drops the extra verification flags that ssl.create_default_context(...) applied before (VERIFY_X509_PARTIAL_CHAIN and VERIFY_X509_STRICT). This changes TLS validation behavior for verify=True users (notably on OpenSSL/Linux paths), potentially rejecting chains that previously worked and weakening strict cert checks. Please initialize the context with equivalent verify flags so verify=True keeps prior semantics.

Useful? React with 👍 / 👎.

# Default case...
ctx = ssl.create_default_context(cafile=certifi.where())
# Default case: rely on the system trust store via `truststore`.
ctx = truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Honor SSLKEYLOGFILE in default verify path

The previous default path used ssl.create_default_context(...), which automatically reads SSLKEYLOGFILE; this branch now constructs the context directly via truststore.SSLContext(...), so key logging is no longer enabled from the environment for normal verify=True usage. That is a regression for debugging/decryption workflows (for example Wireshark-based TLS troubleshooting). Consider explicitly propagating SSLKEYLOGFILE to the created context to preserve behavior.

Useful? React with 👍 / 👎.

Base automatically changed from py-3.10-to-3.14 to main May 12, 2026 18:38
# Conflicts:
#	pyproject.toml
#	src/httpcore2/pyproject.toml
#	tests/httpx2/test_config.py
#	uv.lock
@github-actions

github-actions Bot commented Jun 1, 2026

Copy link
Copy Markdown

@codspeed-hq

codspeed-hq Bot commented Jun 1, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 15 untouched benchmarks
⏩ 7 skipped benchmarks1


Comparing truststore-v2 (226970a) with main (32a423e)

Open in CodSpeed

Footnotes

  1. 7 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@Kludex Kludex changed the title Switch from certifi to truststore for default SSL verification Switch from certifi to truststore for default SSL verification Jun 1, 2026
@Kludex Kludex merged commit 4fe6b77 into main Jun 1, 2026
14 checks passed
@Kludex Kludex deleted the truststore-v2 branch June 1, 2026 10:07
@mbeijen

mbeijen commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

I think this is a really nice change! 👍

I'm interested to see what kind of and how much breakage this would cause -- for instance on docker containers without proper CA setup or embedded linux where certifi would work but the native CA does not really?

Of course, if you would be a developer experiencing issues with truststore you could still add certifi and use that!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Drop certifi, use system trust store by default

2 participants