One needs to be careful and construct the HTTPResponse properly before we ask GenerateHTTPRespone to produce the final thing so that it can be consumed by any HTTP client outside of WL world.
The following explicit MIME specification will solve the above problem.
res = HTTPResponse[
ExportString[Plot[x, {x, 0, 1}], "PNG"],
<|"ContentType" -> "image/png"|>
];
hr = GenerateHTTPResponse[res];
ImportByteArray@hr["BodyByteArray"]

Also as expected the following two ways will also produce the above image.
ImportString[hr["Body"], "PNG"]
ImportByteArray@ByteArray@hr["BodyBytes"]
Reason
The problem OP faced is just due to mime type getting messed up.
Note how the MIME type gets spoiled when we do:
GenerateHTTPResponse[
ExportByteArray[Plot[x, {x, 0, 1}], "PNG"]]["ContentType"]
"text/plain;charset=utf-8"
WL will not deduce the correct content type "image/png" from the above code.
However the above behavior is probably natural as a byte array is just a collection of byte and possess no extra metadata about it's content type. Until one supply such metadata to a processor of the given byte array (e.g the process of creating a HTTP response from the given byte array), it is forced to predict the content type before it can process. In our case GenerateHTTPResponse predicts the content type to default "text/plain;charset=utf-8" and things gets messed up.
Using ExportForm
If one wants to skip explicitly forming the HTTPResponse there is a shorter way possible as pointed by @Kuba here. It uses the ExportForm function that can successfully infer the content type and form the HTTP response.
expr = Plot[x, {x, 0, 1}]]
GenerateHTTPResponse @ ExportForm[expr, "PNG"]
GenerateHTTPResponse @ ExportForm[expr, "PNG"], it will set headers correctly. See related: 189803 – Kuba Nov 07 '19 at 06:11ExportFormis a great find. – Edmund Nov 07 '19 at 11:38CloudForm. – Edmund Nov 07 '19 at 13:19