ssl keylog_filename: Inspect TLS in Wireshark

Published on: October 2, 2026
Reading time: 5 minutes
Laptop screen with code for TLS analysis using Python ssl keylog_filename

Troubleshooting HTTPS is difficult because TLS hides the exact bytes that developers often need to inspect. Python’s SSLContext.keylog_filename attribute provides a controlled way to record session secrets in the NSS Key Log format. Wireshark can combine those secrets with a packet capture and decrypt authorized TLS sessions.

The feature does not turn HTTPS into plain HTTP, disable certificate checks, or modify traffic on the wire. Packets remain encrypted. Decryption is possible only for an analyst who has both the matching capture and the key log file. This makes the feature valuable for development, integration testing, API debugging, protocol analysis, and diagnosing TLS interoperability problems.

What keylog_filename does

keylog_filename belongs to an ssl.SSLContext. When you assign a writable path before the handshake, Python appends TLS secrets to that file. The format is understood by browsers and network tools. Depending on the negotiated TLS version, the log may contain CLIENT_RANDOM records or TLS 1.3 traffic-secret labels.

Support depends on the OpenSSL library used by the Python build. Modern installations commonly support it on Linux, macOS, and Windows, but production code should not assume availability without checking the target environment.

Basic urllib example

import ssl
import urllib.request

context = ssl.create_default_context()
context.keylog_filename = "tls-keys.log"

with urllib.request.urlopen(
    "https://www.python.org/",
    context=context,
    timeout=10,
) as response:
    print(response.status)
    print(response.read(200))

The default context keeps hostname verification and certificate validation enabled. The only additional behavior is writing session secrets. Start a packet capture before running the script, then configure Wireshark to read the generated file.

Configure Wireshark

Open Wireshark preferences, find the TLS protocol settings, and set the key log filename to the absolute path of tls-keys.log. Reload the capture or start a new one. Filters such as tls, http2, and http help locate decrypted application data.

The official Python ssl documentation describes the context API. The Wireshark TLS guide explains key-log configuration and common capture limitations.

Raw socket example

import socket
import ssl

context = ssl.create_default_context()
context.keylog_filename = "session-secrets.log"

with socket.create_connection(("www.python.org", 443), timeout=10) as raw:
    with context.wrap_socket(raw, server_hostname="www.python.org") as secure:
        secure.sendall(
            b"GET / HTTP/1.1\r\nHost: www.python.org\r\nConnection: close\r\n\r\n"
        )
        data = secure.recv(4096)
        print(data.decode("latin-1", errors="replace"))

Set the filename before wrap_socket performs the handshake. Enabling it afterward cannot reconstruct secrets for an already negotiated session.

SSLKEYLOGFILE environment variable

Some applications honor the SSLKEYLOGFILE environment variable. Python-created default contexts may use it in supported situations. Explicit configuration is usually clearer in tests because it documents intent and avoids relying on global process state.

export SSLKEYLOGFILE="$PWD/tls-keys.log"
python client.py

For temporary diagnostic files, use a protected directory and delete the file after the test. The Academify guide to Python tempfile covers temporary-file lifecycles, while Python pathlib helps manage paths reliably.

Treat the file as a secret

A TLS key log is highly sensitive. Anyone who obtains the matching capture and key log may decrypt recorded sessions. Never commit it to Git, attach it to a public issue, or upload it to an untrusted support portal. Add patterns such as *.keylog, tls-keys.log, and session-secrets.log to .gitignore.

Avoid enabling the feature in production. Long-running services can generate large files containing secrets for many users. This creates disclosure, retention, disk-space, and concurrency risks. Restrict file permissions to the service account and keep the diagnostic window short.

A safer helper

from pathlib import Path
import ssl


def create_debug_context(path: Path, enabled: bool = False) -> ssl.SSLContext:
    context = ssl.create_default_context()
    if enabled:
        path.parent.mkdir(parents=True, exist_ok=True)
        context.keylog_filename = str(path)
    return context


context = create_debug_context(
    Path(".debug") / "tls-keys.log",
    enabled=True,
)

An explicit flag reduces accidental activation. In larger systems, connect the flag to a local-only setting and reject it during production startup.

