HttpClientFactory logging redacts header values by default

HttpClientFactory's default logging includes Trace-level logs that output all the request and response headers. The RedactLoggedHeaders method lets you specify which of these headers are sensitive. The values of those headers aren't logged (the header names remain, but the values are substituted with *). Previously, if none of the headers were specified as sensitive with RedactLoggedHeaders, all headers were considered non-sensitive and were logged with all their values as-is. Starting in .NET 9, unless specified, all headers are instead considered sensitive and all the values are redacted by default.

Previous behavior

Previously, if RedactLoggedHeaders wasn't called, all the headers were logged as-is. If RedactLoggedHeaders was called, the specified headers were redacted, and other headers were logged as-is.

services.AddHttpClient("default", ...); // 1

services.AddHttpClient("redacted-predicate", ...) // 2
    .RedactLoggedHeaders(h => h.StartsWith("Auth") || h.StartsWith("X-"));

services.AddHttpClient("redacted-collection", ...) // 3
    .RedactLoggedHeaders(new[] { "Authorization", "X-Sensitive", });

(1) Default—not redacted

trce: System.Net.Http.HttpClient.default.ClientHandler[102]
      Request Headers:
      Authorization: NTLM blob
      X-Sensitive: some, secret, values
      X-Other: some, other, values
      Cache-Control: no-cache

(2) Redacted with predicate

trce: System.Net.Http.HttpClient.redacted-predicate.ClientHandler[102]
      Request Headers:
      Authorization: *
      X-Sensitive: *
      X-Other: *
      Cache-Control: no-cache

(3) Redacted with collection

trce: System.Net.Http.HttpClient.redacted-collection.ClientHandler[102]
      Request Headers:
      Authorization: *
      X-Sensitive: *
      X-Other: some, other, values
      Cache-Control: no-cache

New behavior

Starting in .NET 9, if RedactLoggedHeaders isn't called, all the headers are redacted. If RedactLoggedHeaders is called, the specified headers are redacted.

services.AddHttpClient("default", ...); // 1 <--- CHANGED

services.AddHttpClient("redacted-predicate", ...) // 2 <--- remains the same
    .RedactLoggedHeaders(h => h.StartsWith("Auth") || h.StartsWith("X-"));

services.AddHttpClient("redacted-collection", ...) // 3 <--- remains the same
    .RedactLoggedHeaders(new[] { "Authorization", "X-Sensitive", });

(1) Default—all are redacted

trce: System.Net.Http.HttpClient.default.ClientHandler[102]
      Request Headers:
      Authorization: *
      X-Sensitive: *
      X-Other: *
      Cache-Control: *

Version introduced

.NET 9 RC 1

Type of breaking change

This change is a behavioral change.

Reason for change

If headers aren't specified as sensitive, sensitive information can be exposed in logs. This change makes the logging safe by default.

If you want to log the headers, assess which headers might contain sensitive information and explicitly specify them using the RedactLoggedHeaders API. You can redact headers per client, or globally for all clients by using the ConfigureHttpClientDefaults(IServiceCollection, Action<IHttpClientBuilder>).

Consider using an "allow-list" approach rather than a "block-list" approach.

If you strongly believe none of your headers can contain sensitive information, or for internal debug purposes, you can disable the redaction completely by passing a delegate that returns false.

private static readonly string[] SafeToLogHeaders = ....;
private static readonly string[] SuperSecretHeaders = ....;

// Specify for all clients.
services.ConfigureHttpClientDefaults(b =>
    b.RedactLoggedHeaders(h =>
        Array.IndexOf(SafeToLogHeaders, h) == -1)); // log values only for SafeToLogHeaders

// "globally" specified RedactLoggedHeaders can be overriden per-name.
// NOTE: RedactLoggedHeaders completely replaces the previously specified check.
services.AddHttpClient("override")
    .RedactLoggedHeaders(SuperSecretHeaders); // log all values except SuperSecretHeaders

// -OR-

// (dangerous) Disable header value redaction for all clients.
services.ConfigureHttpClientDefaults(b => b.RedactLoggedHeaders(_ => false));

Affected APIs