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: But if we nomarlize that, we are putting in jeopardy all existing code written to HTTP semantics that may not even be aware of the version of HTTP carrying the fields that they are interpretting. Surely there is some other way to allow h2 framing to be used to carry non-HTTP streams without putting all existing HTTP code at risk? If we don't want developers abusing the current protocol, then give them a proper solution for their use-case rather than corrupt the existing primary use-case.
They should represent the exact same level of risk as having the same code parse HTTP/1. But actually that might in fact be what we're all seeking without precisely realizing it, which is making sure an H2 message can be at least reconstructed as an H1 message that is safe to parse. The rules on CR/LF precisely have always been a concern to all of us for this. Same as above. This is always difficult to say if we'd try to extend what HTTP permits. However we're certain that some will almost always be dangerous along a chain or cause parsing issues even inside implementations. Not exactly. This is actually the current situation probably because some consider that the effort needed to comply with the rules in semantics is too high for little value the this level. This is something I can understand. For example in haproxy we have a semantics layer which performs a lot of controls, deduplicates content-length, checks Host against etc. But at the lowest level it is difficult to enforce such checks when you're just converting header lists to internal messages. However we already have the NUL/CR/LF checks that allows the upper layer parser to proceed (the initial implementation used to build an H1 message but that's no longer the case). Thus maybe we should replace all this with an approach centered around this tricky HTTP/1 translation, which also does not infer how implementations should work internally nor suggest to put anything non-HTTP inside HEADERS frames: Just to be clear, I really do not want to see HEADERS frame being abused to carry non-HTTP because I know pretty well what it's like to be an intermediary developer whose product is pointed the finger at for breaking stuff when inserted in a working but non-compliant chain. This is also why I would like to see the SHOULD reject as malformed.