Using httpx

import ssl
import httpx

context = ssl.create_default_context()
context.keylog_filename = "httpx-tls.log"

with httpx.Client(verify=context, timeout=10) as client:
    response = client.get("https://www.python.org/")
    print(response.status_code)

Some HTTP clients accept an SSLContext directly; others require a custom transport or adapter. Review the Academify tutorials on urllib.request and Python requests to distinguish application, DNS, TCP, certificate, and protocol failures.

Common problems

An empty file often means the path was not writable, no new handshake occurred, or the connection pool reused an existing TLS session. Close the client, disable pooling for the test, and force a fresh connection. A packet capture from the wrong network interface is another common cause, especially with containers, VPNs, virtual machines, and WSL.

Old key logs cannot decrypt new sessions, and new key logs cannot decrypt earlier captures unless the matching session secrets were recorded. The capture and key file must correspond to the same handshakes.

TLS 1.3 and HTTP/2

TLS 1.3 uses multiple traffic secrets, which the NSS format represents with dedicated labels. Recent Wireshark versions understand those labels. After TLS decryption, Wireshark still needs to decode the application protocol. Many HTTPS APIs negotiate HTTP/2 through ALPN, so decrypted traffic may appear as HTTP/2 frames rather than familiar HTTP/1.1 text.

Responsible use

Use this capability only on systems, accounts, and networks you are authorized to inspect. It is appropriate for your own services, test environments, staging systems, and approved incident response. It is not appropriate for intercepting third-party traffic, collecting credentials, or bypassing organizational controls.

Practical checklist

  • Keep certificate verification enabled.
  • Set the path before the TLS handshake.
  • Use an absolute path in Wireshark.
  • Capture the correct network interface.
  • Force a fresh connection when necessary.
  • Protect the file with restrictive permissions.
  • Delete the key log after diagnosis.
  • Never commit it to source control.

Conclusion

SSLContext.keylog_filename is one of the most effective Python tools for authorized TLS troubleshooting. It preserves normal encryption and validation while giving Wireshark the secrets needed to inspect a matching capture. Used with a dedicated context, strict file protection, and a short diagnostic window, it can reveal HTTP headers, redirects, ALPN negotiation, HTTP/2 frames, and API behavior that would otherwise remain hidden.

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    Laptop with code and SQLite database for Python sqlite3 autocommit
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    sqlite3 autocommit: Control Transactions in Python

    Learn Python sqlite3 autocommit for explicit transactions, commits, rollbacks, compatibility, and safer SQLite locking.

    Ler mais

    Tempo de leitura: 5 minutos
    02/10/2026
    Developer working with immutable objects and Python copy.replace
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    copy.replace: Update Immutable Objects in Python

    Learn Python copy.replace to create new object versions with targeted changes, immutable state, validation, and predictable code.

    Ler mais

    Tempo de leitura: 5 minutos
    01/10/2026
    Code and file structure illustrating Python pathlib.Path.info
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    pathlib.Path.info: Cached File Metadata

    Learn pathlib.Path.info in Python to classify files with cached metadata, scan directories efficiently, and avoid unnecessary system calls.

    Ler mais

    Tempo de leitura: 6 minutos
    01/10/2026
    Laptop with Python testing material for asyncio loop_factory
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    loop_factory: Isolate Event Loops in asyncio Tests

    Learn loop_factory in IsolatedAsyncioTestCase for isolated, predictable asyncio tests with reliable cleanup.

    Ler mais

    Tempo de leitura: 5 minutos
    30/09/2026
    Developer navigating ZIP archive files with Python zipfile.Path
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    zipfile.Path: Browse ZIP Files Without Extraction

    Learn Python zipfile.Path to navigate, read, and validate files inside ZIP archives without extracting everything.

    Ler mais

    Tempo de leitura: 5 minutos
    30/09/2026
    Programmer working with Python email headers
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    email.headerregistry: Safer Structured Email Headers

    Learn Python email.headerregistry for structured headers, addresses, groups, dates, parameters, parsing, and safer email generation.

    Ler mais

    Tempo de leitura: 5 minutos
    29/09/2026