Haproxy removes the "Reason-Phrase" from the HTTP response status code

Haproxy removes the “Reason-Phrase” from the HTTP response status code.

I created a very basic php script to test this, the output is simply status code 400 with a reason phrase.
<?php
header(“HTTP/1.1 400 (The image exceeds the maximum width of 500px. Please adjust the image.)”);

When the request goes directly to the apache webserver I can see this using developer console in Chrome (Network -> Headers) and I get:

Status Code: 400 (The image exceeds the maximum width of 500px. Please adjust the image.)

When the Apache web server is proxied through Haproxy i get only the status code
Status Code: 400

So either there is a setting missing in my config to allow this or simply does not work. I have tried it on Haproxy 1.8, 1.9, 2.0 and 2.1 and I get the same problem.

Can anyone help?

I cannot reproduce, in both 1.8.23 and 2.2-dev8-786752-32 I see the response untouched:

*   Trying 10.0.0.33...
* TCP_NODELAY set
* Connected to dev.lan.ltri.eu (10.0.0.33) port 80 (#0)
> GET /http-long-reason.php HTTP/1.1
> Host: dev.lan.ltri.eu
> User-Agent: curl/7.55.1
> Accept: */*
>
< HTTP/1.1 400 (The image exceeds the maximum width of 500px. Please adjust the image.)
< Server: nginx/1.14.0 (Ubuntu)
< Date: Tue, 23 Jun 2020 16:46:03 GMT
< Content-Type: text/html; charset=UTF-8
< Transfer-Encoding: chunked
< X-Hey: This-is-haproxy-speaking
<
 * Connection #0 to host dev.lan.ltri.eu left intact

Can you provide the entire haproxy configuration and the entire output of curl -v against Apache directly and vs haproxy?

Thanks for the response!

Ok, so you are right curl -v works fine, I can see the phrase but I cannot see it in Chrome, Firefox or Edge where we want to display this message to the end user.

Maybe this is a bug in those browsers, but it affects all of them! And only when my web server is proxied through Haproxy?

As you requested my HAproxy Config (I stripped it down to basics to try and solve this but this config creates the same problem in all those browsers)
Running V2.0.15

global
	daemon
	
defaults
	mode http


frontend general_frontend_web
	mode http
	bind *:80 
	use_backend backend_web
		
backend backend_web
	balance roundrobin
	server meetingorganizer1 meetingorganizer1:80 #disabled

Browser are the first thing I checked, it was displayed just fine.

A end user will never check the developer console in the browser for a specific HTTP response header reason.

Also, using long error message as HTTP response is completely unusual. HTTP response reasons are supposed to be short, like 2 or 3 words without any special characters.

Application level errors need to be in the HTTP payload instead.

On closer inspection I found it works when I drop my SSL configuration so something is wrong there. I will investigate

Also you are right about long error messages, I spoke to the developers about that and they want a third party service to upload these images to our system and then it dynamically generates a page for the end user.

None the less the problem is still there. Here is my config with ssl config:

global
	daemon
	maxconn 60    # Sets the maximum per-process number of concurrent connections to <number>. 
	maxsslconn 60 # Sets the maximum per-process number of concurrent SSL connections to <number>.
	maxconnrate 60 # Sets the maximum per-process number of connections per second to <number>.
	maxsslrate 60  # Sets the maximum per-process number of SSL sessions per second to <number>.
		
	# intermediate configuration, tweak to your needs
    ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
    ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets

    ssl-default-server-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
    ssl-default-server-options no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets

    ssl-dh-param-file /usr/local/etc/haproxy/cert/dhparam.pem
	
defaults
	mode http


frontend general_frontend_web
	mode http
	#option forwardfor
	bind *:80
	#  Wildcard cert
	bind *:443 ssl crt /usr/local/etc/haproxy/cert/private/cert.pem alpn h2,http/1.1 
	use_backend backend_web
    redirect scheme https code 301 if !{ ssl_fc }
		
backend backend_web
	balance roundrobin
	server meetingorganizer1 meetingorganizer1:80 #disabled

There is no reason phrase in HTTP/2. I guess you perform HTTP/2 requests from your browser.

2 Likes

Changed the bind like so:
bind *:443 ssl crt /usr/local/etc/haproxy/cert/private/cert.pem alpn http/1.1

This now works, have to see if we really want to default to http1.1.

Thanks, I had no idea this was not in HTTP2.0