Is it correct to response to HEAD req from elastic API with empty "transfer-encoding: chunked"?

Hello, we're troubleshooting issue in communication between client and elastic 8.13 in between with HAProxy.
Is it correct to response to HEAD request with EMPTY "transfer-encoding: chunked"? In other words - to HEAD request to elastic API, the response is "transfer-encoding: chunked" without any data.

Thanks
Jiri

Yes, this is correct behaviour. See e.g. RFC 9110 §9.3.2:

The server SHOULD send the same header fields in response to a HEAD request as it would have sent if the request method had been GET.

That's fine but there with NO DATA - RFC 9112 RFC 9112 - HTTP/1.1

The chunked transfer coding wraps content in order to transfer it as a series of chunks, each with its own size indicator, followed by an OPTIONAL trailer section containing trailer fields.

And it does not have size indicator because it has no data.

No, that's not the case. HEAD requests are special in this regard. See e.g. RFC 9112 §6.3:

  1. Any response to a HEAD request [...] is always terminated by the first empty line after the header fields, regardless of the header fields present in the message, [...]

Isn't it the same issue as it was here?

github issue

No, that issue was a bug, long-since fixed. IIRC we fixed it before it had any real-world impact.

Also from §6.1 of RFC9112

Transfer-Encoding MAY be sent in a response to a HEAD request or in a 304 (Not Modified) response (Section 15.4.5 of [HTTP]) to a GET request, neither of which includes a message body, to indicate that the origin server would have applied a transfer coding to the message body if the request had been an unconditional GET. This indication is not required, however, because any recipient on the response chain (including the origin server) can remove transfer codings when they are not needed.

