diff --git a/litellm/litellm_core_utils/litellm_logging.py b/litellm/litellm_core_utils/litellm_logging.py index 9e0461e951d..fc28847682e 100644 --- a/litellm/litellm_core_utils/litellm_logging.py +++ b/litellm/litellm_core_utils/litellm_logging.py @@ -7108,13 +7108,18 @@ class StandardLoggingPayloadSetup: headers: Final = proxy_server_request.get("headers", {}) if not isinstance(headers, dict): return None - request_headers: Final = cast( # cast-ok: request headers are untyped framework data - dict[str, str], headers - ) - header_tags: Final = [] + # Headers are case-insensitive per RFC 7230 / RFC 9110. + # Normalize header keys to lowercase to ensure tags are captured regardless of request casing. + normalized_headers: Final[dict[str, str]] = { + str(k).lower(): str(v) + for k, v in headers.items() + if v is not None + } + + header_tags: Final[list[str]] = [] for header_name in extra_headers: - header_value = request_headers.get(header_name) + header_value = normalized_headers.get(header_name.lower()) if header_value: header_tags.append(f"{header_name}: {header_value}") diff --git a/tests/unit/litellm_core_utils/test_litellm_logging.py b/tests/unit/litellm_core_utils/test_litellm_logging.py index 561ac9b2b33..8646bd2d52d 100644 --- a/tests/unit/litellm_core_utils/test_litellm_logging.py +++ b/tests/unit/litellm_core_utils/test_litellm_logging.py @@ -3662,6 +3662,21 @@ def test_get_extra_header_tags(): assert "x-custom: my-value" in result assert "x-empty:" not in str(result) + # Test case 8: Case-insensitive header matching (RFC 7230 / RFC 9110) + litellm.extra_spend_tag_headers = ["x-session-id", "X-Organization-ID"] + result = StandardLoggingPayloadSetup._get_extra_header_tags( + proxy_server_request={ + "headers": { + "X-Session-Id": "sess_123", + "x-organization-id": "org_456", + } + } + ) + assert result is not None + assert len(result) == 2 + assert "x-session-id: sess_123" in result + assert "X-Organization-ID: org_456" in result + finally: # Restore original value if original_extra_headers is not None: