 doc/configuration.txt       | 18 ++++++++++++++
 include/proto/proto_http.h  |  2 ++
 include/types/server.h      |  2 ++
 src/backend.c               |  5 ++++
 src/checks.c                |  7 ++++++
 src/proto_http.c            | 59 +++++++++++++++++++++++++++++++++++++++++++++
 src/proto_htx.c             | 30 +++++++++++++++++++++++
 src/server.c                |  6 +++++
 src/stream.c                |  9 +++++++
 tests/test-server-vhost.cfg | 19 +++++++++++++++
 10 files changed, 157 insertions(+)

diff --git a/doc/configuration.txt b/doc/configuration.txt
index a46384bf..fcefdd5c 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -12531,6 +12531,24 @@ verifyhost <hostname>
   handshake is aborted. The hostnames in the server-provided certificate may
   include wildcards. See also "verify", "sni" and "no-verifyhost" options.
 
+vhost <hostname>
+  The "vhost" parameter is used to specify the hostname for a backend. 
+  It will be set in the Host header in requests forwarded to the backend, 
+  in HTTP healthchecks and when establishing TLS connections (TLS SNI).
+  It is required when using services that rely on the Host header for
+  routing connections (kubernetes ingress, ALB, other haproxy, etc...).
+
+  Examples :
+  backend vhost-example
+    mode http
+
+    option httpchk GET /healthcheck
+    http-check expect status 200
+    server srv-host 10.10.0.1:8080 vhost myapp.naeast-1a.example.com check
+    server srv-host 10.20.0.1:8080 vhost myapp.nawest-2b.example.com check
+
+  See also : "http-send-name-header", "httpchk"
+
 weight <weight>
   The "weight" parameter is used to adjust the server's weight relative to
   other servers. All servers will receive a load proportional to their weight
diff --git a/include/proto/proto_http.h b/include/proto/proto_http.h
index 102c5ded..e72d158a 100644
--- a/include/proto/proto_http.h
+++ b/include/proto/proto_http.h
@@ -40,6 +40,7 @@ int http_process_request(struct stream *s, struct channel *req, int an_bit);
 int http_process_tarpit(struct stream *s, struct channel *req, int an_bit);
 int http_wait_for_request_body(struct stream *s, struct channel *req, int an_bit);
 int http_send_name_header(struct stream *s, struct proxy* be, const char* svr_name);
+int http_send_vhost_header(struct stream *s, struct proxy* be, const char* vhost);
 int http_wait_for_response(struct stream *s, struct channel *rep, int an_bit);
 int http_process_res_common(struct stream *s, struct channel *rep, int an_bit, struct proxy *px);
 int http_request_forward_body(struct stream *s, struct channel *req, int an_bit);
@@ -86,6 +87,7 @@ void htx_res_set_status(unsigned int status, const char *reason, struct stream *
 void htx_check_request_for_cacheability(struct stream *s, struct channel *req);
 void htx_check_response_for_cacheability(struct stream *s, struct channel *res);
 int htx_send_name_header(struct stream *s, struct proxy *be, const char *srv_name);
+int htx_send_vhost_header(struct stream *s, struct proxy *be, const char *vhost);
 void htx_perform_server_redirect(struct stream *s, struct stream_interface *si);
 void htx_server_error(struct stream *s, struct stream_interface *si, int err, int finst, const struct buffer *msg);
 void htx_reply_and_close(struct stream *s, short status, struct buffer *msg);
diff --git a/include/types/server.h b/include/types/server.h
index 56852897..41e0cf03 100644
--- a/include/types/server.h
+++ b/include/types/server.h
@@ -271,6 +271,8 @@ struct server {
 	int puid;				/* proxy-unique server ID, used for SNMP, and "first" LB algo */
 	int tcp_ut;                             /* for TCP, user timeout */
 
+	char *vhost;                            /* the host header to send per backend */
+
 	int do_check;                           /* temporary variable used during parsing to denote if health checks must be enabled */
 	int do_agent;                           /* temporary variable used during parsing to denote if an auxiliary agent check must be enabled */
 	struct check check;                     /* health-check specific configuration */
diff --git a/src/backend.c b/src/backend.c
index 05fefcdf..8d71528b 100644
--- a/src/backend.c
+++ b/src/backend.c
@@ -1672,6 +1672,11 @@ int connect_server(struct stream *s)
 				srv_conn->flags |= CO_FL_PRIVATE;
 			}
 		}