That seems to cover exactly this scenario (please correct me if I've misunderstood the question) and the ES behaviour is explicitly allowed (though not required).

1 Like

Thanks Tim, that's a good quote too, although it's only a MAY ...

... whereas RFC 9110 §9.3.2 says its a SHOULD.

RFC9112 7.1

The chunked transfer coding wraps content in order to transfer it as a series of chunks, each with its own size indicator, followed by an OPTIONAL trailer section containing trailer fields.

So if there is transfer-encoding chunked, there must be size indicator.

No, that does not contradict what we've said above. RFC9112 §7.1 says:

The chunked transfer coding wraps content ...

A response to a HEAD request has no content, regardless of the header fields, so there's nothing to wrap. See my previous message which links to the bit of the spec that says unambigously that such a response is "always terminated by the first empty line after the header fields, regardless of the header fields present in the message". There is no other way to interpret this.

By way of an empirical demonstration, the behaviour of curl agrees with what we're saying. With the empty body it works fine to send multiple requests on the same connection:

$ curl -vvv --head http://127.0.0.1:9123/foo http://127.0.0.1:9123/bar http://127.0.0.1:9123/baz
*   Trying 127.0.0.1:9123...
* Connected to 127.0.0.1 (127.0.0.1) port 9123
> HEAD /foo HTTP/1.1
> Host: 127.0.0.1:9123
> User-Agent: curl/8.7.1
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 200 OK
HTTP/1.1 200 OK
< Content-type: application/json
Content-type: application/json
< Transfer-encoding: chunked
Transfer-encoding: chunked
<

* Connection #0 to host 127.0.0.1 left intact
* Found bundle for host: 0x600002508f60 [serially]
* Can not multiplex, even if we wanted to
* Re-using existing connection with host 127.0.0.1
> HEAD /bar HTTP/1.1
> Host: 127.0.0.1:9123
> User-Agent: curl/8.7.1
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 200 OK
HTTP/1.1 200 OK
< Content-type: application/json
Content-type: application/json
< Transfer-encoding: chunked
Transfer-encoding: chunked
<

* Connection #0 to host 127.0.0.1 left intact
* Found bundle for host: 0x600002508f60 [serially]
* Can not multiplex, even if we wanted to
* Re-using existing connection with host 127.0.0.1
> HEAD /baz HTTP/1.1
> Host: 127.0.0.1:9123
> User-Agent: curl/8.7.1
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 200 OK
HTTP/1.1 200 OK
< Content-type: application/json
Content-type: application/json
< Transfer-encoding: chunked
Transfer-encoding: chunked
<

* Connection #0 to host 127.0.0.1 left intact

The raw bytes all look good:

00000000  48 45 41 44 20 2f 66 6f  6f 20 48 54 54 50 2f 31   HEAD /fo o HTTP/1
00000010  2e 31 0d 0a 48 6f 73 74  3a 20 31 32 37 2e 30 2e   .1..Host : 127.0.
00000020  30 2e 31 3a 39 31 32 33  0d 0a 55 73 65 72 2d 41   0.1:9123 ..User-A
00000030  67 65 6e 74 3a 20 63 75  72 6c 2f 38 2e 37 2e 31   gent: cu rl/8.7.1
00000040  0d 0a 41 63 63 65 70 74  3a 20 2a 2f 2a 0d 0a 0d   ..Accept : */*...
00000050  0a                                                 .
    00000000  48 54 54 50 2f 31 2e 31  20 32 30 30 20 4f 4b 0d   HTTP/1.1  200 OK.
    00000010  0a                                                 .
    00000011  43 6f 6e 74 65 6e 74 2d  74 79 70 65 3a 20 61 70   Content- type: ap
    00000021  70 6c 69 63 61 74 69 6f  6e 2f 6a 73 6f 6e 0d 0a   plicatio n/json..
    00000031  54 72 61 6e 73 66 65 72  2d 65 6e 63 6f 64 69 6e   Transfer -encodin
    00000041  67 3a 20 63 68 75 6e 6b  65 64 0d 0a               g: chunk ed..
    0000004D  0d 0a                                              ..
00000051  48 45 41 44 20 2f 62 61  72 20 48 54 54 50 2f 31   HEAD /ba r HTTP/1
00000061  2e 31 0d 0a 48 6f 73 74  3a 20 31 32 37 2e 30 2e   .1..Host : 127.0.
00000071  30 2e 31 3a 39 31 32 33  0d 0a 55 73 65 72 2d 41   0.1:9123 ..User-A
00000081  67 65 6e 74 3a 20 63 75  72 6c 2f 38 2e 37 2e 31   gent: cu rl/8.7.1
00000091  0d 0a 41 63 63 65 70 74  3a 20 2a 2f 2a 0d 0a 0d   ..Accept : */*...
000000A1  0a                                                 .
    0000004F  48 54 54 50 2f 31 2e 31  20 32 30 30 20 4f 4b 0d   HTTP/1.1  200 OK.
    0000005F  0a                                                 .
    00000060  43 6f 6e 74 65 6e 74 2d  74 79 70 65 3a 20 61 70   Content- type: ap
    00000070  70 6c 69 63 61 74 69 6f  6e 2f 6a 73 6f 6e 0d 0a   plicatio n/json..
    00000080  54 72 61 6e 73 66 65 72  2d 65 6e 63 6f 64 69 6e   Transfer -encodin
    00000090  67 3a 20 63 68 75 6e 6b  65 64 0d 0a               g: chunk ed..
    0000009C  0d 0a                                              ..
000000A2  48 45 41 44 20 2f 62 61  7a 20 48 54 54 50 2f 31   HEAD /ba z HTTP/1
000000B2  2e 31 0d 0a 48 6f 73 74  3a 20 31 32 37 2e 30 2e   .1..Host : 127.0.
000000C2  30 2e 31 3a 39 31 32 33  0d 0a 55 73 65 72 2d 41   0.1:9123 ..User-A
000000D2  67 65 6e 74 3a 20 63 75  72 6c 2f 38 2e 37 2e 31   gent: cu rl/8.7.1
000000E2  0d 0a 41 63 63 65 70 74  3a 20 2a 2f 2a 0d 0a 0d   ..Accept : */*...
000000F2  0a                                                 .
    0000009E  48 54 54 50 2f 31 2e 31  20 32 30 30 20 4f 4b 0d   HTTP/1.1  200 OK.
    000000AE  0a                                                 .
    000000AF  43 6f 6e 74 65 6e 74 2d  74 79 70 65 3a 20 61 70   Content- type: ap
    000000BF  70 6c 69 63 61 74 69 6f  6e 2f 6a 73 6f 6e 0d 0a   plicatio n/json..
    000000CF  54 72 61 6e 73 66 65 72  2d 65 6e 63 6f 64 69 6e   Transfer -encodin
    000000DF  67 3a 20 63 68 75 6e 6b  65 64 0d 0a               g: chunk ed..
    000000EB  0d 0a                                              ..

Adding an empty chunk marker causes an error on the second request and forces it to use a new connection for the third:

$ curl -vvv --head http://127.0.0.1:9123/foo http://127.0.0.1:9123/bar http://127.0.0.1:9123/baz
*   Trying 127.0.0.1:9123...
* Connected to 127.0.0.1 (127.0.0.1) port 9123
> HEAD /foo HTTP/1.1
> Host: 127.0.0.1:9123
> User-Agent: curl/8.7.1
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 200 OK
HTTP/1.1 200 OK
< Content-type: application/json
Content-type: application/json
< Transfer-encoding: chunked
Transfer-encoding: chunked
<

* Connection #0 to host 127.0.0.1 left intact
* Found bundle for host: 0x600000b789c0 [serially]
* Can not multiplex, even if we wanted to
* Re-using existing connection with host 127.0.0.1
> HEAD /bar HTTP/1.1
> Host: 127.0.0.1:9123
> User-Agent: curl/8.7.1
> Accept: */*
>
* Request completely sent off
* Invalid status line
* Closing connection
curl: (8) Invalid status line
* Hostname 127.0.0.1 was found in DNS cache
*   Trying 127.0.0.1:9123...
* Connected to 127.0.0.1 (127.0.0.1) port 9123
> HEAD /baz HTTP/1.1
> Host: 127.0.0.1:9123
> User-Agent: curl/8.7.1
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 200 OK
HTTP/1.1 200 OK
< Content-type: application/json
Content-type: application/json
< Transfer-encoding: chunked
Transfer-encoding: chunked
<

* Connection #1 to host 127.0.0.1 left intact

NB "Invalid status line" and "Closing connection". The raw bytes:

00000000  48 45 41 44 20 2f 66 6f  6f 20 48 54 54 50 2f 31   HEAD /fo o HTTP/1
00000010  2e 31 0d 0a 48 6f 73 74  3a 20 31 32 37 2e 30 2e   .1..Host : 127.0.
00000020  30 2e 31 3a 39 31 32 33  0d 0a 55 73 65 72 2d 41   0.1:9123 ..User-A
00000030  67 65 6e 74 3a 20 63 75  72 6c 2f 38 2e 37 2e 31   gent: cu rl/8.7.1
00000040  0d 0a 41 63 63 65 70 74  3a 20 2a 2f 2a 0d 0a 0d   ..Accept : */*...
00000050  0a                                                 .
    00000000  48 54 54 50 2f 31 2e 31  20 32 30 30 20 4f 4b 0d   HTTP/1.1  200 OK.
    00000010  0a                                                 .
    00000011  43 6f 6e 74 65 6e 74 2d  74 79 70 65 3a 20 61 70   Content- type: ap
    00000021  70 6c 69 63 61 74 69 6f  6e 2f 6a 73 6f 6e 0d 0a   plicatio n/json..
    00000031  54 72 61 6e 73 66 65 72  2d 65 6e 63 6f 64 69 6e   Transfer -encodin
    00000041  67 3a 20 63 68 75 6e 6b  65 64 0d 0a               g: chunk ed..
    0000004D  0d 0a                                              ..
00000051  48 45 41 44 20 2f 62 61  72 20 48 54 54 50 2f 31   HEAD /ba r HTTP/1
00000061  2e 31 0d 0a 48 6f 73 74  3a 20 31 32 37 2e 30 2e   .1..Host : 127.0.
00000071  30 2e 31 3a 39 31 32 33  0d 0a 55 73 65 72 2d 41   0.1:9123 ..User-A
00000081  67 65 6e 74 3a 20 63 75  72 6c 2f 38 2e 37 2e 31   gent: cu rl/8.7.1
00000091  0d 0a 41 63 63 65 70 74  3a 20 2a 2f 2a 0d 0a 0d   ..Accept : */*...
000000A1  0a                                                 .
    0000004F  30 0d 0a                                           0..
    00000052  0d 0a                                              ..
    00000054  48 54 54 50 2f 31 2e 31  20 32 30 30 20 4f 4b 0d   HTTP/1.1  200 OK.
    00000064  0a                                                 .
    00000065  43 6f 6e 74 65 6e 74 2d  74 79 70 65 3a 20 61 70   Content- type: ap
    00000075  70 6c 69 63 61 74 69 6f  6e 2f 6a 73 6f 6e 0d 0a   plicatio n/json..
    00000085  54 72 61 6e 73 66 65 72  2d 65 6e 63 6f 64 69 6e   Transfer -encodin
    00000095  67 3a 20 63 68 75 6e 6b  65 64 0d 0a               g: chunk ed..
    000000A1  0d 0a                                              ..

Note that curl sends the second request before it even receives the 0\r\n\r\n. This is not pipelining, curl hasn't supported that for years. It's doing this because it got to the end of the response.

For completeness, if they were GET requests then the empty chunk markers are required of course:

$ curl -vvv http://127.0.0.1:9123/foo http://127.0.0.1:9123/bar http://127.0.0.1:9123/baz
*   Trying 127.0.0.1:9123...
* Connected to 127.0.0.1 (127.0.0.1) port 9123
> GET /foo HTTP/1.1
> Host: 127.0.0.1:9123
> User-Agent: curl/8.7.1
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 200 OK
< Content-type: application/json
< Transfer-encoding: chunked
<
* Connection #0 to host 127.0.0.1 left intact
* Found bundle for host: 0x600000d186c0 [serially]
* Can not multiplex, even if we wanted to
* Re-using existing connection with host 127.0.0.1
> GET /bar HTTP/1.1
> Host: 127.0.0.1:9123
> User-Agent: curl/8.7.1
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 200 OK
< Content-type: application/json
< Transfer-encoding: chunked
<
* Connection #0 to host 127.0.0.1 left intact
* Found bundle for host: 0x600000d186c0 [serially]
* Can not multiplex, even if we wanted to
* Re-using existing connection with host 127.0.0.1
> GET /baz HTTP/1.1
> Host: 127.0.0.1:9123
> User-Agent: curl/8.7.1
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 200 OK
< Content-type: application/json
< Transfer-encoding: chunked
<
* Connection #0 to host 127.0.0.1 left intact
00000000  47 45 54 20 2f 66 6f 6f  20 48 54 54 50 2f 31 2e   GET /foo  HTTP/1.
00000010  31 0d 0a 48 6f 73 74 3a  20 31 32 37 2e 30 2e 30   1..Host:  127.0.0
00000020  2e 31 3a 39 31 32 33 0d  0a 55 73 65 72 2d 41 67   .1:9123. .User-Ag
00000030  65 6e 74 3a 20 63 75 72  6c 2f 38 2e 37 2e 31 0d   ent: cur l/8.7.1.
00000040  0a 41 63 63 65 70 74 3a  20 2a 2f 2a 0d 0a 0d 0a   .Accept:  */*....
    00000000  48 54 54 50 2f 31 2e 31  20 32 30 30 20 4f 4b 0d   HTTP/1.1  200 OK.
    00000010  0a                                                 .
    00000011  43 6f 6e 74 65 6e 74 2d  74 79 70 65 3a 20 61 70   Content- type: ap
    00000021  70 6c 69 63 61 74 69 6f  6e 2f 6a 73 6f 6e 0d 0a   plicatio n/json..
    00000031  54 72 61 6e 73 66 65 72  2d 65 6e 63 6f 64 69 6e   Transfer -encodin
    00000041  67 3a 20 63 68 75 6e 6b  65 64 0d 0a               g: chunk ed..
    0000004D  0d 0a                                              ..
    0000004F  30 0d 0a                                           0..
    00000052  0d 0a                                              ..
00000050  47 45 54 20 2f 62 61 72  20 48 54 54 50 2f 31 2e   GET /bar  HTTP/1.
00000060  31 0d 0a 48 6f 73 74 3a  20 31 32 37 2e 30 2e 30   1..Host:  127.0.0
00000070  2e 31 3a 39 31 32 33 0d  0a 55 73 65 72 2d 41 67   .1:9123. .User-Ag
00000080  65 6e 74 3a 20 63 75 72  6c 2f 38 2e 37 2e 31 0d   ent: cur l/8.7.1.
00000090  0a 41 63 63 65 70 74 3a  20 2a 2f 2a 0d 0a 0d 0a   .Accept:  */*....
    00000054  48 54 54 50 2f 31 2e 31  20 32 30 30 20 4f 4b 0d   HTTP/1.1  200 OK.
    00000064  0a                                                 .
    00000065  43 6f 6e 74 65 6e 74 2d  74 79 70 65 3a 20 61 70   Content- type: ap
    00000075  70 6c 69 63 61 74 69 6f  6e 2f 6a 73 6f 6e 0d 0a   plicatio n/json..
    00000085  54 72 61 6e 73 66 65 72  2d 65 6e 63 6f 64 69 6e   Transfer -encodin
    00000095  67 3a 20 63 68 75 6e 6b  65 64 0d 0a               g: chunk ed..
    000000A1  0d 0a                                              ..
    000000A3  30 0d 0a                                           0..
    000000A6  0d 0a                                              ..
000000A0  47 45 54 20 2f 62 61 7a  20 48 54 54 50 2f 31 2e   GET /baz  HTTP/1.
000000B0  31 0d 0a 48 6f 73 74 3a  20 31 32 37 2e 30 2e 30   1..Host:  127.0.0
000000C0  2e 31 3a 39 31 32 33 0d  0a 55 73 65 72 2d 41 67   .1:9123. .User-Ag
000000D0  65 6e 74 3a 20 63 75 72  6c 2f 38 2e 37 2e 31 0d   ent: cur l/8.7.1.
000000E0  0a 41 63 63 65 70 74 3a  20 2a 2f 2a 0d 0a 0d 0a   .Accept:  */*....
    000000A8  48 54 54 50 2f 31 2e 31  20 32 30 30 20 4f 4b 0d   HTTP/1.1  200 OK.
    000000B8  0a                                                 .
    000000B9  43 6f 6e 74 65 6e 74 2d  74 79 70 65 3a 20 61 70   Content- type: ap
    000000C9  70 6c 69 63 61 74 69 6f  6e 2f 6a 73 6f 6e 0d 0a   plicatio n/json..
    000000D9  54 72 61 6e 73 66 65 72  2d 65 6e 63 6f 64 69 6e   Transfer -encodin
    000000E9  67 3a 20 63 68 75 6e 6b  65 64 0d 0a               g: chunk ed..
    000000F5  0d 0a                                              ..
    000000F7  30 0d 0a                                           0..
    000000FA  0d 0a                                              ..

I just noticed you mentioned HAProxy in your OP. HAProxy also behaves as expected here. Indeed, if the backend server sends any content in the response body then HAProxy strips it out and reports 502 Bad Gateway.

The raw bytes on the backend:

00000000  48 45 41 44 20 2f 66 6f  6f 20 48 54 54 50 2f 31   HEAD /fo o HTTP/1
00000010  2e 31 0d 0a 68 6f 73 74  3a 20 31 32 37 2e 30 2e   .1..host : 127.0.
00000020  30 2e 31 3a 39 31 32 34  0d 0a 75 73 65 72 2d 61   0.1:9124 ..user-a
00000030  67 65 6e 74 3a 20 63 75  72 6c 2f 38 2e 37 2e 31   gent: cu rl/8.7.1
00000040  0d 0a 61 63 63 65 70 74  3a 20 2a 2f 2a 0d 0a 0d   ..accept : */*...
00000050  0a                                                 .
    00000000  48 54 54 50 2f 31 2e 31  20 32 30 30 20 4f 4b 0d   HTTP/1.1  200 OK.
    00000010  0a 43 6f 6e 74 65 6e 74  2d 74 79 70 65 3a 20 61   .Content -type: a
    00000020  70 70 6c 69 63 61 74 69  6f 6e 2f 6a 73 6f 6e 0d   pplicati on/json.
    00000030  0a 54 72 61 6e 73 66 65  72 2d 65 6e 63 6f 64 69   .Transfe r-encodi
    00000040  6e 67 3a 20 63 68 75 6e  6b 65 64 0d 0a 0d 0a      ng: chun ked....
00000051  48 45 41 44 20 2f 62 61  72 20 48 54 54 50 2f 31   HEAD /ba r HTTP/1
00000061  2e 31 0d 0a 68 6f 73 74  3a 20 31 32 37 2e 30 2e   .1..host : 127.0.
00000071  30 2e 31 3a 39 31 32 34  0d 0a 75 73 65 72 2d 61   0.1:9124 ..user-a
00000081  67 65 6e 74 3a 20 63 75  72 6c 2f 38 2e 37 2e 31   gent: cu rl/8.7.1
00000091  0d 0a 61 63 63 65 70 74  3a 20 2a 2f 2a 0d 0a 0d   ..accept : */*...
000000A1  0a                                                 .
    0000004F  30 0d 0a 0d 0a                                     0....
    00000054  48 54 54 50 2f 31 2e 31  20 32 30 30 20 4f 4b 0d   HTTP/1.1  200 OK.
    00000064  0a 43 6f 6e 74 65 6e 74  2d 74 79 70 65 3a 20 61   .Content -type: a
    00000074  70 70 6c 69 63 61 74 69  6f 6e 2f 6a 73 6f 6e 0d   pplicati on/json.
    00000084  0a 54 72 61 6e 73 66 65  72 2d 65 6e 63 6f 64 69   .Transfe r-encodi
    00000094  6e 67 3a 20 63 68 75 6e  6b 65 64 0d 0a 0d 0a      ng: chun ked....
[RST, new connection]
00000000  48 45 41 44 20 2f 62 61  7a 20 48 54 54 50 2f 31   HEAD /ba z HTTP/1
00000010  2e 31 0d 0a 68 6f 73 74  3a 20 31 32 37 2e 30 2e   .1..host : 127.0.
00000020  30 2e 31 3a 39 31 32 34  0d 0a 75 73 65 72 2d 61   0.1:9124 ..user-a
00000030  67 65 6e 74 3a 20 63 75  72 6c 2f 38 2e 37 2e 31   gent: cu rl/8.7.1
00000040  0d 0a 61 63 63 65 70 74  3a 20 2a 2f 2a 0d 0a 0d   ..accept : */*...
00000050  0a                                                 .
    00000000  48 54 54 50 2f 31 2e 31  20 32 30 30 20 4f 4b 0d   HTTP/1.1  200 OK.
    00000010  0a 43 6f 6e 74 65 6e 74  2d 74 79 70 65 3a 20 61   .Content -type: a
    00000020  70 70 6c 69 63 61 74 69  6f 6e 2f 6a 73 6f 6e 0d   pplicati on/json.
    00000030  0a 54 72 61 6e 73 66 65  72 2d 65 6e 63 6f 64 69   .Transfe r-encodi
    00000040  6e 67 3a 20 63 68 75 6e  6b 65 64 0d 0a 0d 0a      ng: chun ked....
    0000004F  30 0d 0a 0d 0a                                     0....

On the frontend:

00000000  48 45 41 44 20 2f 66 6f  6f 20 48 54 54 50 2f 31   HEAD /fo o HTTP/1
00000010  2e 31 0d 0a 48 6f 73 74  3a 20 31 32 37 2e 30 2e   .1..Host : 127.0.
00000020  30 2e 31 3a 39 31 32 34  0d 0a 55 73 65 72 2d 41   0.1:9124 ..User-A
00000030  67 65 6e 74 3a 20 63 75  72 6c 2f 38 2e 37 2e 31   gent: cu rl/8.7.1
00000040  0d 0a 41 63 63 65 70 74  3a 20 2a 2f 2a 0d 0a 0d   ..Accept : */*...
00000050  0a                                                 .
    00000000  48 54 54 50 2f 31 2e 31  20 32 30 30 20 4f 4b 0d   HTTP/1.1  200 OK.
    00000010  0a 63 6f 6e 74 65 6e 74  2d 74 79 70 65 3a 20 61   .content -type: a
    00000020  70 70 6c 69 63 61 74 69  6f 6e 2f 6a 73 6f 6e 0d   pplicati on/json.
    00000030  0a 74 72 61 6e 73 66 65  72 2d 65 6e 63 6f 64 69   .transfe r-encodi
    00000040  6e 67 3a 20 63 68 75 6e  6b 65 64 0d 0a 0d 0a      ng: chun ked....
00000051  48 45 41 44 20 2f 62 61  72 20 48 54 54 50 2f 31   HEAD /ba r HTTP/1
00000061  2e 31 0d 0a 48 6f 73 74  3a 20 31 32 37 2e 30 2e   .1..Host : 127.0.
00000071  30 2e 31 3a 39 31 32 34  0d 0a 55 73 65 72 2d 41   0.1:9124 ..User-A
00000081  67 65 6e 74 3a 20 63 75  72 6c 2f 38 2e 37 2e 31   gent: cu rl/8.7.1
00000091  0d 0a 41 63 63 65 70 74  3a 20 2a 2f 2a 0d 0a 0d   ..Accept : */*...
000000A1  0a                                                 .
    0000004F  48 54 54 50 2f 31 2e 31  20 35 30 32 20 42 61 64   HTTP/1.1  502 Bad
    0000005F  20 47 61 74 65 77 61 79  0d 0a 63 6f 6e 74 65 6e    Gateway ..conten
    0000006F  74 2d 6c 65 6e 67 74 68  3a 20 31 30 37 0d 0a 63   t-length : 107..c
    0000007F  61 63 68 65 2d 63 6f 6e  74 72 6f 6c 3a 20 6e 6f   ache-con trol: no
    0000008F  2d 63 61 63 68 65 0d 0a  63 6f 6e 74 65 6e 74 2d   -cache.. content-
    0000009F  74 79 70 65 3a 20 74 65  78 74 2f 68 74 6d 6c 0d   type: te xt/html.
    000000AF  0a 0d 0a                                           ...
000000A2  48 45 41 44 20 2f 62 61  7a 20 48 54 54 50 2f 31   HEAD /ba z HTTP/1
000000B2  2e 31 0d 0a 48 6f 73 74  3a 20 31 32 37 2e 30 2e   .1..Host : 127.0.
000000C2  30 2e 31 3a 39 31 32 34  0d 0a 55 73 65 72 2d 41   0.1:9124 ..User-A
000000D2  67 65 6e 74 3a 20 63 75  72 6c 2f 38 2e 37 2e 31   gent: cu rl/8.7.1
000000E2  0d 0a 41 63 63 65 70 74  3a 20 2a 2f 2a 0d 0a 0d   ..Accept : */*...
000000F2  0a                                                 .
    000000B2  48 54 54 50 2f 31 2e 31  20 32 30 30 20 4f 4b 0d   HTTP/1.1  200 OK.
    000000C2  0a 63 6f 6e 74 65 6e 74  2d 74 79 70 65 3a 20 61   .content -type: a
    000000D2  70 70 6c 69 63 61 74 69  6f 6e 2f 6a 73 6f 6e 0d   pplicati on/json.
    000000E2  0a 74 72 61 6e 73 66 65  72 2d 65 6e 63 6f 64 69   .transfe r-encodi
    000000F2  6e 67 3a 20 63 68 75 6e  6b 65 64 0d 0a 0d 0a      ng: chun ked....

Note the (correct) lack of 0\r\n\r\n in the response body sent by the frontend, and the 502 Bad Gateway because of the incorrect behaviour on the backend.

we have fixed bugreport on haproxy - haproxy partially forwards transfer-encoding from backend in response to a HEAD request · Issue #2836 · haproxy/haproxy · GitHub

1 Like

Ah right yeah that'd explain it. I ran my tests with HAProxy 3.1.3, this bug is fixed there. 3.1.1 is buggy in this way indeed.