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

**URL:** <https://discuss.elastic.co/t/is-it-correct-to-response-to-head-req-from-elastic-api-with-empty-transfer-encoding-chunked/372941>\
**Category:** Elasticsearch\
**Created:** [January 8, 2025, 3:06pm UTC](https://discuss.elastic.co/t/is-it-correct-to-response-to-head-req-from-elastic-api-with-empty-transfer-encoding-chunked/372941 "2025-01-08T15:06:11Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![pencakj](https://avatars.discourse-cdn.com/v4/letter/p/eb9ed0/32.png) [@pencakj](https://discuss.elastic.co/u/pencakj)\
**Post date:** [January 8, 2025, 3:06pm UTC](https://discuss.elastic.co/t/is-it-correct-to-response-to-head-req-from-elastic-api-with-empty-transfer-encoding-chunked/372941/1 "2025-01-08T15:06:11Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![DavidTurner](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/davidturner/32/22453_2.png) [@DavidTurner](https://discuss.elastic.co/u/DavidTurner)\
**Post date:** [January 12, 2025, 9:17am UTC](https://discuss.elastic.co/t/is-it-correct-to-response-to-head-req-from-elastic-api-with-empty-transfer-encoding-chunked/372941/2 "2025-01-12T09:17:30Z")

</div>

Yes, this is correct behaviour. See e.g. [RFC 9110 §9.3.2](https://httpwg.org/specs/rfc9110.html#HEAD):

> 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.

---

<div class="post-metadata">

**Author:** ![pencakj](https://avatars.discourse-cdn.com/v4/letter/p/eb9ed0/32.png) [@pencakj](https://discuss.elastic.co/u/pencakj)\
**Post date:** [January 13, 2025, 8:14am UTC](https://discuss.elastic.co/t/is-it-correct-to-response-to-head-req-from-elastic-api-with-empty-transfer-encoding-chunked/372941/3 "2025-01-13T08:14:57Z")

</div>

That's fine but there with NO DATA - RFC 9112 [RFC 9112 - HTTP/1.1](https://datatracker.ietf.org/doc/html/rfc9112#section-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.

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

---

<div class="post-metadata">

**Author:** ![DavidTurner](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/davidturner/32/22453_2.png) [@DavidTurner](https://discuss.elastic.co/u/DavidTurner)\
**Post date:** [January 13, 2025, 9:20am UTC](https://discuss.elastic.co/t/is-it-correct-to-response-to-head-req-from-elastic-api-with-empty-transfer-encoding-chunked/372941/4 "2025-01-13T09:20:05Z")

</div>

No, that's not the case. HEAD requests are special in this regard. See e.g. [RFC 9112 §6.3](https://www.rfc-editor.org/rfc/rfc9112#message.body.length):

> 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** , [...]

---

<div class="post-metadata">

**Author:** ![pencakj](https://avatars.discourse-cdn.com/v4/letter/p/eb9ed0/32.png) [@pencakj](https://discuss.elastic.co/u/pencakj)\
**Post date:** [January 13, 2025, 9:39am UTC](https://discuss.elastic.co/t/is-it-correct-to-response-to-head-req-from-elastic-api-with-empty-transfer-encoding-chunked/372941/5 "2025-01-13T09:39:34Z")

</div>

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

[github issue](https://github.com/elastic/elasticsearch/issues/92032)

---

<div class="post-metadata">

**Author:** ![DavidTurner](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/davidturner/32/22453_2.png) [@DavidTurner](https://discuss.elastic.co/u/DavidTurner)\
**Post date:** [January 13, 2025, 9:43am UTC](https://discuss.elastic.co/t/is-it-correct-to-response-to-head-req-from-elastic-api-with-empty-transfer-encoding-chunked/372941/6 "2025-01-13T09:43:13Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![TimV](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/timv/32/13162_2.png) [@TimV](https://discuss.elastic.co/u/TimV)\
**Post date:** [January 14, 2025, 1:01am UTC](https://discuss.elastic.co/t/is-it-correct-to-response-to-head-req-from-elastic-api-with-empty-transfer-encoding-chunked/372941/7 "2025-01-14T01:01:47Z")

</div>

Also from [§6.1 of RFC9112](https://datatracker.ietf.org/doc/html/rfc9112#section-6.1)

> Transfer-Encoding MAY be sent in a response to a HEAD request or in a 304 (Not Modified) response ([Section 15.4.5](https://www.rfc-editor.org/rfc/rfc9110#section-15.4.5) of [[HTTP](https://datatracker.ietf.org/doc/html/rfc9110)]) 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).

---

<div class="post-metadata">

**Author:** ![DavidTurner](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/davidturner/32/22453_2.png) [@DavidTurner](https://discuss.elastic.co/u/DavidTurner)\
**Post date:** [January 14, 2025, 7:57am UTC](https://discuss.elastic.co/t/is-it-correct-to-response-to-head-req-from-elastic-api-with-empty-transfer-encoding-chunked/372941/8 "2025-01-14T07:57:22Z")

</div>

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

> [@TimV](#):
>
> Transfer-Encoding MAY be sent in a response to a HEAD request

... whereas [RFC 9110 §9.3.2](https://httpwg.org/specs/rfc9110.html#HEAD) says its a `SHOULD`.

> [@DavidTurner](#):
>
> 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.

---

<div class="post-metadata">

**Author:** ![pencakj](https://avatars.discourse-cdn.com/v4/letter/p/eb9ed0/32.png) [@pencakj](https://discuss.elastic.co/u/pencakj)\
**Post date:** [January 29, 2025, 1:28pm UTC](https://discuss.elastic.co/t/is-it-correct-to-response-to-head-req-from-elastic-api-with-empty-transfer-encoding-chunked/372941/9 "2025-01-29T13:28:28Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![DavidTurner](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/davidturner/32/22453_2.png) [@DavidTurner](https://discuss.elastic.co/u/DavidTurner)\
**Post date:** [January 29, 2025, 1:42pm UTC](https://discuss.elastic.co/t/is-it-correct-to-response-to-head-req-from-elastic-api-with-empty-transfer-encoding-chunked/372941/10 "2025-01-29T13:42:30Z")

</div>

> [@pencakj](#):
>
> 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](https://httpwg.org/specs/rfc9112.html#rfc.section.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](https://discuss.elastic.co/t/is-it-correct-to-response-to-head-req-from-elastic-api-with-empty-transfer-encoding-chunked/372941/4) 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.

---

<div class="post-metadata">

**Author:** ![DavidTurner](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/davidturner/32/22453_2.png) [@DavidTurner](https://discuss.elastic.co/u/DavidTurner)\
**Post date:** [January 29, 2025, 3:03pm UTC](https://discuss.elastic.co/t/is-it-correct-to-response-to-head-req-from-elastic-api-with-empty-transfer-encoding-chunked/372941/11 "2025-01-29T15:03:24Z")

</div>

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:

```auto
$ 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:

```auto
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:

```auto
$ 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:

```auto
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](https://daniel.haxx.se/blog/2019/04/06/curl-says-bye-bye-to-pipelining/). It's doing this because it got to the end of the response.

---

<div class="post-metadata">

**Author:** ![DavidTurner](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/davidturner/32/22453_2.png) [@DavidTurner](https://discuss.elastic.co/u/DavidTurner)\
**Post date:** [January 29, 2025, 3:14pm UTC](https://discuss.elastic.co/t/is-it-correct-to-response-to-head-req-from-elastic-api-with-empty-transfer-encoding-chunked/372941/12 "2025-01-29T15:14:39Z")

</div>

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

```auto
$ 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

```

```auto
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 ..

```

---

<div class="post-metadata">

**Author:** ![DavidTurner](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/davidturner/32/22453_2.png) [@DavidTurner](https://discuss.elastic.co/u/DavidTurner)\
**Post date:** [January 29, 2025, 7:19pm UTC](https://discuss.elastic.co/t/is-it-correct-to-response-to-head-req-from-elastic-api-with-empty-transfer-encoding-chunked/372941/13 "2025-01-29T19:19:51Z")

</div>

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:

```auto
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:

```auto
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.

---

<div class="post-metadata">

**Author:** ![pencakj](https://avatars.discourse-cdn.com/v4/letter/p/eb9ed0/32.png) [@pencakj](https://discuss.elastic.co/u/pencakj)\
**Post date:** [January 29, 2025, 7:32pm UTC](https://discuss.elastic.co/t/is-it-correct-to-response-to-head-req-from-elastic-api-with-empty-transfer-encoding-chunked/372941/14 "2025-01-29T19:32:52Z")

</div>

we have fixed bugreport on haproxy - [haproxy partially forwards transfer-encoding from backend in response to a HEAD request · Issue #2836 · haproxy/haproxy · GitHub](https://github.com/haproxy/haproxy/issues/2836)

---

<div class="post-metadata">

**Author:** ![DavidTurner](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/davidturner/32/22453_2.png) [@DavidTurner](https://discuss.elastic.co/u/DavidTurner)\
**Post date:** [January 30, 2025, 8:07am UTC](https://discuss.elastic.co/t/is-it-correct-to-response-to-head-req-from-elastic-api-with-empty-transfer-encoding-chunked/372941/15 "2025-01-30T08:07:24Z")

</div>

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.
