Code change:
MUST NOT forward fields that contain any of the values that are listed as prohibited above. <del> A recipient MAY treat a message that contains a field name or value that includes other characters disallowed by HTTP and HTTP as malformed. When a request message violates one of the requirements above, it SHOULD be responded to using the 400 (Bad Request) status code (HTTP) before the stream is reset, unless a more suitable status code is defined, or the status code cannot be sent (e.g., because the error occurs in a trailer field). Note that field values that are not valid according to the definition of the corresponding field do not cause a request to be malformed; the requirements above only apply to the generic syntax for field values as defined in HTTP. </del> <ins> When a request message violates one of these requirements, an implementation SHOULD generate a HTTP, unless a more suitable status code is defined or the status code cannot be sent (e.g., because the error occurs in a trailer field). </ins> 8.2.2.

Related discussion:
text: I like this paragraph and I'm totally OK with a spec that has conditions like "implementations which reassemble elements as an HTTP/1-like message before ...." However, it is not just HTTP/1 as there are many HTTP semantic layers that are unaware of the protocol version, so it needs to be any impl that represents streams as HTTP messages must do specific validation whilst the h2 "abusers" are free use streams to the limites of HPACK capabilities, so long as they don't represent the results as HTTP messages - either on the wire or to layers above. Furthermore an impl that is representing HTTP messages cannot just do the minimal safety checks you listed. It MUST validate the fields against the general syntax of HTTP fields. For example, if I provide a h2 implementation of the Java class, then my impl MUST validate the fields against HTTP syntax. But if instead, I use my h2 impl to implement a gRPC API, then I'm under no such obligation as I'm not representing HTTP messages. More over, it is only the protocol impl that can do this validation. Another example is that my server is used on PAAS platforms where the application was deployed decades ago and the source code is probably lost and the dev team all retired. The app cannot be redeployed and it certainly cannot be reviewed for how it parses the Cookie header. It's not sufficient for me to switch them from HTTP/1 to H2 and in the process expose those applications to non-valid HTTP headers in HTTP messages I deliver to them. If the h2 impl is not going to validate the fields are valid HTTP then who is? So how about something like:
The thing is, stacks that support multiple versions will certainly not implement the full header syntax in each version because that is exactly what ought to be done at the semantics layer and why the HTTP spec was split like this as well. We really have messaging (H1,H2,H3) and semantics. The transport explains how to extract elements from a byte stream and how to encode them into a byte stream. The semantics details their syntax and consistency. NUL/CR/LF purely come from the H1 world but H1 was HTTP before H2 existed, so it could be argued that they are legacy limitations that probably affects most stacks. The cookie header splitting is purely H2, and once recombined it must parse correctly according to the semantics definition.