+
+		if (srv->vhost) {
+			ssl_sock_set_servername(srv_conn, srv->vhost);
+			srv_conn->flags |= CO_FL_PRIVATE;
+		}
 #endif /* USE_OPENSSL */
 
 	}
diff --git a/src/checks.c b/src/checks.c
index a1fe015d..15673fcd 100644
--- a/src/checks.c
+++ b/src/checks.c
@@ -1586,6 +1586,11 @@ static int connect_conn_chk(struct task *t)
 			if (s->proxy->options2 & PR_O2_CHK_SNDST)
 				b_putblk(&check->bo, trash.area,
 					 httpchk_build_status_header(s, trash.area, trash.size));
+			/* set the host header per backend */
+			if (s->vhost)
+				b_putist(&check->bo, ist("Host: "));
+				b_putist(&check->bo, ist(s->vhost));
+				b_putist(&check->bo, ist("\r\n"));
 			/* prevent HTTP keep-alive when "http-check expect" is used */
 			if (s->proxy->options2 & PR_O2_EXP_TYPE)
 				b_putist(&check->bo, ist("Connection: close\r\n"));
@@ -1663,6 +1668,8 @@ static int connect_conn_chk(struct task *t)
 
 #ifdef USE_OPENSSL
 	if (ret == SF_ERR_NONE) {
+		if (s->vhost)
+			ssl_sock_set_servername(conn, s->vhost);
 		if (s->check.sni)
 			ssl_sock_set_servername(conn, s->check.sni);
 		if (s->check.alpn_str)
diff --git a/src/proto_http.c b/src/proto_http.c
index 35f86ef1..2dfbff14 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -3378,6 +3378,65 @@ int http_send_name_header(struct stream *s, struct proxy* be, const char* srv_na
 	return 0;
 }
 
+/* send a server's vhost with an outgoing request over an established connection.
+ * Note: this function is designed to be called once the request has been scheduled
+ * for being forwarded. This is the reason why it rewinds the buffer before
+ * proceeding.
+ */
+int http_send_vhost_header(struct stream *s, struct proxy* be, const char* vhost) {
+
+	struct hdr_ctx ctx;
+	struct http_txn *txn = s->txn;
+	char *hdr_name = "Host";
+	int hdr_name_len = strlen(hdr_name);
+	struct channel *chn = txn->req.chn;
+	char *hdr_val;
+	unsigned int old_o, old_i;
+
+	if (IS_HTX_STRM(s))
+		return htx_send_vhost_header(s, be, vhost);
+	ctx.idx = 0;
+
+	old_o = http_hdr_rewind(&txn->req);
+	if (old_o) {
+		/* The request was already skipped, let's restore it */
+		c_rew(chn, old_o);
+		txn->req.next += old_o;
+		txn->req.sov += old_o;
+	}
+
+	old_i = ci_data(chn);
+	while (http_find_header2(hdr_name, hdr_name_len, ci_head(txn->req.chn), &txn->hdr_idx, &ctx)) {
+		/* remove any existing values from the header */
+	        http_remove_header2(&txn->req, &txn->hdr_idx, &ctx);
+	}
+
+	/* Add the new header requested with the server value */
+	hdr_val = trash.area;
+	memcpy(hdr_val, hdr_name, hdr_name_len);
+	hdr_val += hdr_name_len;
+	*hdr_val++ = ':';
+	*hdr_val++ = ' ';
+	hdr_val += strlcpy2(hdr_val, vhost,
+			    trash.area + trash.size - hdr_val);
+	http_header_add_tail2(&txn->req, &txn->hdr_idx, trash.area,
+			      hdr_val - trash.area);
+
+	if (old_o) {
+		/* If this was a forwarded request, we must readjust the amount of
+		 * data to be forwarded in order to take into account the size
+		 * variations. Note that the current state is >= HTTP_MSG_BODY,
+		 * so we don't have to adjust ->sol.
+		 */
+		old_o += ci_data(chn) - old_i;
+		c_adv(chn, old_o);
+		txn->req.next -= old_o;
+		txn->req.sov  -= old_o;
+	}
+
+	return 0;
+}
+
 /* Terminate current transaction and prepare a new one. This is very tricky
  * right now but it works.
  */
diff --git a/src/proto_htx.c b/src/proto_htx.c
index d5119c10..9276abb3 100644
--- a/src/proto_htx.c
+++ b/src/proto_htx.c
@@ -4806,6 +4806,36 @@ int htx_send_name_header(struct stream *s, struct proxy *be, const char *srv_nam
 	return 0;
 }
 
+/* send a server's vhost with an outgoing request over an established connection.
+ * Note: this function is designed to be called once the request has been
+ * scheduled for being forwarded. This is the reason why the number of forwarded
+ * bytes have to be adjusted.
+ */
+int htx_send_vhost_header(struct stream *s, struct proxy *be, const char *vhost)
+{
+	struct htx *htx;
+	struct http_hdr_ctx ctx;
+	struct ist hdr;
+	uint32_t data;
+
+	hdr = ist2("Host", strlen("Host"));
+	htx = htxbuf(&s->req.buf);
+	data = htx->data;
+
+	ctx.blk = NULL;
+	while (http_find_header(htx, hdr, &ctx, 1))
+		http_remove_header(htx, &ctx);
+	http_add_header(htx, hdr, ist2(vhost, strlen(vhost)));
+
+	if (co_data(&s->req)) {
+		if (data >= htx->data)
+			c_rew(&s->req, data - htx->data);
+		else
+			c_adv(&s->req, htx->data - data);
+	}
+	return 0;
+}
+
 /*
  * In a GET, HEAD or POST request, check if the requested URI matches the stats uri
  * for the current backend.
diff --git a/src/server.c b/src/server.c
index a815f400..09f54791 100644
--- a/src/server.c
+++ b/src/server.c
@@ -1735,6 +1735,7 @@ static void srv_settings_cpy(struct server *srv, struct server *src, int srv_tmp
 		srv->cklen  = src->cklen;
 	}
 	srv->use_ssl                  = src->use_ssl;
+	srv->vhost                    = src->vhost;
 	srv->check.addr = srv->agent.addr = src->check.addr;
 	srv->check.use_ssl            = src->check.use_ssl;
 	srv->check.port               = src->check.port;
@@ -2529,6 +2530,11 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr
 
 				cur_arg += 2;
 			}
+			else if (!strcmp(args[cur_arg], "vhost")) {
+				free(newsrv->vhost);
+				newsrv->vhost = strdup(args[cur_arg + 1]);
+				cur_arg += 2;
+			}
 			else if (!strcmp(args[cur_arg], "rise")) {
 				if (!*args[cur_arg + 1]) {
 					ha_alert("parsing [%s:%d]: '%s' expects an integer argument.\n",
diff --git a/src/stream.c b/src/stream.c
index 69befbdd..52f2b153 100644
--- a/src/stream.c
+++ b/src/stream.c
@@ -2449,6 +2449,15 @@ struct task *process_stream(struct task *t, void *context, unsigned short state)
 				http_send_name_header(s, s->be, objt_server(s->target)->id);
 			}
 
+			/* Now we can add the server vhost to a header (if requested) */
+			/* check for HTTP mode and proxy server_name_hdr_name != NULL */
+			if (si_state_in(si_b->state, SI_SB_CON|SI_SB_RDY|SI_SB_EST) &&
+			    (s->be->mode == PR_MODE_HTTP) &&
+			    objt_server(s->target) &&
+			    (objt_server(s->target)->vhost != NULL)) {
+				http_send_vhost_header(s, s->be, objt_server(s->target)->vhost);
+			}
+
 			srv = objt_server(s->target);
 			if (si_b->state == SI_ST_ASS && srv && srv->rdr_len && (s->flags & SF_REDIRECTABLE))
 				http_perform_server_redirect(s, si_b);
diff --git a/tests/test-server-vhost.cfg b/tests/test-server-vhost.cfg
new file mode 100644
index 00000000..a8853668
--- /dev/null
+++ b/tests/test-server-vhost.cfg
@@ -0,0 +1,19 @@
+# Test Virtual Host
+global
+       maxconn 100
+
+defaults
+	mode http
+	timeout client 10000
+	timeout server 10000
+	timeout connect 10000
+	balance roundrobin
+
+listen send-vhost
+	bind :8000
+
+	server srv-host 127.0.0.1:8080 vhost api.example.com
+
+	# Add headers containing the correct values for test verification
+	reqadd X-test-server-name-header:\ Host
+	reqadd X-test-server-name-value:\ api.example.com
