Skip to content

Redact HTTP headers

Headers get logged at DEBUG and TRACE, and several of them carry a credential in full. string will not save you here — it works on free-form text and has no idea what a header name is. Redacting headers is an explicit, separate call.

Mask the values before logging a header map

Test the name, then mask the value:

use rtb_redact::{is_sensitive_header, redact_header_value};

fn log_headers<'a>(headers: impl Iterator<Item = (&'a str, &'a str)>) {
    for (name, value) in headers {
        let shown = if is_sensitive_header(name) {
            redact_header_value(value)
        } else {
            value.to_string()
        };
        tracing::debug!(header = name, value = %shown);
    }
}

is_sensitive_header is ASCII-case-insensitive, so Authorization, AUTHORIZATION and authorization all match. redact_header_value returns [redacted] for anything non-empty and an empty string for an empty value — it never inspects the value, so nothing can leak through a partial match.

Which headers are covered

Ten names, listed in full in the API reference: authorization, proxy-authorization, cookie, set-cookie, x-api-key, x-auth-token, x-amz-security-token, x-goog-api-key, x-anthropic-api-key, x-openai-api-key.

Do not query the set directly — SENSITIVE_HEADERS.contains("Authorization") is false, because every entry is stored lowercase and the underlying phf::Set is case-sensitive. Go through is_sensitive_header.

Add the headers your service treats as secret

The set is fixed at compile time and cannot be extended. x-gitlab-token, x-hub-signature, x-shopify-hmac-sha256 and every internal header your own services mint are not in it.

Layer your own names on top:

use rtb_redact::is_sensitive_header;

const EXTRA_SENSITIVE: &[&str] = &["x-gitlab-token", "x-internal-session"];

fn sensitive(name: &str) -> bool {
    is_sensitive_header(name)
        || EXTRA_SENSITIVE.iter().any(|h| h.eq_ignore_ascii_case(name))
}

Keep the local list in one place, next to the code that logs headers, rather than repeating it per call site. If the name is a widely-used one rather than something internal, it belongs in the crate — raise it on the project on GitLab.

Do not rely on string for a header line pasted into text

A header that arrives as part of an error message or a raw request dump is just text, and only the ordinary rules apply to it:

in : x-api-key: sk-abcdef1234567890abcdef    out: x-api-key: [redacted]
in : Cookie: session=abc123; theme=dark      out: unchanged

The first is redacted because the value matches the sk- prefix rule, not because the header name is known. The second keeps its session cookie in full: session is not a recognised query-parameter name and the value is too short for the opaque-run rule.

If you are logging a request dump, mask the headers before you build the dump string.