HTTP/1.1 Header Casing

When handling HTTP/1.1, Envoy will normalize the header keys to be all lowercase. While this is compliant with the HTTP/1.1 spec, in practice this can result in issues when migrating existing systems that might rely on specific header casing.

To support these use cases, Envoy allows configuring a formatting scheme for the headers, which will have Envoy transform the header keys during serialization.

To configure this formatting on response headers, specify the format in the http_protocol_options. To configure this for upstream request headers, specify the formatting in http_protocol_options in the cluster’s extension_protocol_options.

Currently Envoy supports two mutually exclusive types of header key formatters:

Stateless formatters

Stateless formatters are run on encoding and do not depend on any previous knowledge of the headers. An example of this type of formatter is the proper case words formatter. These formatters are useful when converting from non-HTTP/1 to HTTP/1 (within a single proxy or across multiple hops) or when stateful formatting is not desired due to increased memory requirements.

Stateful formatters

Stateful formatters are instantiated on decoding, called for every decoded header, attached to the header map, and are then available during encoding to format the headers prior to writing. Thus, they traverse the entire proxy stack. An example of this type of formatter is the preserve case formatter configured via the stateful_formatter field. The following is an example configuration which will preserve HTTP/1 header case across the proxy.

  1. static_resources:
  2. listeners:
  3. - address:
  4. socket_address:
  5. address: 0.0.0.0
  6. port_value: 443
  7. filter_chains:
  8. - filters:
  9. - name: envoy.filters.network.http_connection_manager
  10. typed_config:
  11. "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
  12. stat_prefix: ingress_http
  13. http_protocol_options:
  14. header_key_format:
  15. stateful_formatter:
  16. name: preserve_case
  17. typed_config:
  18. "@type": type.googleapis.com/envoy.extensions.http.header_formatters.preserve_case.v3.PreserveCaseFormatterConfig
  19. http_filters:
  20. - name: envoy.filters.http.router
  21. route_config:
  22. virtual_hosts:
  23. - name: default
  24. domains: ["*"]
  25. routes:
  26. - match: {prefix: "/"}
  27. route:
  28. cluster: service_foo
  29. clusters:
  30. - name: service_foo
  31. typed_extension_protocol_options:
  32. envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
  33. "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
  34. explicit_http_config:
  35. http_protocol_options:
  36. header_key_format:
  37. stateful_formatter:
  38. name: preserve_case
  39. typed_config:
  40. "@type": type.googleapis.com/envoy.extensions.http.header_formatters.preserve_case.v3.PreserveCaseFormatterConfig
  41. load_assignment:
  42. cluster_name: some_service
  43. endpoints:
  44. - lb_endpoints:
  45. - endpoint:
  46. address:
  47. socket_address:
  48. address: 127.0.0.1
  49. port_value: 8080