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

    Python code representing functools.Placeholder and positional arguments
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    functools.Placeholder: Flexible Arguments in partial

    Learn functools.Placeholder in Python 3.14 to build flexible partial and partialmethod calls with open positional arguments.

    Ler mais

    Tempo de leitura: 5 minutos
    17/10/2026
    Code on a screen representing Python t-strings
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python 3.14 T-strings: Structured Templates

    Learn Python t-strings in Python 3.14, inspect Template and Interpolation objects, and build structured, extensible renderers.

    Ler mais

    Tempo de leitura: 5 minutos
    16/10/2026
    CPU and memory modules representing Python shared_memory track
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    shared_memory track: Control the Resource Tracker

    Learn Python shared_memory track for safe resource tracking, ownership, cleanup, and independent process coordination.

    Ler mais

    Tempo de leitura: 5 minutos
    16/10/2026
    Data storage unit representing compression with Python compression.zstd
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    compression.zstd: Streaming and Safe Compression

    Learn Python compression.zstd for fast file and data compression with streaming, dictionaries, testing, and safe limits.

    Ler mais

    Tempo de leitura: 6 minutos
    15/10/2026
    Code on screen for diagnosing processes with Python sys.remote_exec
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    sys.remote_exec: Diagnose Running Python Processes

    Learn Python sys.remote_exec for diagnosing running processes with controlled scripts, strong permissions, and safe operational practices.

    Ler mais

    Tempo de leitura: 6 minutos
    15/10/2026
    Python code and environment configuration with os.reload_environ
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    os.reload_environ: Thread Safety and Environment

    Learn Python os.reload_environ to refresh externally changed environment variables with validation, compatibility, and thread-safety precautions.

    Ler mais

    Tempo de leitura: 4 minutos
    14/10/2026