Repository: jcmoraisjr/haproxy-ingress

Issue:
Path scoped binding to HTTP/s port
There are cases when multiple HTTP ports are needed. 
Currently this is not possible (except with a custom template and hardcoded secondary ports).

Some ideas of how this could work (borrowed from docker proxy flow):
1. Configure haproxy with bind ports via ConfigMap
2. Configure each ingress with source port via annotations.

But even a simple config http(s)-ports-extra which would duplicate the main port, would be good for a start.

Hints from issue comments:
@jhohertz: This would be very useful for cases of very high densities of connections, and as far as I can tell, no other ingress controller so far supports doing so.@jcmoraisjr: Hi, multi port is already supported, doc [here](http://haproxy-ingress.github.io/docs/configuration/keys/#bind). What do you mean is to bind some ingress to some listening ports - eg listening 8443 and 9443, but only 8443 would find the app while 9443 would find another one or 404? Could you describe a bit more about this use case and why/how the current codebase doesn't work?@jcmoraisjr: A starting implementation was just merged, to be available in the first v0.17 version.

Solution patch:
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index e22a25b2..60065cee 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -30,7 +30,7 @@ jobs:
     strategy:
       matrix:
         haproxy-version:
-        - "2.4" # minimum supported
+        - "2.6" # minimum supported
         - "2.8" # embedded version
         - "3.2" # latest
         envtest-version:
diff --git a/docs/content/en/docs/configuration/gateway-api.md b/docs/content/en/docs/configuration/gateway-api.md
index c5db7b82..c3f0f307 100644
--- a/docs/content/en/docs/configuration/gateway-api.md
+++ b/docs/content/en/docs/configuration/gateway-api.md
@@ -6,7 +6,7 @@ description: >
   Configure HAProxy using Gateway API resources.
 ---
 
-[Gateway API](https://gateway-api.sigs.k8s.io/) is a collection of Kubernetes resources that can be installed as [Custom Resource Definitions](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/). Just like Ingress resources, Gateway API resources are used to configure incoming HTTP/s and TCP requests to the in cluster applications. HAProxy Ingress v0.15 partially supports the Gateway API spec, `v1alpha2`, `v1beta1` and `v1` versions.
+[Gateway API](https://gateway-api.sigs.k8s.io/) is a collection of Kubernetes resources that can be installed as [Custom Resource Definitions](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/). Just like Ingress resources, Gateway API resources are used to configure incoming HTTP/s, TLS and TCP requests to the in cluster applications. HAProxy Ingress v0.17 partially supports the Gateway API spec, `v1alpha2`, `v1beta1` and `v1` versions.
 
 ## Installation
 
@@ -21,13 +21,12 @@ See below the [getting started steps](#getting-started).
 
 ## Conformance
 
-Gateway API `v1alpha2`, `v1beta1` and `v1` specs are partially implemented in v0.15 release. The following list describes what is (or is not) supported:
+Gateway API `v1alpha2`, `v1beta1` and `v1` specs are partially implemented in v0.17 release. The following list describes what is (or is not) supported:
 
 * Target Services can be annotated with [Backend or Path scoped]({{% relref "keys#scope" %}}) configuration keys, this will continue to be supported.
 * Gateway API resources doesn't support annotations, this is planned to continue to be unsupported. Extensions to the Gateway API spec will be added in the extension points of the API.
-* Only the `GatewayClass`, `Gateway`, `TCPRoute` and `HTTPRoute` resource definitions are implemented.
+* `GatewayClass`, `Gateway`, `TCPRoute`, `TLSRoute` and `HTTPRoute` are the only implemented resources.
 * The controller doesn't implement partial parsing yet for Gateway API resources, changes should be a bit slow on clusters with thousands of Ingress, Gateway API resources or Services.
-* Gateway's Listener Port and Protocol are implemented for TCPRoute, but they are not implemented for HTTPRoute - for HTTP workloads, Port uses the global [bind-port]({{% relref "keys#bind-port" %}}) configuration and Protocol is based on the presence or absence of the TLS attribute.
 * Gateway's Addresses is not implemented - binding addresses use the global [bind-ip-addr]({{% relref "keys#bind-ip-addr" %}}) configuration.
 * Gateway's Hostname only supports empty/absence of Hostname or a single `*`, any other string will override the HTTPRoute Hostnames configuration without any merging.
 * HTTPRoute's Rules and BackendRefs don't support Filters.
diff --git a/docs/content/en/docs/configuration/keys.md b/docs/content/en/docs/configuration/keys.md
index 99ebe1cf..0a6cba32 100644
--- a/docs/content/en/docs/configuration/keys.md
+++ b/docs/content/en/docs/configuration/keys.md
@@ -235,290 +235,283 @@ fast with HAProxy Ingress.
 
 ## Scope
 
-HAProxy Ingress configuration keys may be in one of five distinct scopes:
-`Global`, `Host`, `Backend`, `Path`, `TCP`. A scope defines where a configuration
-key can be declared and how it interacts with Ingress and Service resources.
+HAProxy Ingress configuration keys may be in one of six distinct scopes: `Global`, `Frontend`, `Host`, `Backend`, `Path`, `TCP`. A scope defines where a configuration key value is applied in the HAProxy configuration, described in the sections below.
 
-Configuration keys declared in `Ingress` resources might conflict. More about
-the scenarios in the `Host`, `Backend` and `TCP` scopes below. A warning will
-be logged in the case of a conflict, and the used value will be of the Ingress
-resource that was created first.
+Configuration keys declared in `Ingress` resources might conflict in the case the same `Frontend` ports, `Host` hostname, `Backend` service, or the `TCP` port number is used on distinct ingress resources, and those ingress resources configure the same key with distinct values. In the case this happens, a warning will be logged and the used value will be of the Ingress resource that was created first.
 
 ### Global
 
-Defines configuration keys that apply for all hostnames and backend
-services, and should be declared only in the Global config ConfigMap
-resource. Configuration keys of the Global scope declared as Ingress
-or Service annotations, and also in the IngressClass ConfigMap are
-ignored. Configuration keys of the Global scope never conflict.
+Configuration keys from this scope are global for the whole HAProxy instance, and it is usually configured in the global area of the configuration file.
+
+The only way to configure global scoped keys are via the global config ConfigMap resource. They are ignored if declared elsewhere. Since these keys cannot be configured via Ingress resources, they will also never conflict.
+
+### Frontend
+
+Configuration keys from this scope are applied per HAProxy frontend, so it is always related with the current `http-port` and `https-port` values, which will indicate the correct frontend receiving the configuration. Distinct frontends can receive distinct values from keys of this scope.
+
+Frontend scoped keys can be declared in global or IngressClass related ConfigMaps as default values, or in any Ingress resource for a more granular configuration. They can conflict since they can be configured via Ingress resource.
+
+When a frontend scoped key is used as an annotation, they should always be configured along with both `http-port` and `https-port`, and those ports should have a distinct value from the global one, otherwise the global and frontend scoped configurations will conflict.
 
 ### Host
 
-Defines configuration keys that bind to the hostname. Configuration
-keys of the host scope can be declared in any ConfigMap, or in any Ingress
-resource. A conflict happens when the same host configuration key with
-distinct values are declared in distinct Ingress resources but to the same
-hostname.
+Configuration keys from the host scope are applied per hostname, which is more a Kubernetes API and HAProxy Ingress concept than a HAProxy one. Distinct hostnames can receive distinct values from keys of this scope.
+
+Host scoped keys can be declared in global or IngressClass related ConfigMaps as default values, or in any Ingress resource for a more granular configuration. They can conflict since they can be configured via Ingress resource.
 
 ### Backend
 
-Defines configuration keys that bind to the Service resource, which
-is converted to a HAProxy backend after the configuration parsing. Configuration
-keys of the backend scope can be declared in any ConfigMap or as Ingress or Service
-annotation. A conflict happens when the same backend configuration key with distinct
-values are declared in distinct Ingress resources but to the same Service or HAProxy
-backend.
+Configuration keys from this scope are applied per HAProxy backend, which has an one-to-one relationship with the Kubernetes Service when configured via Ingress API. So, distinct referenced services can receive distinct values from keys of this scope.
+
+Backend scoped keys can be declared in global or IngressClass related ConfigMaps as default values, or in any Ingress or Service resources for a more granular configuration. They can conflict when configured via Ingress, and will never conflict when configured via Service. In the case of a conflict between a Service and an Ingress, the Service configuration is used.
 
 ### Path
 
-Defines configuration keys that bind to the hostname and the HTTP path.
-Configuration keys of the Path scope can be declared in any ConfigMap as a default
-value, or as Ingress or Service annotation. Configuration keys of the Path scope
-never conflict.
+Configuration keys from this scope are applied per a combination of the hostname and the HTTP path. Just like the `Host` scope, it is more a Kubernetes API and HAProxy Ingress concept than a HAProxy one. Distinct combinations of hostname and path can receive distinct values from keys of this scope.
+
+Path scoped keys can be declared in global or IngressClass related ConfigMaps as default values, or in any Ingress or Service resource for a more granular configuration. They can only conflict in the case the same key is configured both in an Ingress and the Service it points to. They will never conflict if configured exclusively via Ingress, since the same path, with the same match type, under the same hostname cannot be configured more than once. If this happens, HAProxy Ingress will reject the path declaration instead of conflict the configuration key value.
 
 ### TCP
 
-Defines configuration keys that bind on the port number of a TCP service.
-Configuration keys of the TCP scope can be declared in any ConfigMap as a default
-value, or as Ingress annotation. A conflict happens when the same TCP configuration
-key with distinct values are declared in distinct Ingress resources but to the same
-TCP port number.
+Keys from this scope are applied per configured TCP port, assigned via the `tcp-service-port`. Every distinct TCP port creates a distinct HAProxy frontend, so distinct TCP port numbers, consequently their frontends, can receive distinct values from keys of this scope.
+
+TCP scoped keys can be declared in global or IngressClass related ConfigMaps as default values, or in any Ingress resource for a more granular configuration. They can conflict since they can be configured via Ingress resource.
 
 ## Keys
 
 The table below describes all supported configuration keys.
 
-| Configuration key                                    | Data type                               | Scope   | Default value      |
-|------------------------------------------------------|-----------------------------------------|---------|--------------------|
-| [`acme-emails`](#acme)                               | email1,email2,...                       | Global  |                    |
-| [`acme-endpoint`](#acme)                             | [`v2-staging`\|`v2`\|`endpoint`]        | Global  |                    |
-| [`acme-expiring`](#acme)                             | number of days                          | Global  | `30`               |
-| [`acme-preferred-chain`](#acme)                      | CN (Common Name) of the issuer          | Host    |                    |
-| [`acme-shared`](#acme)                               | [true\|false]                           | Global  | `false`            |
-| [`acme-terms-agreed`](#acme)                         | [true\|false]                           | Global  | `false`            |
-| [`affinity`](#affinity)                              | affinity type                           | Backend |                    |
-| [`agent-check-addr`](#agent-check)                   | address for agent checks                | Backend |                    |
-| [`agent-check-interval`](#agent-check)               | time with suffix                        | Backend |                    |
-| [`agent-check-port`](#agent-check)                   | backend agent listen port               | Backend |                    |
-| [`agent-check-send`](#agent-check)                   | string to send upon agent connection    | Backend |                    |
-| [`allowlist-source-range`](#allowlist)               | Comma-separated IPs or CIDRs            | Path    |                    |
-| [`allowlist-source-header`](#allowlist)              | Header name that will be used as a src  | Path    |                    |
-| [`app-root`](#app-root)                              | /url                                    | Host    |                    |
-| [`assign-backend-server-id`](#backend-server-id)     | [true\|false]                           | Backend | `false`            |
-| [`auth-external-placement`](#auth-external)          | [backend\|frontend]                     | Path    | `backend`          |
-| [`auth-headers-fail`](#auth-external)                | `<header>,...`                          | Path    | `*`                |
-| [`auth-headers-request`](#auth-external)             | `<header>,...`                          | Path    | `*`                |
-| [`auth-headers-succeed`](#auth-external)             | `<header>,...`                          | Path    | `*`                |
-| [`auth-log-format`](#log-format)                     | http log format for auth external       | Global  | do not log         |
-| [`auth-method`](#auth-external)                      | http request method                     | Path    | `GET`              |
-| [`auth-proxy`](#auth-external)                       | frontend name and tcp port interval     | Global  | `_front__auth:14415-14499` |
-| [`auth-realm`](#auth-basic)                          | realm string                            | Path    |                    |
-| [`auth-secret`](#auth-basic)                         | secret name                             | Path    |                    |
-| [`auth-signin`](#auth-external)                      | Sign in URL                             | Path    |                    |
-| [`auth-tls-cert-header`](#auth-tls)                  | [true\|false]                           | Backend |                    |
-| [`auth-tls-error-page`](#auth-tls)                   | url                                     | Host    |                    |
-| [`auth-tls-secret`](#auth-tls)                       | namespace/secret name                   | Host    |                    |
-| [`auth-tls-strict`](#auth-tls)                       | [true\|false]                           | Host    |                    |
-| [`auth-tls-verify-client`](#auth-tls)                | [off\|optional\|on\|optional_no_ca]     | Host    |                    |
-| [`auth-url`](#auth-external)                         | Authentication URL                      | Path    |                    |
-| [`backend-check-interval`](#health-check)            | time with suffix                        | Backend | `2s`               |
-| [`backend-protocol`](#backend-protocol)              | [h1\|h2\|h1-ssl\|h2-ssl]                | Backend | `h1`               |
-| [`backend-server-naming`](#backend-server-naming)    | [sequence\|ip\|pod]                     | Backend | `sequence`         |
-| [`backend-server-slots-increment`](#dynamic-scaling) | number of slots                         | Backend | `1`                |
-| [`balance-algorithm`](#balance-algorithm)            | algorithm name                          | Backend | `random(2)`        |
-| [`bind-fronting-proxy`](#bind)                       | ip + port                               | Global  |                    |
-| [`bind-http`](#bind)                                 | ip + port                               | Global  |                    |
-| [`bind-https`](#bind)                                | ip + port                               | Global  |                    |
-| [`bind-ip-addr-healthz`](#bind-ip-addr)              | IP address                              | Global  |                    |
-| [`bind-ip-addr-http`](#bind-ip-addr)                 | IP address                              | Global  |                    |
-| [`bind-ip-addr-prometheus`](#bind-ip-addr)           | IP address                              | Global  |                    |
-| [`bind-ip-addr-stats`](#bind-ip-addr)                | IP address                              | Global  |                    |
-| [`bind-ip-addr-tcp`](#bind-ip-addr)                  | IP address                              | Global  |                    |
-| [`blue-green-balance`](#blue-green)                  | label=value=weight,...                  | Backend |                    |
-| [`blue-green-cookie`](#blue-green)                   | `CookieName:LabelName` pair             | Backend |                    |
-| [`blue-green-deploy`](#blue-green)                   | label=value=weight,...                  | Backend |                    |
-| [`blue-green-header`](#blue-green)                   | `HeaderName:LabelName` pair             | Backend |                    |
-| [`blue-green-mode`](#blue-green)                     | [pod\|deploy]                           | Backend |                    |
-| [`cert-signer`](#acme)                               | "acme"                                  | Host    |                    |
-| [`close-sessions-duration`](#close-sessions-duration) | time with suffix or percentage         | Global  | leave sessions open |
-| [`config-backend`](#configuration-snippet)           | multiline backend config                | Backend |                    |
-| [`config-defaults`](#configuration-snippet)          | multiline config for the defaults section | Global |                   |
-| [`config-frontend`](#configuration-snippet)          | multiline HTTP and HTTPS frontend config | Global | |
-| [`config-frontend-early`](#configuration-snippet)    | multiline HTTP and HTTPS frontend config, applied before any builtin logic | Global  |                   |
-| [`config-frontend-late`](#configuration-snippet)     | multiline HTTP and HTTPS frontend config, same as `config-frontend` | Global  |                   |
-| [`config-global`](#configuration-snippet)            | multiline config for the global section | Global  |                    |
-| [`config-peers`](#configuration-snippet)             | multiline config for the peers section  | Global  |                    |
-| [`config-proxy`](#configuration-snippet)             | multiline config for any proxy          | Global  |                    |
-| [`config-sections`](#configuration-snippet)          | multiline custom sections declaration   | Global  |                    |
-| [`config-tcp`](#configuration-snippet)               | multiline ConfigMap based TCP config    | Global  |                    |
-| [`config-tcp-service`](#configuration-snippet)       | multiline TCP service config            | TCP     |                    |
-| [`cookie-key`](#affinity)                            | secret key                              | Global  | `Ingress`          |
-| [`cors-allow-credentials`](#cors)                    | [true\|false]                           | Path    |                    |
-| [`cors-allow-headers`](#cors)                        | headers list                            | Path    |                    |
-| [`cors-allow-methods`](#cors)                        | methods list                            | Path    |                    |
-| [`cors-allow-origin`](#cors)                         | URL                                     | Path    |                    |
-| [`cors-allow-origin-regex`](#cors)                   | regex                                   | Path    |                    |
-| [`cors-enable`](#cors)                               | [true\|false]                           | Path    |                    |
-| [`cors-expose-headers`](#cors)                       | headers                                 | Path    |                    |
-| [`cors-max-age`](#cors)                              | time (seconds)                          | Path    |                    |
-| [`cpu-map`](#cpu-map)                                | haproxy CPU Map format                  | Global  |                    |
-| [`cross-namespace-secrets-ca`](#cross-namespace)     | [allow\|deny]                           | Global  | `deny`             |
-| [`cross-namespace-secrets-crt`](#cross-namespace)    | [allow\|deny]                           | Global  | `deny`             |
-| [`cross-namespace-secrets-passwd`](#cross-namespace) | [allow\|deny]                           | Global  | `deny`             |
-| [`cross-namespace-services`](#cross-namespace)       | [allow\|deny]                           | Global  | `deny`             |
-| [`default-backend-redirect`](#default-redirect)      | Location                                | Global  |                    |
-| [`default-backend-redirect-code`](#default-redirect) | HTTP status code                        | Global  | `302`              |
-| [`denylist-source-range`](#allowlist)                | Comma-separated IPs or CIDRs            | Path    |                    |
-| [`dns-accepted-payload-size`](#dns-resolvers)        | number                                  | Global  | `8192`             |
-| [`dns-cluster-domain`](#dns-resolvers)               | cluster name                            | Global  | `cluster.local`    |
-| [`dns-hold-obsolete`](#dns-resolvers)                | time with suffix                        | Global  | `0s`               |
-| [`dns-hold-valid`](#dns-resolvers)                   | time with suffix                        | Global  | `1s`               |
-| [`dns-resolvers`](#dns-resolvers)                    | multiline resolver=ip[:port]            | Global  |                    |
-| [`dns-timeout-retry`](#dns-resolvers)                | time with suffix                        | Global  | `1s`               |
-| [`drain-support`](#drain-support)                    | [true\|false]                           | Global  | `false`            |
-| [`drain-support-redispatch`](#drain-support)         | [true\|false]                           | Global  | `true`             |
-| [`dynamic-scaling`](#dynamic-scaling)                | [true\|false]                           | Backend | `true`             |
-| [`external-has-lua`](#external)                      | [true\|false]                           | Global  | `false`            |
-| [`fcgi-app`](#fastcgi)                               | fcgi-app section name                   | Backend |                    |
-| [`fcgi-enabled-apps`](#fastcgi)                      | comma-separated list of names           | Global  | `*`                |
-| [`forwardfor`](#forwardfor)                          | [add\|ignore\|ifmissing]                | Global  | `add`              |
-| [`fronting-proxy-port`](#fronting-proxy-port)        | port number                             | Global  | 0 (do not listen)  |
-| [`groupname`](#security)                             | haproxy group name                      | Global  | `haproxy`          |
-| [`headers`](#headers)                                | multiline header:value pair             | Backend |                    |
-| [`health-check-addr`](#health-check)                 | address for health checks               | Backend |                    |
-| [`health-check-fall-count`](#health-check)           | number of failures                      | Backend |                    |
-| [`health-check-interval`](#health-check)             | time with suffix                        | Backend |                    |
-| [`health-check-port`](#health-check)                 | port for health checks                  | Backend |                    |
-| [`health-check-rise-count`](#health-check)           | number of successes                     | Backend |                    |
-| [`health-check-uri`](#health-check)                  | uri for http health checks              | Backend |                    |
-| [`healthz-port`](#bind-port)                         | port number                             | Global  | `10253`            |
-| [`hsts`](#hsts)                                      | [true\|false]                           | Path    | `true`             |
-| [`hsts-include-subdomains`](#hsts)                   | [true\|false]                           | Path    | `false`            |
-| [`hsts-max-age`](#hsts)                              | number of seconds                       | Path    | `15768000`         |
-| [`hsts-preload`](#hsts)                              | [true\|false]                           | Path    | `false`            |
-| [`http-header-match`](#http-match)                   | header name and value, exact match      | Path    |                    |
-| [`http-header-match-regex`](#http-match)             | header name and value, regex match      | Path    |                    |
-| [`http-log-format`](#log-format)                     | http log format                         | Global  | HAProxy default log format |
-| [`http-port`](#bind-port)                            | port number                             | Global  | `80`               |
-| [`http-response-<code>`](#http-response)             | response output                         | vary    |                    |
-| [`http-response-prometheus-root`](#http-response)    | response output                         | Global  |                    |
-| [`https-log-format`](#log-format)                    | https(tcp) log format\|`default`        | Global  | do not log         |
-| [`https-port`](#bind-port)                           | port number                             | Global  | `443`              |
-| [`https-to-http-port`](#fronting-proxy-port)         | port number                             | Global  | 0 (do not listen)  |
-| [`initial-weight`](#initial-weight)                  | weight value                            | Backend | `1`                |
-| [`limit-connections`](#limit)                        | qty                                     | Backend |                    |
-| [`limit-rps`](#limit)                                | rate per second                         | Backend |                    |
-| [`limit-whitelist`](#limit)                          | cidr list                               | Backend |                    |
-| [`load-server-state`](#load-server-state) (experimental) |[true\|false]                        | Global  | `false`            |
-| [`master-exit-on-failure`](#master-worker)           | [true\|false]                           | Global  | `true`             |
-| [`max-connections`](#connection)                     | number                                  | Global  | `2000`             |
-| [`maxconn-server`](#connection)                      | qty                                     | Backend |                    |
-| [`maxqueue-server`](#connection)                     | qty                                     | Backend |                    |
-| [`modsecurity-args`](#modsecurity)                   | space-separated list of strings         | Global  | `unique-id method path query req.ver req.hdrs_bin req.body_size req.body` |
-| [`modsecurity-endpoints`](#modsecurity)              | comma-separated list of IP:port (spoa)  | Global  | no waf config      |
-| [`modsecurity-timeout-hello`](#modsecurity)          | time with suffix                        | Global  | `100ms`            |
-| [`modsecurity-timeout-idle`](#modsecurity)           | time with suffix                        | Global  | `30s`              |
-| [`modsecurity-timeout-processing`](#modsecurity)     | time with suffix                        | Global  | `1s`               |
-| [`modsecurity-use-coraza`](#modsecurity)             | [true\|false]                           | Global  | `false`               |
-| [`nbproc-ssl`](#nbproc)                              | number of process                       | Global  | `0`                |
-| [`nbthread`](#nbthread)                              | number of threads                       | Global  |                    |
-| [`no-redirect-locations`](#redirect)                 | comma-separated list of URIs            | Global  | `/.well-known/acme-challenge` |
-| [`no-tls-redirect-locations`](#ssl-redirect)         | comma-separated list of URIs            | Global  | `/.well-known/acme-challenge` |
-| [`oauth`](#oauth)                                    | "oauth2_proxy"                          | Path    |                    |
-| [`oauth-headers`](#oauth)                            | `<header>:<var>,...`                    | Path    |                    |
-| [`oauth-uri-prefix`](#oauth)                         | URI prefix                              | Path    |                    |
-| [`original-forwarded-for-hdr`](#forwardfor)          | header name                             | Global  | `X-Original-Forwarded-For` |
-| [`path-type`](#path-type)                            | path matching type                      | Path    | `begin`            |
-| [`path-type-order`](#path-type)                      | comma-separated path type list          | Global  | `exact,prefix,begin,regex` |
-| [`peers-name`](#peers)                               | peers section name                      | Global  | `ingress`          |
-| [`peers-port`](#peers)                               | port number                             | Global  |                    |
-| [`peers-table`](#peers)                              | stick-table declaration                 | Backend |                    |
-| [`peers-table-global`](#peers)                       | stick-table declaration                 | Global  |                    |
-| [`prometheus-port`](#bind-port)                      | port number                             | Global  |                    |
-| [`proxy-body-size`](#proxy-body-size)                | size (bytes)                            | Path    | unlimited          |
-| [`proxy-protocol`](#proxy-protocol)                  | [v1\|v2\|v2-ssl\|v2-ssl-cn]             | Backend |                    |
-| [`real-ip-hdr`](#forwardfor)                         | header name                             | Global  | `X-Real-IP`        |
-| [`redirect-from`](#redirect)                         | domain name                             | Host    |                    |
-| [`redirect-from-code`](#redirect)                    | http status code                        | Global  | `302`              |
-| [`redirect-from-regex`](#redirect)                   | regex                                   | Host    |                    |
-| [`redirect-to`](#redirect)                           | fully qualified URL                     | Path    |                    |
-| [`redirect-to-code`](#redirect)                      | http status code                        | Global  | `302`              |
-| [`rewrite-target`](#rewrite-target)                  | path string                             | Path    |                    |
-| [`secure-backends`](#secure-backend)                 | [true\|false]                           | Backend |                    |
-| [`secure-crt-secret`](#secure-backend)               | secret name                             | Backend |                    |
-| [`secure-sni`](#secure-backend)                      | [`sni`\|`host`\|`<hostname>`]           | Backend |                    |
-| [`secure-verify-ca-secret`](#secure-backend)         | secret name                             | Backend |                    |
-| [`secure-verify-hostname`](#secure-backend)          | hostname                                | Backend |                    |
-| [`server-alias`](#server-alias)                      | domain name                             | Host    |                    |
-| [`server-alias-regex`](#server-alias)                | regex                                   | Host    |                    |
-| [`service-upstream`](#service-upstream)              | [true\|false]                           | Backend | `false`            |
-| [`session-cookie-domain`](#affinity)                 | domain name                             | Backend |                    |
-| [`session-cookie-dynamic`](#affinity)                | [true\|false]                           | Backend |                    |
-| [`session-cookie-keywords`](#affinity)               | cookie options                          | Backend | `indirect nocache httponly`     |
-| [`session-cookie-name`](#affinity)                   | cookie name                             | Backend |                    |
-| [`session-cookie-preserve`](#affinity)               | [true\|false]                           | Backend | `false`            |
-| [`session-cookie-shared`](#affinity)                 | [true\|false]                           | Backend | `false`            |
-| [`session-cookie-strategy`](#affinity)               | [insert\|prefix\|rewrite]               | Backend |                    |
-| [`session-cookie-value-strategy`](#affinity)         | [server-name\|pod-uid]                  | Backend | `server-name`      |
-| [`slots-min-free`](#dynamic-scaling)                 | minimum number of free slots            | Backend | `0`                |
-| [`source-address-intf`](#source-address-intf)        | `<intf1>[,<intf2>...]`                  | Backend |                    |
-| [`ssl-always-add-https`](#ssl-always-add-https)      | [true\|false]                           | Host    | `false`            |
-| [`ssl-always-follow-redirect`](#ssl-always-add-https) | [true\|false]                          | Host    | `true`             |
-| [`ssl-cipher-suites`](#ssl-ciphers)                  | colon-separated list                    | Host    | [see description](#ssl-ciphers) |
-| [`ssl-cipher-suites-backend`](#ssl-ciphers)          | colon-separated list                    | Backend | [see description](#ssl-ciphers) |
-| [`ssl-ciphers`](#ssl-ciphers)                        | colon-separated list                    | Host    | [see description](#ssl-ciphers) |
-| [`ssl-ciphers-backend`](#ssl-ciphers)                | colon-separated list                    | Backend | [see description](#ssl-ciphers) |
-| [`ssl-dh-default-max-size`](#ssl-dh)                 | number                                  | Global  | `1024`             |
-| [`ssl-dh-param`](#ssl-dh)                            | namespace/secret name                   | Global  | no custom DH param |
-| [`ssl-engine`](#ssl-engine)                          | OpenSSL engine name and parameters      | Global  | no engine set      |
-| [`ssl-fingerprint-lower`](#auth-tls)                 | [true\|false]                           | Backend | `false`            |
-| [`ssl-fingerprint-sha2-bits`](#auth-tls)             | Bits of the SHA-2 fingerprint           | Backend |                    |
-| [`ssl-headers-prefix`](#auth-tls)                    | prefix                                  | Global  | `X-SSL`            |
-| [`ssl-mode-async`](#ssl-engine)                      | [true\|false]                           | Global  | `false`            |
-| [`ssl-options`](#ssl-options)                        | space-separated list                    | Global  | [see description](#ssl-options) |
-| [`ssl-options-backend`](#ssl-options)                | space-separated list                    | Backend | [see description](#ssl-options) |
-| [`ssl-options-host`](#ssl-options)                   | space-separated list                    | Host    | [see description](#ssl-options) |
-| [`ssl-passthrough`](#ssl-passthrough)                | [true\|false]                           | Host    |                    |
-| [`ssl-passthrough-http-port`](#ssl-passthrough)      | backend port                            | Host    |                    |
-| [`ssl-redirect`](#ssl-redirect)                      | [true\|false]                           | Path    | `true`             |
-| [`ssl-redirect-code`](#ssl-redirect)                 | http status code                        | Global  | `302`              |
-| [`stats-auth`](#stats)                               | user:passwd                             | Global  | no auth            |
-| [`stats-port`](#stats)                               | port number                             | Global  | `1936`             |
-| [`stats-proxy-protocol`](#stats)                     | [true\|false]                           | Global  | `false`            |
-| [`stats-ssl-cert`](#stats)                           | namespace/secret name                   | Global  | no ssl/plain http  |
-| [`strict-host`](#strict-host)                        | [true\|false]                           | Global  | `false`            |
-| [`syslog-endpoint`](#syslog)                         | IP:port (udp)                           | Global  | do not log         |
-| [`syslog-format`](#syslog)                           | rfc5424\|rfc3164                        | Global  | `rfc5424`          |
-| [`syslog-length`](#syslog)                           | maximum length                          | Global  | `1024`             |
-| [`syslog-tag`](#syslog)                              | syslog tag field string                 | Global  | `ingress`          |
-| [`tcp-log-format`](#log-format)                      | ConfigMap based TCP log format          | Global  |                    |
-| [`tcp-service-log-format`](#log-format)              | TCP service log format                  | TCP     | HAProxy default log format |
-| [`tcp-service-port`](#tcp-services)                  | TCP service port number                 | TCP     |                    |
-| [`tcp-service-proxy-protocol`](#proxy-protocol)      | [true\|false]                           | TCP     | `false`            |
-| [`timeout-client`](#timeout)                         | time with suffix                        | Global  | `50s`              |
-| [`timeout-client-fin`](#timeout)                     | time with suffix                        | Global  | `50s`              |
-| [`timeout-connect`](#timeout)                        | time with suffix                        | Backend | `5s`               |
-| [`timeout-http-request`](#timeout)                   | time with suffix                        | Backend | `5s`               |
-| [`timeout-keep-alive`](#timeout)                     | time with suffix                        | Backend | `1m`               |
-| [`timeout-queue`](#timeout)                          | time with suffix                        | Backend | `5s`               |
-| [`timeout-server`](#timeout)                         | time with suffix                        | Backend | `50s`              |
-| [`timeout-server-fin`](#timeout)                     | time with suffix                        | Backend | `50s`              |
-| [`timeout-stop`](#timeout)                           | time with suffix                        | Global  | `10m`              |
-| [`timeout-tunnel`](#timeout)                         | time with suffix                        | Backend | `1h`               |
-| [`tls-alpn`](#tls-alpn)                              | TLS ALPN advertisement                  | Host    | `h2,http/1.1`      |
-| [`use-chroot`](#security)                            | [true\|false]                           | Global  | `false`            |
-| [`use-cpu-map`](#cpu-map)                            | [true\|false]                           | Global  | `true`             |
-| [`use-forwarded-proto`](#fronting-proxy-port)        | [true\|false]                           | Global  | `true`             |
-| [`use-haproxy-user`](#security)                      | [true\|false]                           | Global  | `false`            |
-| [`use-htx`](#use-htx)                                | [true\|false]                           | Global  | `false`            |
-| [`use-proxy-protocol`](#proxy-protocol)              | [true\|false]                           | Global  | `false`            |
-| [`use-resolver`](#dns-resolvers)                     | resolver name                           | Backend |                    |
-| [`username`](#security)                              | haproxy user name                       | Global  | `haproxy`          |
-| [`var-namespace`](#var-namespace)                    | [true\|false]                           | Host    | `false`            |
-| [`waf`](#waf)                                        | "modsecurity"                           | Path    |                    |
-| [`waf-mode`](#waf)                                   | [deny\|detect]                          | Path    | `deny` (if waf is set) |
-| [`whitelist-source-range`](#allowlist)               | Comma-separated IPs or CIDRs            | Path    |                    |
-| [`worker-max-reloads`](#master-worker)               | number of reloads                       | Global  | `0`                |
+| Configuration key                                    | Data type                               | Scope    | Default value                    |
+|------------------------------------------------------|-----------------------------------------|----------|----------------------------------|
+| [`acme-emails`](#acme)                               | email1,email2,...                       | Global   |                                  |
+| [`acme-endpoint`](#acme)                             | [`v2-staging`\|`v2`\|`endpoint`]        | Global   |                                  |
+| [`acme-expiring`](#acme)                             | number of days                          | Global   | `30`                             |
+| [`acme-preferred-chain`](#acme)                      | CN (Common Name) of the issuer          | Host     |                                  |
+| [`acme-shared`](#acme)                               | [true\|false]                           | Global   | `false`                          |
+| [`acme-terms-agreed`](#acme)                         | [true\|false]                           | Global   | `false`                          |
+| [`affinity`](#affinity)                              | affinity type                           | Backend  |                                  |
+| [`agent-check-addr`](#agent-check)                   | address for agent checks                | Backend  |                                  |
+| [`agent-check-interval`](#agent-check)               | time with suffix                        | Backend  |                                  |
+| [`agent-check-port`](#agent-check)                   | backend agent listen port               | Backend  |                                  |
+| [`agent-check-send`](#agent-check)                   | string to send upon agent connection    | Backend  |                                  |
+| [`allowlist-source-range`](#allowlist)               | Comma-separated IPs or CIDRs            | Path     |                                  |
+| [`allowlist-source-header`](#allowlist)              | Header name that will be used as a src  | Path     |                                  |
+| [`app-root`](#app-root)                              | /url                                    | Host     |                                  |
+| [`assign-backend-server-id`](#backend-server-id)     | [true\|false]                           | Backend  | `false`                          |
+| [`auth-external-placement`](#auth-external)          | [backend\|frontend]                     | Path     | `backend`                        |
+| [`auth-headers-fail`](#auth-external)                | `<header>,...`                          | Path     | `*`                              |
+| [`auth-headers-request`](#auth-external)             | `<header>,...`                          | Path     | `*`                              |
+| [`auth-headers-succeed`](#auth-external)             | `<header>,...`                          | Path     | `*`                              |
+| [`auth-log-format`](#log-format)                     | http log format for auth external       | Global   | do not log                       |
+| [`auth-method`](#auth-external)                      | http request method                     | Path     | `GET`                            |
+| [`auth-proxy`](#auth-external)                       | frontend name and tcp port interval     | Global   | `_front__auth:14415-14499`       |
+| [`auth-realm`](#auth-basic)                          | realm string                            | Path     |                                  |
+| [`auth-secret`](#auth-basic)                         | secret name                             | Path     |                                  |
+| [`auth-signin`](#auth-external)                      | Sign in URL                             | Path     |                                  |
+| [`auth-tls-cert-header`](#auth-tls)                  | [true\|false]                           | Backend  |                                  |
+| [`auth-tls-error-page`](#auth-tls)                   | url                                     | Host     |                                  |
+| [`auth-tls-secret`](#auth-tls)                       | namespace/secret name                   | Host     |                                  |
+| [`auth-tls-strict`](#auth-tls)                       | [true\|false]                           | Host     |                                  |
+| [`auth-tls-verify-client`](#auth-tls)                | [off\|optional\|on\|optional_no_ca]     | Host     |                                  |
+| [`auth-url`](#auth-external)                         | Authentication URL                      | Path     |                                  |
+| [`backend-check-interval`](#health-check)            | time with suffix                        | Backend  | `2s`                             |
+| [`backend-protocol`](#backend-protocol)              | [h1\|h2\|h1-ssl\|h2-ssl]                | Backend  | `h1`                             |
+| [`backend-server-naming`](#backend-server-naming)    | [sequence\|ip\|pod]                     | Backend  | `sequence`                       |
+| [`backend-server-slots-increment`](#dynamic-scaling) | number of slots                         | Backend  | `1`                              |
+| [`balance-algorithm`](#balance-algorithm)            | algorithm name                          | Backend  | `random(2)`                      |
+| [`bind-fronting-proxy`](#bind)                       | ip + port                               | Frontend |                                  |
+| [`bind-http`](#bind)                                 | ip + port                               | Frontend |                                  |
+| [`bind-https`](#bind)                                | ip + port                               | Frontend |                                  |
+| [`bind-ip-addr-healthz`](#bind-ip-addr)              | IP address                              | Global   |                                  |
+| [`bind-ip-addr-http`](#bind-ip-addr)                 | IP address                              | Frontend |                                  |
+| [`bind-ip-addr-prometheus`](#bind-ip-addr)           | IP address                              | Global   |                                  |
+| [`bind-ip-addr-stats`](#bind-ip-addr)                | IP address                              | Global   |                                  |
+| [`bind-ip-addr-tcp`](#bind-ip-addr)                  | IP address                              | Global   |                                  |
+| [`blue-green-balance`](#blue-green)                  | label=value=weight,...                  | Backend  |                                  |
+| [`blue-green-cookie`](#blue-green)                   | `CookieName:LabelName` pair             | Backend  |                                  |
+| [`blue-green-deploy`](#blue-green)                   | label=value=weight,...                  | Backend  |                                  |
+| [`blue-green-header`](#blue-green)                   | `HeaderName:LabelName` pair             | Backend  |                                  |
+| [`blue-green-mode`](#blue-green)                     | [pod\|deploy]                           | Backend  |                                  |
+| [`cert-signer`](#acme)                               | "acme"                                  | Host     |                                  |
+| [`close-sessions-duration`](#close-sessions-duration) | time with suffix or percentage         | Global   | leave sessions open              |
+| [`config-backend`](#configuration-snippet)           | multiline backend config                | Backend  |                                  |
+| [`config-defaults`](#configuration-snippet)          | multiline config for the defaults section | Global |                                  |
+| [`config-frontend`](#configuration-snippet)          | multiline HTTP and HTTPS frontend config | Global  |                                  |
+| [`config-frontend-early`](#configuration-snippet)    | multiline HTTP and HTTPS frontend config, applied before any builtin logic | Global | |
+| [`config-frontend-late`](#configuration-snippet)     | multiline HTTP and HTTPS frontend config, same as `config-frontend` | Global |        |
+| [`config-global`](#configuration-snippet)            | multiline config for the global section | Global   |                                  |
+| [`config-peers`](#configuration-snippet)             | multiline config for the peers section  | Global   |                                  |
+| [`config-proxy`](#configuration-snippet)             | multiline config for any proxy          | Global   |                                  |
+| [`config-sections`](#configuration-snippet)          | multiline custom sections declaration   | Global   |                                  |
+| [`config-tcp`](#configuration-snippet)               | multiline ConfigMap based TCP config    | Global   |                                  |
+| [`config-tcp-service`](#configuration-snippet)       | multiline TCP service config            | TCP      |                                  |
+| [`cookie-key`](#affinity)                            | secret key                              | Global   | `Ingress`                        |
+| [`cors-allow-credentials`](#cors)                    | [true\|false]                           | Path     |                                  |
+| [`cors-allow-headers`](#cors)                        | headers list                            | Path     |                                  |
+| [`cors-allow-methods`](#cors)                        | methods list                            | Path     |                                  |
+| [`cors-allow-origin`](#cors)                         | URL                                     | Path     |                                  |
+| [`cors-allow-origin-regex`](#cors)                   | regex                                   | Path     |                                  |
+| [`cors-enable`](#cors)                               | [true\|false]                           | Path     |                                  |
+| [`cors-expose-headers`](#cors)                       | headers                                 | Path     |                                  |
+| [`cors-max-age`](#cors)                              | time (seconds)                          | Path     |                                  |
+| [`cpu-map`](#cpu-map)                                | haproxy CPU Map format                  | Global   |                                  |
+| [`cross-namespace-secrets-ca`](#cross-namespace)     | [allow\|deny]                           | Global   | `deny`                           |
+| [`cross-namespace-secrets-crt`](#cross-namespace)    | [allow\|deny]                           | Global   | `deny`                           |
+| [`cross-namespace-secrets-passwd`](#cross-namespace) | [allow\|deny]                           | Global   | `deny`                           |
+| [`cross-namespace-services`](#cross-namespace)       | [allow\|deny]                           | Global   | `deny`                           |
+| [`default-backend-redirect`](#default-redirect)      | Location                                | Global   |                                  |
+| [`default-backend-redirect-code`](#default-redirect) | HTTP status code                        | Global   | `302`                            |
+| [`denylist-source-range`](#allowlist)                | Comma-separated IPs or CIDRs            | Path     |                                  |
+| [`dns-accepted-payload-size`](#dns-resolvers)        | number                                  | Global   | `8192`                           |
+| [`dns-cluster-domain`](#dns-resolvers)               | cluster name                            | Global   | `cluster.local`                  |
+| [`dns-hold-obsolete`](#dns-resolvers)                | time with suffix                        | Global   | `0s`                             |
+| [`dns-hold-valid`](#dns-resolvers)                   | time with suffix                        | Global   | `1s`                             |
+| [`dns-resolvers`](#dns-resolvers)                    | multiline resolver=ip[:port]            | Global   |                                  |
+| [`dns-timeout-retry`](#dns-resolvers)                | time with suffix                        | Global   | `1s`                             |
+| [`drain-support`](#drain-support)                    | [true\|false]                           | Global   | `false`                          |
+| [`drain-support-redispatch`](#drain-support)         | [true\|false]                           | Global   | `true`                           |
+| [`dynamic-scaling`](#dynamic-scaling)                | [true\|false]                           | Backend  | `true`                           |
+| [`external-has-lua`](#external)                      | [true\|false]                           | Global   | `false`                          |
+| [`fcgi-app`](#fastcgi)                               | fcgi-app section name                   | Backend  |                                  |
+| [`fcgi-enabled-apps`](#fastcgi)                      | comma-separated list of names           | Global   | `*`                              |
+| [`forwardfor`](#forwardfor)                          | [add\|ignore\|ifmissing]                | Global   | `add`                            |
+| [`fronting-proxy-port`](#fronting-proxy-port)        | port number                             | Frontend | 0 (do not listen)                |
+| [`groupname`](#security)                             | haproxy group name                      | Global   | `haproxy`                        |
+| [`headers`](#headers)                                | multiline header:value pair             | Backend  |                                  |
+| [`health-check-addr`](#health-check)                 | address for health checks               | Backend  |                                  |
+| [`health-check-fall-count`](#health-check)           | number of failures                      | Backend  |                                  |
+| [`health-check-interval`](#health-check)             | time with suffix                        | Backend  |                                  |
+| [`health-check-port`](#health-check)                 | port for health checks                  | Backend  |                                  |
+| [`health-check-rise-count`](#health-check)           | number of successes                     | Backend  |                                  |
+| [`health-check-uri`](#health-check)                  | uri for http health checks              | Backend  |                                  |
+| [`healthz-port`](#bind-port)                         | port number                             | Global   | `10253`                          |
+| [`hsts`](#hsts)                                      | [true\|false]                           | Path     | `true`                           |
+| [`hsts-include-subdomains`](#hsts)                   | [true\|false]                           | Path     | `false`                          |
+| [`hsts-max-age`](#hsts)                              | number of seconds                       | Path     | `15768000`                       |
+| [`hsts-preload`](#hsts)                              | [true\|false]                           | Path     | `false`                          |
+| [`http-header-match`](#http-match)                   | header name and value, exact match      | Path     |                                  |
+| [`http-header-match-regex`](#http-match)             | header name and value, regex match      | Path     |                                  |
+| [`http-log-format`](#log-format)                     | http log format                         | Global   | HAProxy default log format       |
+| [`http-port`](#bind-port)                            | port number                             | Frontend | `80`                             |
+| [`http-response-<code>`](#http-response)             | response output                         | vary     |                                  |
+| [`http-response-prometheus-root`](#http-response)    | response output                         | Global   |                                  |
+| [`https-log-format`](#log-format)                    | https(tcp) log format\|`default`        | Global   | do not log                       |
+| [`https-port`](#bind-port)                           | port number                             | Frontend | `443`                            |
+| [`https-to-http-port`](#fronting-proxy-port)         | port number                             | Frontend | 0 (do not listen)                |
+| [`initial-weight`](#initial-weight)                  | weight value                            | Backend  | `1`                              |
+| [`limit-connections`](#limit)                        | qty                                     | Backend  |                                  |
+| [`limit-rps`](#limit)                                | rate per second                         | Backend  |                                  |
+| [`limit-whitelist`](#limit)                          | cidr list                               | Backend  |                                  |
+| [`load-server-state`](#load-server-state) (experimental) |[true\|false]                        | Global   | `false`                          |
+| [`master-exit-on-failure`](#master-worker)           | [true\|false]                           | Global   | `true`                           |
+| [`max-connections`](#connection)                     | number                                  | Global   | `2000`                           |
+| [`maxconn-server`](#connection)                      | qty                                     | Backend  |                                  |
+| [`maxqueue-server`](#connection)                     | qty                                     | Backend  |                                  |
+| [`modsecurity-args`](#modsecurity)                   | space-separated list of strings         | Global   | `unique-id method path query req.ver req.hdrs_bin req.body_size req.body` |
+| [`modsecurity-endpoints`](#modsecurity)              | comma-separated list of IP:port (spoa)  | Global   | no waf config                    |
+| [`modsecurity-timeout-hello`](#modsecurity)          | time with suffix                        | Global   | `100ms`                          |
+| [`modsecurity-timeout-idle`](#modsecurity)           | time with suffix                        | Global   | `30s`                            |
+| [`modsecurity-timeout-processing`](#modsecurity)     | time with suffix                        | Global   | `1s`                             |
+| [`modsecurity-use-coraza`](#modsecurity)             | [true\|false]                           | Global   | `false`                          |
+| [`nbproc-ssl`](#nbproc)                              | number of process                       | Global   | `0`                              |
+| [`nbthread`](#nbthread)                              | number of threads                       | Global   |                                  |
+| [`no-redirect-locations`](#redirect)                 | comma-separated list of URIs            | Global   | `/.well-known/acme-challenge`    |
+| [`no-tls-redirect-locations`](#ssl-redirect)         | comma-separated list of URIs            | Global   | `/.well-known/acme-challenge`    |
+| [`oauth`](#oauth)                                    | "oauth2_proxy"                          | Path     |                                  |
+| [`oauth-headers`](#oauth)                            | `<header>:<var>,...`                    | Path     |                                  |
+| [`oauth-uri-prefix`](#oauth)                         | URI prefix                              | Path     |                                  |
+| [`original-forwarded-for-hdr`](#forwardfor)          | header name                             | Global   | `X-Original-Forwarded-For`       |
+| [`path-type`](#path-type)                            | path matching type                      | Path     | `begin`                          |
+| [`path-type-order`](#path-type)                      | comma-separated path type list          | Global   | `exact,prefix,begin,regex`       |
+| [`peers-name`](#peers)                               | peers section name                      | Global   | `ingress`                        |
+| [`peers-port`](#peers)                               | port number                             | Global   |                                  |
+| [`peers-table`](#peers)                              | stick-table declaration                 | Backend  |                                  |
+| [`peers-table-global`](#peers)                       | stick-table declaration                 | Global   |                                  |
+| [`prometheus-port`](#bind-port)                      | port number                             | Global   |                                  |
+| [`proxy-body-size`](#proxy-body-size)                | size (bytes)                            | Path     | unlimited                        |
+| [`proxy-protocol`](#proxy-protocol)                  | [v1\|v2\|v2-ssl\|v2-ssl-cn]             | Backend  |                                  |
+| [`real-ip-hdr`](#forwardfor)                         | header name                             | Global   | `X-Real-IP`                      |
+| [`redirect-from`](#redirect)                         | domain name                             | Host     |                                  |
+| [`redirect-from-code`](#redirect)                    | http status code                        | Frontend | `302`                            |
+| [`redirect-from-regex`](#redirect)                   | regex                                   | Host     |                                  |
+| [`redirect-to`](#redirect)                           | fully qualified URL                     | Path     |                                  |
+| [`redirect-to-code`](#redirect)                      | http status code                        | Frontend | `302`                            |
+| [`rewrite-target`](#rewrite-target)                  | path string                             | Path     |                                  |
+| [`secure-backends`](#secure-backend)                 | [true\|false]                           | Backend  |                                  |
+| [`secure-crt-secret`](#secure-backend)               | secret name                             | Backend  |                                  |
+| [`secure-sni`](#secure-backend)                      | [`sni`\|`host`\|`<hostname>`]           | Backend  |                                  |
+| [`secure-verify-ca-secret`](#secure-backend)         | secret name                             | Backend  |                                  |
+| [`secure-verify-hostname`](#secure-backend)          | hostname                                | Backend  |                                  |
+| [`server-alias`](#server-alias)                      | domain name                             | Host     |                                  |
+| [`server-alias-regex`](#server-alias)                | regex                                   | Host     |                                  |
+| [`service-upstream`](#service-upstream)              | [true\|false]                           | Backend  | `false`                          |
+| [`session-cookie-domain`](#affinity)                 | domain name                             | Backend  |                                  |
+| [`session-cookie-dynamic`](#affinity)                | [true\|false]                           | Backend  |                                  |
+| [`session-cookie-keywords`](#affinity)               | cookie options                          | Backend  | `indirect nocache httponly`      |
+| [`session-cookie-name`](#affinity)                   | cookie name                             | Backend  |                                  |
+| [`session-cookie-preserve`](#affinity)               | [true\|false]                           | Backend  | `false`                          |
+| [`session-cookie-shared`](#affinity)                 | [true\|false]                           | Backend  | `false`                          |
+| [`session-cookie-strategy`](#affinity)               | [insert\|prefix\|rewrite]               | Backend  |                                  |
+| [`session-cookie-value-strategy`](#affinity)         | [server-name\|pod-uid]                  | Backend  | `server-name`                    |
+| [`slots-min-free`](#dynamic-scaling)                 | minimum number of free slots            | Backend  | `0`                              |
+| [`source-address-intf`](#source-address-intf)        | `<intf1>[,<intf2>...]`                  | Backend  |                                  |
+| [`ssl-always-add-https`](#ssl-always-add-https)      | [true\|false]                           | Host     | `false`                          |
+| [`ssl-always-follow-redirect`](#ssl-always-add-https) | [true\|false]                          | Host     | `true`                           |
+| [`ssl-cipher-suites`](#ssl-ciphers)                  | colon-separated list                    | Host     | [see description](#ssl-ciphers)  |
+| [`ssl-cipher-suites-backend`](#ssl-ciphers)          | colon-separated list                    | Backend  | [see description](#ssl-ciphers)  |
+| [`ssl-ciphers`](#ssl-ciphers)                        | colon-separated list                    | Host     | [see description](#ssl-ciphers)  |
+| [`ssl-ciphers-backend`](#ssl-ciphers)                | colon-separated list                    | Backend  | [see description](#ssl-ciphers)  |
+| [`ssl-dh-default-max-size`](#ssl-dh)                 | number                                  | Global   | `1024`                           |
+| [`ssl-dh-param`](#ssl-dh)                            | namespace/secret name                   | Global   | no custom DH param               |
+| [`ssl-engine`](#ssl-engine)                          | OpenSSL engine name and parameters      | Global   | no engine set                    |
+| [`ssl-fingerprint-lower`](#auth-tls)                 | [true\|false]                           | Backend  | `false`                          |
+| [`ssl-fingerprint-sha2-bits`](#auth-tls)             | Bits of the SHA-2 fingerprint           | Backend  |                                  |
+| [`ssl-headers-prefix`](#auth-tls)                    | prefix                                  | Global   | `X-SSL`                          |
+| [`ssl-mode-async`](#ssl-engine)                      | [true\|false]                           | Global   | `false`                          |
+| [`ssl-options`](#ssl-options)                        | space-separated list                    | Global   | [see description](#ssl-options)  |
+| [`ssl-options-backend`](#ssl-options)                | space-separated list                    | Backend  | [see description](#ssl-options)  |
+| [`ssl-options-host`](#ssl-options)                   | space-separated list                    | Host     | [see description](#ssl-options)  |
+| [`ssl-passthrough`](#ssl-passthrough)                | [true\|false]                           | Host     |                                  |
+| [`ssl-passthrough-http-port`](#ssl-passthrough)      | backend port                            | Host     |                                  |
+| [`ssl-redirect`](#ssl-redirect)                      | [true\|false]                           | Path     | `true`                           |
+| [`ssl-redirect-code`](#ssl-redirect)                 | http status code                        | Global   | `302`                            |
+| [`stats-auth`](#stats)                               | user:passwd                             | Global   | no auth                          |
+| [`stats-port`](#stats)                               | port number                             | Global   | `1936`                           |
+| [`stats-proxy-protocol`](#stats)                     | [true\|false]                           | Global   | `false`                          |
+| [`stats-ssl-cert`](#stats)                           | namespace/secret name                   | Global   | no ssl/plain http                |
+| [`strict-host`](#strict-host)                        | [true\|false]                           | Global   | `false`                          |
+| [`syslog-endpoint`](#syslog)                         | IP:port (udp)                           | Global   | do not log                       |
+| [`syslog-format`](#syslog)                           | rfc5424\|rfc3164                        | Global   | `rfc5424`                        |
+| [`syslog-length`](#syslog)                           | maximum length                          | Global   | `1024`                           |
+| [`syslog-tag`](#syslog)                              | syslog tag field string                 | Global   | `ingress`                        |
+| [`tcp-log-format`](#log-format)                      | ConfigMap based TCP log format          | Global   |                                  |
+| [`tcp-service-log-format`](#log-format)              | TCP service log format                  | TCP      | HAProxy default log format       |
+| [`tcp-service-port`](#tcp-services)                  | TCP service port number                 | TCP      |                                  |
+| [`tcp-service-proxy-protocol`](#proxy-protocol)      | [true\|false]                           | TCP      | `false`                          |
+| [`timeout-client`](#timeout)                         | time with suffix                        | Global   | `50s`                            |
+| [`timeout-client-fin`](#timeout)                     | time with suffix                        | Global   | `50s`                            |
+| [`timeout-connect`](#timeout)                        | time with suffix                        | Backend  | `5s`                             |
+| [`timeout-http-request`](#timeout)                   | time with suffix                        | Backend  | `5s`                             |
+| [`timeout-keep-alive`](#timeout)                     | time with suffix                        | Backend  | `1m`                             |
+| [`timeout-queue`](#timeout)                          | time with suffix                        | Backend  | `5s`                             |
+| [`timeout-server`](#timeout)                         | time with suffix                        | Backend  | `50s`                            |
+| [`timeout-server-fin`](#timeout)                     | time with suffix                        | Backend  | `50s`                            |
+| [`timeout-stop`](#timeout)                           | time with suffix                        | Global   | `10m`                            |
+| [`timeout-tunnel`](#timeout)                         | time with suffix                        | Backend  | `1h`                             |
+| [`tls-alpn`](#tls-alpn)                              | TLS ALPN advertisement                  | Host     | `h2,http/1.1`                    |
+| [`use-chroot`](#security)                            | [true\|false]                           | Global   | `false`                          |
+| [`use-cpu-map`](#cpu-map)                            | [true\|false]                           | Global   | `true`                           |
+| [`use-forwarded-proto`](#fronting-proxy-port)        | [true\|false]                           | Frontend | `true`                           |
+| [`use-haproxy-user`](#security)                      | [true\|false]                           | Global   | `false`                          |
+| [`use-htx`](#use-htx)                                | [true\|false]                           | Global   | `false`                          |
+| [`use-proxy-protocol`](#proxy-protocol)              | [true\|false]                           | Frontend | `false`                          |
+| [`use-resolver`](#dns-resolvers)                     | resolver name                           | Backend  |                                  |
+| [`username`](#security)                              | haproxy user name                       | Global   | `haproxy`                        |
+| [`var-namespace`](#var-namespace)                    | [true\|false]                           | Host     | `false`                          |
+| [`waf`](#waf)                                        | "modsecurity"                           | Path     |                                  |
+| [`waf-mode`](#waf)                                   | [deny\|detect]                          | Path     | `deny` (if waf is set)           |
+| [`whitelist-source-range`](#allowlist)               | Comma-separated IPs or CIDRs            | Path     |                                  |
+| [`worker-max-reloads`](#master-worker)               | number of reloads                       | Global   | `0`                              |
 
 ---
 
@@ -995,11 +988,11 @@ See also:
 
 ### Bind
 
-| Configuration key      | Scope    | Default | Since |
-|------------------------|----------|---------|-------|
-| `bind-fronting-proxy`  | `Global` |         | v0.8  |
-| `bind-http`            | `Global` |         | v0.8  |
-| `bind-https`           | `Global` |         | v0.8  |
+| Configuration key      | Scope      | Default | Since |
+|------------------------|------------|---------|-------|
+| `bind-fronting-proxy`  | `Frontend` |         | v0.8  |
+| `bind-http`            | `Frontend` |         | v0.8  |
+| `bind-https`           | `Frontend` |         | v0.8  |
 
 Configures listening IP and port for HTTP/s incoming requests. These
 configuration keys have backward compatibility with [Bind IP addr](#bind-ip-addr),
@@ -1017,15 +1010,11 @@ Configuration examples:
 * `bind-https: ":443,:8443"`: accept https connections on `443` and also `8443` port numbers
 
 {{< alert title="Note" >}}
-`bind-fronting-proxy` and `bind-http` can share the same port number, provided
-that the whole configuration key match, not only the port number.
-See [Fronting proxy](#fronting-proxy-port) doc.
+Since v0.17, `bind-fronting-proxy` and `bind-http` cannot share neither the same frontend nor the same TCP port anymore.
 {{< /alert >}}
 
 {{< alert title="Warning" color="warning" >}}
-Special care should be taken on port number overlap, nether haproxy itself nor
-haproxy-ingress will warn if the same port number is used in more than one
-configuration key.
+Special care should be taken on port number overlap, neither haproxy itself nor haproxy-ingress will warn if the same port number is used on more than one configuration key. Moreover, although it is possible to configure a binding address completely unrelated with the configured `http-port` or `https-port`, the suggestion is that both configurations match somehow.
 {{< /alert >}}
 
 See also:
@@ -1038,13 +1027,13 @@ See also:
 
 ### Bind IP addr
 
-| Configuration key         | Scope    | Default | Since |
-|---------------------------|----------|---------|-------|
-| `bind-ip-addr-healthz`    | `Global` |         |       |
-| `bind-ip-addr-http`       | `Global` |         |       |
-| `bind-ip-addr-prometheus` | `Global` |         | v0.10 |
-| `bind-ip-addr-stats`      | `Global` |         |       |
-| `bind-ip-addr-tcp`        | `Global` |         |       |
+| Configuration key         | Scope      | Default | Since |
+|---------------------------|------------|---------|-------|
+| `bind-ip-addr-healthz`    | `Global`   |         |       |
+| `bind-ip-addr-http`       | `Frontend` |         |       |
+| `bind-ip-addr-prometheus` | `Global`   |         | v0.10 |
+| `bind-ip-addr-stats`      | `Global`   |         |       |
+| `bind-ip-addr-tcp`        | `Global`   |         |       |
 
 Define listening IPv4/IPv6 address on public HAProxy frontends. Since v0.10 the default
 value changed from `*` to an empty string, which haproxy interprets in the same way and
@@ -1052,7 +1041,7 @@ binds on all IPv4 address.
 
 * `bind-ip-addr-tcp`: IP address of all ConfigMap based TCP services declared on [`tcp-services-configmap`]({{% relref "command-line#tcp-services-configmap" %}}) command-line option.
 * `bind-ip-addr-healthz`: IP address of the health check URL.
-* `bind-ip-addr-http`: IP address of all HTTP/s frontends, port `:80` and `:443`, and also [`fronting-proxy-port`](#fronting-proxy-port) if declared.
+* `bind-ip-addr-http`: IP address of HTTP/s frontends.
 * `bind-ip-addr-prometheus`: IP address of the haproxy's internal Prometheus exporter.
 * `bind-ip-addr-stats`: IP address of the statistics page. See also [`stats-port`](#stats).
 
@@ -1066,12 +1055,12 @@ See also:
 
 ### Bind port
 
-| Configuration key | Scope    | Default | Since |
-|-------------------|----------|---------|-------|
-| `healthz-port`    | `Global` | `10253` |       |
-| `http-port`       | `Global` | `80`    |       |
-| `https-port`      | `Global` | `443`   |       |
-| `prometheus-port` | `Global` |         | v0.10 |
+| Configuration key | Scope      | Default | Since |
+|-------------------|------------|---------|-------|
+| `healthz-port`    | `Global`   | `10253` |       |
+| `http-port`       | `Frontend` | `80`    |       |
+| `https-port`      | `Frontend` | `443`   |       |
+| `prometheus-port` | `Global`   |         | v0.10 |
 
 * `healthz-port`: Define the port number HAProxy should listen to in order to answer for health checking requests. Use `/healthz` as the request path.
 * `http-port`: Define the port number of unencrypted HTTP connections.
@@ -1663,51 +1652,27 @@ See also:
 
 ### Fronting proxy port
 
-| Configuration key     | Scope    | Default | Since   |
-|-----------------------|----------|---------|---------|
-| `fronting-proxy-port` | `Global` |         | `v0.8`  |
-| `https-to-http-port`  | `Global` |         |         |
-| `use-forwarded-proto` | `Global` | `true`  | `v0.10` |
-
-A port number to listen to http requests from a fronting proxy that does the ssl
-offload, eg haproxy ingress behind a cloud load balancers that manages the TLS
-certificates. `https-to-http-port` is an alias to `fronting-proxy-port`.
-
-`fronting-proxy-port` and [`http-port`](#bind-port) can share the same port number, see below
-what changes in the behaviour.
-
-`use-forwarded-proto` defines if haproxy should use `X-Forwarded-Proto` header to decide
-how to handle requests made to `fronting-proxy-port` port number.
-
-If `use-forwarded-proto` is `false`, the request takes the `https` route and is handled as if
-`X-Forwarded-Proto` header is `https`, see below. The actual header content is ignored by
-haproxy and forwarded to the backend if provided.
-
-If `use-forwarded-proto` is `true`, the default value, requests made to `fronting-proxy-port`
-port number evaluate the `X-Forwarded-Proto` header to decide how to handle the request:
-
-* If `X-Forwarded-Proto` header is `https`:
-  * HAProxy will handle the request just like the ssl-offload was made by HAProxy itself - HSTS header is provided if configured and
-`X-SSL-*` headers won't be changed or removed if provided.
-* If `X-Forwarded-Proto` header is `http` or any other value except `https`:
-  * HAProxy will redirect scheme to https
-* If `X-Forwarded-Proto` header is missing:
-  * If `fronting-proxy-port` has its own port --- HAProxy will redirect scheme to https
-  * If `fronting-proxy-port` shares the HTTP port --- the request will be handled as plain http, being redirected to https only if `ssl-redirect` is `true`, just like if `fronting-proxy-port` wasn't configured.
-
-{{< alert title="Warning on v0.7 and older" color="warning" >}}
-On v0.7 and older and only if the `X-Forwarded-Proto` is missing: the
-connecting port number was used to define which socket received the request, so
-the fronting proxy should connect to the same port number defined in
-`https-to-http-port`, eg cannot have any proxy like Kubernetes' `NodePort`
-between the load balancer and HAProxy which changes the connecting port number.
-This limitation doesn't exist on v0.8 or above.
+| Configuration key     | Scope      | Default | Since   |
+|-----------------------|------------|---------|---------|
+| `fronting-proxy-port` | `Frontend` |         | `v0.8`  |
+| `https-to-http-port`  | `Frontend` |         |         |
+| `use-forwarded-proto` | `Frontend` | `true`  | `v0.10` |
+
+Configures HAProxy Ingress to accept plain HTTP requests from a fronting load balancer doing the SSL offload.
+
+* `fronting-proxy-port`: configures the port number that should accept the HTTP requests. This configuration was allowed to collide with `http-port` up to v0.16, but since v0.17 fronting-proxy is a flag to the whole frontend. Configure distinct frontends to support both regular HTTP and Fronting Proxy requests on the same deployment, and the port number cannot collide anymore.
+* `use-forwarded-proto`: if `true`, the default value, configures HAProxy to redirect the request to https if the `X-Forwarded-Proto` header is not `https`. If `false`, `X-Forwarded-Proto` header is ignored and passed as is to the backend.
+* `https-to-http-port`: old and deprecated key, now an alias to `fronting-proxy-port`.
+
+HAProxy Ingress has a few differences on HTTP and HTTPS configurations, like, redirect from HTTP if `ssl-redirect` is `true`, add HSTS headers (when configured) only on HTTPS responses, and drop incoming `X-SSL-*` headers for security reasons. Configuring a fronting proxy port makes HAProxy Ingress to have HTTPS behavior over HTTP connection, allowing a fronting load balancer to SSL offload the TLS requests, talking plain HTTP with HAProxy.
+
+{{< alert title="Security warning" color="warning" >}}
+This option must only be used if the network from the fronting load balancer and the ingress nodes is trusted, since the communication happens on plain HTTP, and all the communication is visible via tools like tcpdump. Give also the configured port a special attention and block it from external access: an user can easily add the `X-SSL-*` headers, authenticating itself as any user on applications using mTLS.
 {{< /alert >}}
 
 See also:
 
 * [Bind](#bind)
-* [Bind port](#bind-port)
 
 ### Headers
 
@@ -2366,16 +2331,16 @@ See also:
 
 ### Proxy protocol
 
-| Configuration key            | Scope     | Default | Since |
-|------------------------------|-----------|---------|-------|
-| `proxy-protocol`             | `Backend` | `no`    |       |
-| `tcp-service-proxy-protocol` | `TCP`     | `false` | v0.13 |
-| `use-proxy-protocol`         | `Global`  | `false` |       |
+| Configuration key            | Scope      | Default | Since |
+|------------------------------|------------|---------|-------|
+| `proxy-protocol`             | `Backend`  | `no`    |       |
+| `tcp-service-proxy-protocol` | `TCP`      | `false` | v0.13 |
+| `use-proxy-protocol`         | `Frontend` | `false` |       |
 
 Configures PROXY protocol in frontends and backends.
 
 * `proxy-protocol`: Define if the upstream backends support proxy protocol and what version of the protocol should be used. Supported values are `v1`, `v2`, `v2-ssl`, `v2-ssl-cn` or `no`. The default behavior if not declared is that the protocol is not supported by the backends and should not be used.
-* `use-proxy-protocol`: Define if HTTP services are behind another proxy that uses the PROXY protocol. If `true`, HTTP ports which defaults to `80` and `443` will expect the PROXY protocol, version 1 or 2. The stats endpoint (defaults to port `1936`) has its own [`stats-proxy-protocol`](#stats) configuration key.
+* `use-proxy-protocol`: Define if HTTP services are behind another proxy that uses the PROXY protocol. If `true`, HTTP ports will expect the PROXY protocol, version 1 or 2. The stats endpoint (defaults to port `1936`) has its own [`stats-proxy-protocol`](#stats) configuration key.
 * `tcp-service-proxy-protocol`: Define if the TCP service is behind another proxy that uses the PROXY protocol. Configures as `"true"` if the proxy should expect requests using the PROXY protocol, version 1 or 2. The default value is `"false"`.
 
 See also:
@@ -2391,14 +2356,14 @@ See also:
 
 ### Redirect
 
-| Configuration key       | Scope    | Default                       | Since   |
-|-------------------------|----------|-------------------------------|---------|
-| `no-redirect-locations` | `Global` | `/.well-known/acme-challenge` | v0.14.3 |
-| `redirect-from`         | `Host`   |                               | v0.13   |
-| `redirect-from-code`    | `Global` | `302`                         | v0.13   |
-| `redirect-from-regex`   | `Host`   |                               | v0.13   |
-| `redirect-to`           | `Path`   |                               | v0.13   |
-| `redirect-to-code`      | `Global` | `302`                         | v0.13   |
+| Configuration key       | Scope      | Default                       | Since   |
+|-------------------------|------------|-------------------------------|---------|
+| `no-redirect-locations` | `Global`   | `/.well-known/acme-challenge` | v0.14.3 |
+| `redirect-from`         | `Host`     |                               | v0.13   |
+| `redirect-from-code`    | `Frontend` | `302`                         | v0.13   |
+| `redirect-from-regex`   | `Host`     |                               | v0.13   |
+| `redirect-to`           | `Path`     |                               | v0.13   |
+| `redirect-to-code`      | `Frontend` | `302`                         | v0.13   |
 
 Configures HTTP redirect. Redirect *from* matches source hostnames that should be redirected
 to the hostname declared in the ingress spec. Redirect *to* uses the hostname declared in the
@@ -2811,7 +2776,7 @@ Up to v0.12, `ssl-passthrough` supports only root `/` path. Since v0.13, non roo
 {{< /alert >}}
 
 * `ssl-passthrough`: Enable SSL passthrough if defined as `true`. The backend is then expected to SSL offload the incoming traffic. The default value is `false`, which means HAProxy should do the SSL handshake.
-* `ssl-passthrough-http-port`: Optional HTTP port number of the backend. If defined, connections to the HAProxy's HTTP port, defaults to `80`, is sent to the configured port number of the backend, which expects to speak plain HTTP. If not defined, connections to the HTTP port will redirect the client to HTTPS.
+* `ssl-passthrough-http-port`: Optional HTTP port number of the ssl-passthrough backend. If defined, connections to the HAProxy's HTTP port, defaults to `80`, is sent to the configured port number of the backend, which expects to speak plain HTTP. If not defined, connections to the HTTP port will redirect the client to HTTPS. Note that this configuration only applies to the root path, since any non root path under ssl-passthrough configuration is already configured under the plain HTTP frontend.
 
 Hostnames configured as `ssl-passthrough` configures HAProxy in the following way:
 
diff --git a/pkg/controller/config/config.go b/pkg/controller/config/config.go
index d7bfff06..c4fec1c1 100644
--- a/pkg/controller/config/config.go
+++ b/pkg/controller/config/config.go
@@ -213,10 +213,11 @@ func CreateWithConfig(ctx context.Context, restConfig *rest.Config, opt *Options
 		configLog.Info("watching for Gateway API resources - --watch-gateway is true")
 	}
 
-	var hasGatewayV1, hasGatewayB1, hasGatewayA2, hasTCPRouteA2 bool
+	var hasGatewayV1, hasGatewayB1, hasGatewayA2, hasTCPRouteA2, hasTLSRouteA2 bool
 	if opt.WatchGateway {
 		gwapis := []string{"gatewayclass", "gateway", "httproute"}
 		tcpapis := []string{"tcproute"}
+		tlsapis := []string{"tlsroute"}
 
 		gwV1 := configHasAPI(clientGateway.Discovery(), gatewayv1.GroupVersion, gwapis...)
 		if gwV1 {
@@ -243,10 +244,16 @@ func CreateWithConfig(ctx context.Context, restConfig *rest.Config, opt *Options
 			configLog.Info("found custom resource definition for TCPRoute API v1alpha2")
 		}
 
-		// TODO: cannot enable TCPRoute without Gateway and GatewayClass, but currently HTTPRoute
-		// discovery is coupled and its CRD should be installed as well, even if not used.
+		tlsA2 := configHasAPI(clientGateway.Discovery(), gatewayv1alpha2.GroupVersion, tlsapis...)
+		if tlsA2 {
+			configLog.Info("found custom resource definition for TLSRoute API v1alpha2")
+		}
+
+		// TODO: cannot enable TCPRoute or TLSRoute without Gateway and GatewayClass, but currently
+		// HTTPRoute discovery is coupled and its CRD should be installed as well, even if not used.
 		// We should use a distinct flag for HTTPRoute.
 		hasTCPRouteA2 = tcpA2 && gw
+		hasTLSRouteA2 = tlsA2 && gw
 	}
 
 	if opt.EnableEndpointSlicesAPI {
@@ -508,6 +515,7 @@ func CreateWithConfig(ctx context.Context, restConfig *rest.Config, opt *Options
 		HasGatewayB1:             hasGatewayB1,
 		HasGatewayV1:             hasGatewayV1,
 		HasTCPRouteA2:            hasTCPRouteA2,
+		HasTLSRouteA2:            hasTLSRouteA2,
 		HealthzAddr:              healthz,
 		HealthzURL:               opt.HealthzURL,
 		IngressClass:             opt.IngressClass,
@@ -764,6 +772,7 @@ type Config struct {
 	HasGatewayB1             bool
 	HasGatewayV1             bool
 	HasTCPRouteA2            bool
+	HasTLSRouteA2            bool
 	HealthzAddr              string
 	HealthzURL               string
 	IngressClass             string
diff --git a/pkg/controller/services/cache.go b/pkg/controller/services/cache.go
index 966a7395..4e8882f3 100644
--- a/pkg/controller/services/cache.go
+++ b/pkg/controller/services/cache.go
@@ -73,6 +73,7 @@ var errGatewayA2Disabled = fmt.Errorf("gateway API v1alpha2 wasn't initialized")
 var errGatewayB1Disabled = fmt.Errorf("gateway API v1beta1 wasn't initialized")
 var errGatewayV1Disabled = fmt.Errorf("gateway API v1 wasn't initialized")
 var errTCPRouteA2Disabled = fmt.Errorf("TCPRoute API v1alpha2 wasn't initialized")
+var errTLSRouteA2Disabled = fmt.Errorf("TLSRoute API v1alpha2 wasn't initialized")
 
 func (c *c) get(key string, obj client.Object) error {
 	ns, n, err := cache.SplitMetaNamespaceKey(key)
@@ -402,6 +403,22 @@ func (c *c) GetTCPRouteList() ([]*gatewayv1alpha2.TCPRoute, error) {
 	return rlist, nil
 }
 
+func (c *c) GetTLSRouteList() ([]*gatewayv1alpha2.TLSRoute, error) {
+	if !c.config.HasTLSRouteA2 {
+		return nil, errTLSRouteA2Disabled
+	}
+	list := gatewayv1alpha2.TLSRouteList{}
+	err := c.client.List(c.ctx, &list)
+	if err != nil {
+		return nil, err
+	}
+	rlist := make([]*gatewayv1alpha2.TLSRoute, len(list.Items))
+	for i := range list.Items {
+		rlist[i] = &list.Items[i]
+	}
+	return rlist, nil
+}
+
 func (c *c) GetService(defaultNamespace, serviceName string) (*api.Service, error) {
 	namespace, name, err := buildResourceName(defaultNamespace, "service", serviceName, c.dynconfig.CrossNamespaceServices)
 	if err != nil {
diff --git a/pkg/controller/services/services.go b/pkg/controller/services/services.go
index d15f0740..13c42689 100644
--- a/pkg/controller/services/services.go
+++ b/pkg/controller/services/services.go
@@ -168,6 +168,7 @@ func (s *Services) setup(ctx context.Context) error {
 		HasGatewayB1:     cfg.HasGatewayB1,
 		HasGatewayV1:     cfg.HasGatewayV1,
 		HasTCPRouteA2:    cfg.HasTCPRouteA2,
+		HasTLSRouteA2:    cfg.HasTLSRouteA2,
 	}
 	instance := haproxy.CreateInstance(s.legacylogger.new("haproxy"), instanceOptions)
 	if err := instance.ParseTemplates(); err != nil {
diff --git a/pkg/converters/gateway/gateway.go b/pkg/converters/gateway/gateway.go
index 02330034..ca4cfb02 100644
--- a/pkg/converters/gateway/gateway.go
+++ b/pkg/converters/gateway/gateway.go
@@ -19,6 +19,7 @@ package gateway
 import (
 	"fmt"
 	"reflect"
+	"slices"
 	"sort"
 	"strconv"
 
@@ -81,7 +82,11 @@ func (c *converter) Sync(full bool, gwtyp client.Object) {
 		return
 	}
 
+	// we're not testing TLSRoute hostname declaration collision on HTTPRoute,
+	// so a validation should be added in the case the order changes and
+	// syncTLSRoutes() come first.
 	c.syncHTTPRoutes(gwtyp)
+	c.syncTLSRoutes(gwtyp)
 	c.syncTCPRoutes(gwtyp)
 }
 
@@ -105,8 +110,8 @@ func (c *converter) syncHTTPRoutes(gwtyp client.Object) {
 
 	sortHTTPRoutes(httpRoutesSource)
 	for _, httpRoute := range httpRoutesSource {
-		c.syncRoute(&httpRoute.source, httpRoute.spec.ParentRefs, gwtyp, func(gatewaySource *gatewaySource, sectionName *gatewayv1.SectionName) error {
-			return c.syncHTTPRouteGateway(httpRoute, gatewaySource, sectionName)
+		c.syncRoute(&httpRoute.source, httpRoute.spec.ParentRefs, gwtyp, func(gatewaySource *gatewaySource, sectionName *gatewayv1.SectionName) {
+			c.syncHTTPRouteGateway(httpRoute, gatewaySource, sectionName)
 		})
 	}
 }
@@ -147,6 +152,27 @@ func (c *converter) getHTTPRoutesSource() ([]*httpRouteSource, error) {
 	return httpRoutesSource, nil
 }
 
+func (c *converter) syncTLSRoutes(gwtyp client.Object) {
+	if !c.options.HasTLSRouteA2 {
+		return
+	}
+	tlsRoutes, err := c.cache.GetTLSRouteList()
+	if err != nil {
+		c.logger.Warn("error reading tlsRoute list: %v", err)
+		return
+	}
+	tlsRoutesSource := make([]*tlsRouteSource, len(tlsRoutes))
+	for i := range tlsRoutes {
+		tlsRoutesSource[i] = newTLSRouteSource(tlsRoutes[i], &tlsRoutes[i].Spec)
+	}
+	sortTLSRoutes(tlsRoutesSource)
+	for _, tlsRoute := range tlsRoutesSource {
+		c.syncRoute(&tlsRoute.source, tlsRoute.spec.ParentRefs, gwtyp, func(gatewaySource *gatewaySource, sectionName *gatewayv1.SectionName) {
+			c.syncTLSRouteGateway(tlsRoute, gatewaySource, sectionName)
+		})
+	}
+}
+
 func (c *converter) syncTCPRoutes(gwtyp client.Object) {
 	if !c.options.HasTCPRouteA2 {
 		return
@@ -162,8 +188,8 @@ func (c *converter) syncTCPRoutes(gwtyp client.Object) {
 	}
 	sortTCPRoutes(tcpRoutesSource)
 	for _, tcpRoute := range tcpRoutesSource {
-		c.syncRoute(&tcpRoute.source, tcpRoute.spec.ParentRefs, gwtyp, func(gatewaySource *gatewaySource, sectionName *gatewayv1.SectionName) error {
-			return c.syncTCPRouteGateway(tcpRoute, gatewaySource, sectionName)
+		c.syncRoute(&tcpRoute.source, tcpRoute.spec.ParentRefs, gwtyp, func(gatewaySource *gatewaySource, sectionName *gatewayv1.SectionName) {
+			c.syncTCPRouteGateway(tcpRoute, gatewaySource, sectionName)
 		})
 	}
 }
@@ -179,6 +205,17 @@ func sortHTTPRoutes(httpRoutesSource []*httpRouteSource) {
 	})
 }
 
+func sortTLSRoutes(tlsRoutesSource []*tlsRouteSource) {
+	sort.Slice(tlsRoutesSource, func(i, j int) bool {
+		r1 := tlsRoutesSource[i].obj
+		r2 := tlsRoutesSource[j].obj
+		if r1.GetCreationTimestamp() != r2.GetCreationTimestamp() {
+			return r1.GetCreationTimestamp().Time.Before(r2.GetCreationTimestamp().Time)
+		}
+		return r1.GetNamespace()+"/"+r1.GetName() < r2.GetNamespace()+"/"+r2.GetName()
+	})
+}
+
 func sortTCPRoutes(tcpRoutesSource []*tcpRouteSource) {
 	sort.Slice(tcpRoutesSource, func(i, j int) bool {
 		r1 := tcpRoutesSource[i].obj
@@ -201,6 +238,11 @@ type httpRouteSource struct {
 	spec *gatewayv1.HTTPRouteSpec
 }
 
+type tlsRouteSource struct {
+	source
+	spec *gatewayv1alpha2.TLSRouteSpec
+}
+
 type tcpRouteSource struct {
 	source
 	spec *gatewayv1alpha2.TCPRouteSpec
@@ -231,6 +273,13 @@ func newHTTPRouteSource(obj client.Object, spec *gatewayv1.HTTPRouteSpec) *httpR
 	}
 }
 
+func newTLSRouteSource(obj client.Object, spec *gatewayv1alpha2.TLSRouteSpec) *tlsRouteSource {
+	return &tlsRouteSource{
+		spec:   spec,
+		source: newSource(obj),
+	}
+}
+
 func newTCPRouteSource(obj client.Object, spec *gatewayv1alpha2.TCPRouteSpec) *tcpRouteSource {
 	return &tcpRouteSource{
 		spec:   spec,
@@ -275,7 +324,7 @@ var (
 	gatewayKind  = gatewayv1.Kind("Gateway")
 )
 
-func (c *converter) syncRoute(routeSource *source, parentRefs []gatewayv1.ParentReference, gwtyp client.Object, syncGateway func(gatewaySource *gatewaySource, sectionName *gatewayv1.SectionName) error) {
+func (c *converter) syncRoute(routeSource *source, parentRefs []gatewayv1.ParentReference, gwtyp client.Object, syncGateway func(gatewaySource *gatewaySource, sectionName *gatewayv1.SectionName)) {
 	for _, parentRef := range parentRefs {
 		parentGroup := gatewayGroup
 		parentKind := gatewayKind
@@ -299,18 +348,18 @@ func (c *converter) syncRoute(routeSource *source, parentRefs []gatewayv1.Parent
 			continue
 		}
 		// TODO implement gateway.Spec.Addresses
-		err := syncGateway(gatewaySource, parentRef.SectionName)
-		if err != nil {
-			c.logger.Warn("cannot attach %s to %s: %s", routeSource, gatewaySource, err)
-		}
+		syncGateway(gatewaySource, parentRef.SectionName)
 	}
 }
 
-func (c *converter) syncHTTPRouteGateway(httpRouteSource *httpRouteSource, gatewaySource *gatewaySource, sectionName *gatewayv1.SectionName) error {
+func (c *converter) syncHTTPRouteGateway(httpRouteSource *httpRouteSource, gatewaySource *gatewaySource, sectionName *gatewayv1.SectionName) {
 	for _, listener := range gatewaySource.spec.Listeners {
 		if sectionName != nil && *sectionName != listener.Name {
 			continue
 		}
+		if !c.checkProtocol(gatewaySource, &httpRouteSource.source, listener, gatewayv1.HTTPProtocolType, gatewayv1.HTTPSProtocolType) {
+			continue
+		}
 		if err := c.checkListenerAllowed(gatewaySource, &httpRouteSource.source, &listener); err != nil {
 			c.logger.Warn("skipping attachment of %s to %s listener '%s': %s",
 				httpRouteSource, gatewaySource, listener.Name, err)
@@ -320,31 +369,56 @@ func (c *converter) syncHTTPRouteGateway(httpRouteSource *httpRouteSource, gatew
 			// TODO implement rule.Filters
 			backendRefs := make([]gatewayv1.BackendRef, len(rule.BackendRefs))
 			for i := range rule.BackendRefs {
+				// TODO implement HTTPBackendRef.Filters
 				backendRefs[i] = rule.BackendRefs[i].BackendRef
 			}
-			backend, services := c.createBackend(&httpRouteSource.source, fmt.Sprintf("_rule%d", index), backendRefs)
+			backend, services := c.createBackend(&httpRouteSource.source, fmt.Sprintf("_rule%d", index), false, backendRefs)
 			if backend != nil {
-				passthrough := listener.TLS != nil && listener.TLS.Mode != nil && *listener.TLS.Mode == gatewayv1.TLSModePassthrough
-				if passthrough {
-					backend.ModeTCP = true
-				}
 				hostnames := c.filterHostnames(listener.Hostname, httpRouteSource.spec.Hostnames)
-				hosts, pathLinks := c.createHTTPHosts(&httpRouteSource.source, hostnames, rule.Matches, backend)
-				c.applyCertRef(gatewaySource, &listener, hosts)
+				pathLinks := c.createHTTPHosts(gatewaySource, &httpRouteSource.source, &listener, hostnames, rule.Matches, backend)
 				if c.ann != nil {
 					c.ann.ReadAnnotations(backend, services, pathLinks)
 				}
 			}
 		}
 	}
-	return nil
 }
 
-func (c *converter) syncTCPRouteGateway(tcpRouteSource *tcpRouteSource, gatewaySource *gatewaySource, sectionName *gatewayv1.SectionName) error {
+func (c *converter) syncTLSRouteGateway(tlsRouteSource *tlsRouteSource, gatewaySource *gatewaySource, sectionName *gatewayv1.SectionName) {
 	for _, listener := range gatewaySource.spec.Listeners {
 		if sectionName != nil && *sectionName != listener.Name {
 			continue
 		}
+		if !c.checkProtocol(gatewaySource, &tlsRouteSource.source, listener, gatewayv1.TLSProtocolType) {
+			continue
+		}
+		if err := c.checkListenerAllowed(gatewaySource, &tlsRouteSource.source, &listener); err != nil {
+			c.logger.Warn("skipping attachment of %s to %s listener '%s': %s",
+				tlsRouteSource, gatewaySource, listener.Name, err)
+			continue
+		}
+		for index, rule := range tlsRouteSource.spec.Rules {
+			// TODO implement rule.Filters
+			backend, services := c.createBackend(&tlsRouteSource.source, fmt.Sprintf("_tlsrule%d", index), true, rule.BackendRefs)
+			if backend != nil {
+				hostnames := c.filterHostnames(listener.Hostname, tlsRouteSource.spec.Hostnames)
+				pathLinks := c.createTLSHosts(gatewaySource, tlsRouteSource, &listener, hostnames, backend)
+				if c.ann != nil {
+					c.ann.ReadAnnotations(backend, services, pathLinks)
+				}
+			}
+		}
+	}
+}
+
+func (c *converter) syncTCPRouteGateway(tcpRouteSource *tcpRouteSource, gatewaySource *gatewaySource, sectionName *gatewayv1.SectionName) {
+	for _, listener := range gatewaySource.spec.Listeners {
+		if sectionName != nil && *sectionName != listener.Name {
+			continue
+		}
+		if !c.checkProtocol(gatewaySource, &tcpRouteSource.source, listener, gatewayv1.TCPProtocolType) {
+			continue
+		}
 		if err := c.checkListenerAllowed(gatewaySource, &tcpRouteSource.source, &listener); err != nil {
 			c.logger.Warn("skipping attachment of %s to %s listener '%s': %s",
 				tcpRouteSource, gatewaySource, listener.Name, err)
@@ -352,16 +426,34 @@ func (c *converter) syncTCPRouteGateway(tcpRouteSource *tcpRouteSource, gatewayS
 		}
 		for index, rule := range tcpRouteSource.spec.Rules {
 			// TODO implement rule.Filters
-			backend, services := c.createBackend(&tcpRouteSource.source, fmt.Sprintf("_tcprule%d", index), rule.BackendRefs)
+			backend, services := c.createBackend(&tcpRouteSource.source, fmt.Sprintf("_tcprule%d", index), true, rule.BackendRefs)
 			if backend != nil {
-				pathLinks := c.createTCPService(listener.Port, backend)
+				pathLinks := c.createTCPService(gatewaySource, &listener, nil, backend)
 				if c.ann != nil {
 					c.ann.ReadAnnotations(backend, services, pathLinks)
 				}
 			}
 		}
 	}
-	return nil
+}
+
+func (c *converter) checkProtocol(gatewaySource *gatewaySource, source *source, listener gatewayv1.Listener, proto ...gatewayv1.ProtocolType) bool {
+	if listener.Protocol == "" {
+		c.logger.Warn("missing protocol on %v listener '%s' for %v", gatewaySource, listener.Name, source)
+		return false
+	}
+	if !slices.Contains(proto, listener.Protocol) {
+		c.logger.Warn("invalid protocol on %v listener '%s' for %v: '%s'", gatewaySource, listener.Name, source, listener.Protocol)
+		return false
+	}
+	switch listener.Protocol {
+	case gatewayv1.HTTPSProtocolType, gatewayv1.TLSProtocolType:
+		if listener.TLS == nil {
+			c.logger.Warn("protocol '%s' on %v listener '%s' is missing TLS configuration", listener.Protocol, gatewaySource, listener.Name)
+			return false
+		}
+	}
+	return true
 }
 
 var errRouteNotAllowed = fmt.Errorf("listener does not allow the route")
@@ -420,8 +512,20 @@ func (c *converter) checkListenerAllowedNamespace(gatewaySource *gatewaySource,
 	return errRouteNotAllowed
 }
 
-func (c *converter) createBackend(routeSource *source, index string, backendRefs []gatewayv1.BackendRef) (*hatypes.Backend, []*api.Service) {
+func (c *converter) filterHostnames(listenerHostname *gatewayv1.Hostname, routeHostnames []gatewayv1.Hostname) []gatewayv1.Hostname {
+	if listenerHostname == nil || *listenerHostname == "" || *listenerHostname == "*" {
+		if len(routeHostnames) == 0 {
+			return []gatewayv1.Hostname{"*"}
+		}
+		return routeHostnames
+	}
+	// TODO implement proper filter to wildcard based listenerHostnames -- `*.domain.local`
+	return []gatewayv1.Hostname{*listenerHostname}
+}
+
+func (c *converter) createBackend(routeSource *source, index string, modeTCP bool, backendRefs []gatewayv1.BackendRef) (*hatypes.Backend, []*api.Service) {
 	if habackend := c.haproxy.Backends().FindBackend(routeSource.namespace, routeSource.name, index); habackend != nil {
+		habackend.ModeTCP = modeTCP
 		return habackend, nil
 	}
 	type backend struct {
@@ -482,6 +586,7 @@ func (c *converter) createBackend(routeSource *source, index string, backendRefs
 		return nil, nil
 	}
 	habackend := c.haproxy.Backends().AcquireBackend(routeSource.namespace, routeSource.name, index)
+	habackend.ModeTCP = modeTCP
 	cl := make([]*convutils.WeightCluster, len(backends))
 	for i := range backends {
 		cl[i] = &backends[i].cl
@@ -496,14 +601,15 @@ func (c *converter) createBackend(routeSource *source, index string, backendRefs
 	return habackend, svclist
 }
 
-func (c *converter) createHTTPHosts(routeSource *source, hostnames []gatewayv1.Hostname, matches []gatewayv1.HTTPRouteMatch, backend *hatypes.Backend) (hosts []*hatypes.Host, pathLinks []*hatypes.PathLink) {
-	if backend.ModeTCP && len(matches) > 0 {
-		c.logger.Warn("ignoring match from %s: backend is TCP or SSL Passthrough", routeSource)
-		matches = nil
-	}
+func (c *converter) createHTTPHosts(gatewaySource *gatewaySource, routeSource *source, listener *gatewayv1.Listener, hostnames []gatewayv1.Hostname, matches []gatewayv1.HTTPRouteMatch, backend *hatypes.Backend) (pathLinks []*hatypes.PathLink) {
 	if len(matches) == 0 {
 		matches = []gatewayv1.HTTPRouteMatch{{}}
 	}
+	var hostsTLS map[string]*hatypes.TLSConfig
+	if listener.TLS != nil {
+		hostsTLS = make(map[string]*hatypes.TLSConfig)
+	}
+	frontend := c.haproxy.Frontends().AcquireFrontend(int32(listener.Port), listener.Protocol == gatewayv1.HTTPSProtocolType)
 	for _, match := range matches {
 		var path string
 		var haMatch hatypes.MatchType
@@ -533,8 +639,8 @@ func (c *converter) createHTTPHosts(routeSource *source, hostnames []gatewayv1.H
 			if hstr == "" || hstr == "*" {
 				hstr = hatypes.DefaultHost
 			}
-			h := c.haproxy.Hosts().AcquireHost(hstr)
-			pathlink := hatypes.CreateHostPathLink(hstr, path, haMatch)
+			h := frontend.AcquireHost(hstr)
+			pathlink := hatypes.CreatePathLink(path, haMatch).WithHTTPHost(h)
 			var haheaders hatypes.HTTPHeaderMatch
 			for _, header := range match.Headers {
 				haheaders = append(haheaders, hatypes.HTTPMatch{
@@ -545,149 +651,145 @@ func (c *converter) createHTTPHosts(routeSource *source, hostnames []gatewayv1.H
 			}
 			pathlink.WithHeadersMatch(haheaders)
 			if h.FindPathWithLink(pathlink) != nil {
-				if backend.ModeTCP && h.SSLPassthrough() {
-					c.logger.Warn("skipping redeclared ssl-passthrough root path on %s", routeSource)
-					continue
-				}
-				if !backend.ModeTCP && !h.SSLPassthrough() {
-					c.logger.Warn("skipping redeclared path '%s' type '%s' on %s", path, haMatch, routeSource)
-					continue
-				}
+				c.logger.Warn("skipping redeclared path '%s' type '%s' on %s", path, haMatch, routeSource)
+				continue
 			}
 			c.tracker.TrackRefName([]convtypes.TrackingRef{
 				{Context: convtypes.ResourceHAHostname, UniqueName: h.Hostname},
 			}, convtypes.ResourceGateway, "gw")
-			h.TLS.UseDefaultCrt = false
 			h.AddLink(backend, pathlink)
-			c.handlePassthrough(path, h, backend, routeSource)
-			hosts = append(hosts, h)
 			pathLinks = append(pathLinks, pathlink)
+			if hostsTLS != nil {
+				hostsTLS[h.Hostname] = &h.TLS.TLSConfig
+			}
 		}
 		// TODO implement match.ExtensionRef
 	}
-	return hosts, pathLinks
-}
-
-func (c *converter) createTCPService(port gatewayv1.PortNumber, backend *hatypes.Backend) []*hatypes.PathLink {
-	// TODO: this mimics the format currently expected by TCPService,
-	// implemented by ingress as well; need a refactor, there's already
-	// a few TODOs in ingress converter and TCPService implementations.
-	hostname := fmt.Sprintf("%s:%d", hatypes.DefaultHost, port)
-	backend.ModeTCP = true
-	_, tcphost := c.haproxy.TCPServices().AcquireTCPService(hostname)
-	if !tcphost.Backend.IsEmpty() {
-		c.logger.Warn("skipping redeclared TCPService '%s'", hostname)
-		return nil
+	if hostsTLS != nil {
+		err := c.readCertRefs(gatewaySource, listener, hostsTLS)
+		if err != nil {
+			// avoid partial (i.e. broken) configuration by reverting all the added paths in the case of an error
+			frontend.RemoveAllLinks(pathLinks...)
+			c.logger.Warn("skipping certificate reference on %s listener '%s': %v", gatewaySource, listener.Name, err)
+			return nil
+		}
 	}
-	c.tracker.TrackNames(convtypes.ResourceHAHostname, hostname, convtypes.ResourceGateway, "gw")
-	tcphost.Backend = backend.BackendID()
-	pathLink := hatypes.CreateHostPathLink(hostname, "/", hatypes.MatchExact)
-	return []*hatypes.PathLink{pathLink}
+	return pathLinks
 }
 
-func (c *converter) handlePassthrough(path string, h *hatypes.Host, b *hatypes.Backend, routeSource *source) {
-	// Special handling for TLS passthrough due to current haproxy.Host limitation
-	// TODO: haproxy.Host refactor, allowing to remove this whole func
-	if path != "/" || (!b.ModeTCP && !h.SSLPassthrough()) {
-		// only matter if root path
-		// we also don't care if both present (b.ModeTCP) and past
-		// (h.SSLPassthrough()) passthrough isn't/wasn't configured
-		return
+func (c *converter) createTLSHosts(gatewaySource *gatewaySource, routeSource *tlsRouteSource, listener *gatewayv1.Listener, hostnames []gatewayv1.Hostname, backend *hatypes.Backend) []*hatypes.PathLink {
+	if mode := listener.TLS.Mode; mode == nil || *mode == gatewayv1.TLSModeTerminate {
+		// ssl-offload on haproxy, backend is a plain TCP service
+		return c.createTCPService(gatewaySource, listener, hostnames, backend)
 	}
-	for _, hpath := range h.FindPath("/") {
-		backend := c.haproxy.Backends().FindBackend(hpath.Backend.Namespace, hpath.Backend.Name, hpath.Backend.Port)
-		if backend != nil && !backend.ModeTCP {
-			// current path has a HTTP backend in the root path of a passthrough
-			// domain, and the current haproxy.Host implementation uses this as the
-			// target HTTPS backend. So we need to:
-			//
-			// 1. copy the backend ID to b.HTTPPassthroughBackend if not configured
-			if h.HTTPPassthroughBackend == "" {
-				if b.ModeTCP {
-					h.HTTPPassthroughBackend = hpath.Backend.ID
-				} else {
-					h.HTTPPassthroughBackend = b.ID
-				}
-			} else {
-				c.logger.Warn("skipping redeclared http root path on %s", routeSource)
-			}
-			// and
-			// 2. remove it from the target HTTPS configuration
-			h.RemovePath(hpath)
+	// ssl-passthrough, backend is TLS, just SNI inspection on haproxy without ssl-offload
+	f := c.haproxy.Frontends().AcquireFrontend(int32(listener.Port), true)
+	for _, hostname := range hostnames {
+		h := f.FindHost(string(hostname))
+		if h != nil && !h.SSLPassthrough {
+			c.logger.Warn("skipping hostname '%s' on %s: hostname already declared as HTTP", hostname, routeSource)
+			continue
 		}
-	}
-}
-
-func (c *converter) filterHostnames(listenerHostname *gatewayv1.Hostname, routeHostnames []gatewayv1.Hostname) []gatewayv1.Hostname {
-	if listenerHostname == nil || *listenerHostname == "" || *listenerHostname == "*" {
-		if len(routeHostnames) == 0 {
-			return []gatewayv1.Hostname{"*"}
+		if h == nil {
+			h = f.AcquireHost(string(hostname))
+			h.SSLPassthrough = true
 		}
-		return routeHostnames
+		link := hatypes.CreatePathLink("/", hatypes.MatchPrefix).WithHTTPHost(h)
+		if h.FindPathWithLink(link) != nil {
+			c.logger.Warn("skipping redeclared ssl-passthrough hostname '%s' on %s", hostname, routeSource)
+			continue
+		}
+		c.tracker.TrackNames(convtypes.ResourceHAHostname, string(hostname), convtypes.ResourceGateway, "gw")
+		h.AddLink(backend, link) // TODO missing a better abstraction for ssl-passthrough handling
 	}
-	// TODO implement proper filter to wildcard based listenerHostnames -- `*.domain.local`
-	return []gatewayv1.Hostname{*listenerHostname}
+	return nil
 }
 
-func (c *converter) applyCertRef(source *gatewaySource, listener *gatewayv1.Listener, hosts []*hatypes.Host) {
-	if listener.TLS == nil {
-		return
+func (c *converter) createTCPService(gatewaySource *gatewaySource, listener *gatewayv1.Listener, hostnames []gatewayv1.Hostname, backend *hatypes.Backend) []*hatypes.PathLink {
+	tcpport := c.haproxy.TCPServices().AcquireTCPPort(int(listener.Port))
+	if len(hostnames) == 0 || listener.TLS == nil {
+		// defaults to wildcard (len==0); overwrite hostnames if non TLS (TLS==nil)
+		hostnames = []gatewayv1.Hostname{"*"}
 	}
-	if listener.TLS.Mode != nil && *listener.TLS.Mode == gatewayv1.TLSModePassthrough {
-		for _, host := range hosts {
-			// backend was already changed to ModeTCP; hosts.match was already
-			// changed to root path only and a warning was already logged if needed
-			host.SetSSLPassthrough(true)
+	var hostsTLS map[string]*hatypes.TLSConfig
+	if listener.TLS != nil {
+		hostsTLS = make(map[string]*hatypes.TLSConfig)
+	}
+	var pathlinks []*hatypes.PathLink
+	for _, hostname := range hostnames {
+		if hostname == "" || hostname == "*" {
+			hostname = hatypes.DefaultHost
+		}
+		tcphost := tcpport.AcquireTLSHost(string(hostname))
+		if !tcphost.Backend.IsEmpty() {
+			c.logger.Warn("skipping redeclared TCPService '%s'", hostname)
+			continue
+		}
+		c.tracker.TrackNames(convtypes.ResourceHAHostname, string(hostname), convtypes.ResourceGateway, "gw")
+		tcphost.Backend = backend.BackendID()
+		pathlinks = append(pathlinks, hatypes.CreatePathLink("/", hatypes.MatchExact).WithTCPHost(tcphost))
+		if hostsTLS != nil {
+			tls := &hatypes.TCPServiceTLSConfig{}
+			tcpport.TLS[string(hostname)] = tls
+			hostsTLS[string(hostname)] = &tls.TLSConfig
 		}
-		return
 	}
+	if hostsTLS != nil {
+		err := c.readCertRefs(gatewaySource, listener, hostsTLS)
+		if err != nil {
+			// avoid partial (i.e. broken) configuration by reverting all the added services in the case of an error
+			c.haproxy.TCPServices().RemoveAllLinks(pathlinks...)
+			c.logger.Warn("skipping certificate reference on %s listener '%s': %v", gatewaySource, listener.Name, err)
+			return nil
+		}
+	}
+	return pathlinks
+}
+
+// readCertRefs updates all the TLSConfig references on the hostsTLS hashmap with certificates provided via
+// listener.TLS.CertificateRefs. No further action is needed, provided that the hashmap is populated with the
+// reference to the real TLSConfig.
+//
+// Special handling of the added hosts or services should be done in the case of an error: the caller should
+// revert all the changes, otherwise haproxy would lead to an incomplete/invalid configuration due to the
+// missing of some TLS certificates.
+func (c *converter) readCertRefs(source *gatewaySource, listener *gatewayv1.Listener, hostsTLS map[string]*hatypes.TLSConfig) error {
 	certRefs := listener.TLS.CertificateRefs
 	if len(certRefs) == 0 {
-		c.logger.Warn("skipping certificate reference on %s listener '%s': listener has no certificate reference",
-			source, listener.Name)
-		return
+		return fmt.Errorf("listener has no certificate reference")
 	}
 	var defaultCrtFile *convtypes.CrtFile
-	// iterate over all certRefs
 	for i := range certRefs {
 		certRef := certRefs[i]
 		crtFile, err := c.readCertRef(source.namespace, &certRef)
 		if err != nil {
-			c.logger.Warn("skipping certificate reference on %s listener '%s': %s",
-				source, listener.Name, err)
-			return
+			return err
 		}
 		if defaultCrtFile == nil {
 			// first certificate, use later on hosts with missing ones
 			defaultCrtFile = &crtFile
 		}
-		for _, host := range hosts {
-			if crtFile.Certificate.VerifyHostname(host.Hostname) == nil {
-				if host.TLS.TLSHash != "" && host.TLS.TLSHash != crtFile.SHA1Hash {
+		for hostname, hostTLS := range hostsTLS {
+			if crtFile.Certificate.VerifyHostname(hostname) == nil {
+				if hostTLS.TLSHash != "" && hostTLS.TLSHash != crtFile.SHA1Hash {
 					c.logger.Warn("skipping certificate reference on %s listener '%s' for hostname '%s': a TLS certificate was already assigned",
-						source, listener.Name, host.Hostname)
+						source, listener.Name, hostname)
 					continue
 				}
-				host.TLS.TLSCommonName = crtFile.Certificate.Subject.CommonName
-				host.TLS.TLSFilename = crtFile.Filename
-				host.TLS.TLSHash = crtFile.SHA1Hash
+				hostTLS.TLSCommonName = crtFile.Certificate.Subject.CommonName
+				hostTLS.TLSFilename = crtFile.Filename
+				hostTLS.TLSHash = crtFile.SHA1Hash
 			}
 		}
 	}
-	if defaultCrtFile == nil {
-		c.logger.Warn("skipping certificate reference on %s listener '%s': listener has no valid certificate reference",
-			source, listener.Name)
-		return
-	}
-	// Use first valid certificate to fix all missing hosts
-	for _, host := range hosts {
-		if host.TLS.TLSHash != "" {
-			continue
+	for _, hostTLS := range hostsTLS {
+		if hostTLS.TLSHash == "" {
+			hostTLS.TLSCommonName = defaultCrtFile.Certificate.Subject.CommonName
+			hostTLS.TLSFilename = defaultCrtFile.Filename
+			hostTLS.TLSHash = defaultCrtFile.SHA1Hash
 		}
-		host.TLS.TLSCommonName = defaultCrtFile.Certificate.Subject.CommonName
-		host.TLS.TLSFilename = defaultCrtFile.Filename
-		host.TLS.TLSHash = defaultCrtFile.SHA1Hash
 	}
+	return nil
 }
 
 func (c *converter) readCertRef(namespace string, certRef *gatewayv1.SecretObjectReference) (crtFile convtypes.CrtFile, err error) {
diff --git a/pkg/converters/ingress/annotations/backend.go b/pkg/converters/ingress/annotations/backend.go
index 992c5a4b..93655803 100644
--- a/pkg/converters/ingress/annotations/backend.go
+++ b/pkg/converters/ingress/annotations/backend.go
@@ -207,12 +207,13 @@ func (c *updater) setAuthExternal(config ConfigValueGetter, auth *hatypes.AuthEx
 		return
 	}
 	// TODO track
-	authBackendName, err := c.haproxy.Frontend().AcquireAuthBackendName(backend.BackendID())
+	proxy := &c.haproxy.Frontends().AuthProxy
+	authBackendName, err := proxy.AcquireAuthBackendName(backend.BackendID())
 	if err != nil {
 		// clean up and try again
 		used := c.haproxy.Backends().BuildUsedAuthBackends()
-		c.haproxy.Frontend().RemoveAuthBackendExcept(used)
-		authBackendName, err = c.haproxy.Frontend().AcquireAuthBackendName(backend.BackendID())
+		proxy.RemoveAuthBackendExcept(used)
+		authBackendName, err = proxy.AcquireAuthBackendName(backend.BackendID())
 		if err != nil {
 			// TODO remove backend if not used elsewhere
 			c.logger.Warn("ignoring auth URL on %s: %v", url.Source.String(), err)
@@ -279,7 +280,7 @@ func (c *updater) buildBackendAuthExternal(d *backData) {
 		isBackend := config.Get(ingtypes.BackAuthExternalPlacement).ToLower() == "backend"
 		url := config.Get(ingtypes.BackAuthURL)
 		if isBackend && url.Value != "" {
-			c.setAuthExternal(config, &path.AuthExternal, url)
+			c.setAuthExternal(config, &path.AuthExtBack, url)
 		}
 	}
 }
@@ -645,7 +646,7 @@ func firstToken(s string) string {
 }
 
 func (c *updater) buildBackendCustomResponses(d *backData) {
-	d.backend.CustomHTTPResponses = c.buildHTTPResponses(d.backend.ID, d.mapper, keyScopeBackend)
+	d.backend.CustomHTTPResponses = c.buildHTTPResponses(d.mapper, keyScopeBackend)
 }
 
 func (c *updater) buildBackendDNS(d *backData) {
@@ -740,10 +741,11 @@ func (c *updater) buildBackendOAuth(d *backData) {
 		if oauth.Source == nil {
 			continue
 		}
+		authext := &path.AuthExtBack
 
 		// starting here the auth backend should be configured or requests should be denied
 		// AlwaysDeny will be changed to false if the configuration succeed
-		path.AuthExternal.AlwaysDeny = true
+		authext.AlwaysDeny = true
 
 		if oauth.Value != "oauth2_proxy" && oauth.Value != "oauth2-proxy" {
 			c.logger.Warn("ignoring invalid oauth implementation '%s' on %v", oauth, oauth.Source)
@@ -756,7 +758,7 @@ func (c *updater) buildBackendOAuth(d *backData) {
 		}
 		if authURL := d.mapper.Get(ingtypes.BackAuthURL); authURL.Value != "" {
 			c.logger.Warn("ignoring oauth configuration on %v: auth-url was configured and has precedence", authURL.Source)
-			path.AuthExternal.AlwaysDeny = false
+			authext.AlwaysDeny = false
 			continue
 		}
 		uriPrefix := "/oauth2"
@@ -765,7 +767,7 @@ func (c *updater) buildBackendOAuth(d *backData) {
 		}
 		uriPrefix = strings.TrimRight(uriPrefix, "/")
 		namespace := oauth.Source.Namespace
-		backend := c.findBackend(namespace, uriPrefix)
+		backend := c.findOAuthBackend(namespace, uriPrefix)
 		if backend == nil {
 			c.logger.Error("path '%s' was not found on namespace '%s'", uriPrefix, namespace)
 			continue
@@ -785,19 +787,30 @@ func (c *updater) buildBackendOAuth(d *backData) {
 			headersMap[h[0]] = buildAuthRequestVarName(h[len(h)-1])
 		}
 
-		path.AuthExternal.AlwaysDeny = false
-		path.AuthExternal.AuthBackendName = backend.ID
-		path.AuthExternal.AllowedPath = uriPrefix + "/"
-		path.AuthExternal.AuthPath = uriPrefix + "/auth"
-		path.AuthExternal.HeadersRequest = []string{"*"}
-		path.AuthExternal.HeadersSucceed = []string{"-"}
-		path.AuthExternal.HeadersFail = []string{"-"}
-		path.AuthExternal.HeadersVars = headersMap
-		path.AuthExternal.Method = "HEAD"
-		path.AuthExternal.RedirectOnFail = uriPrefix + "/start?rd=%[path]"
+		authext.AlwaysDeny = false
+		authext.AuthBackendName = backend.ID
+		authext.AllowedPath = uriPrefix + "/"
+		authext.AuthPath = uriPrefix + "/auth"
+		authext.HeadersRequest = []string{"*"}
+		authext.HeadersSucceed = []string{"-"}
+		authext.HeadersFail = []string{"-"}
+		authext.HeadersVars = headersMap
+		authext.Method = "HEAD"
+		authext.RedirectOnFail = uriPrefix + "/start?rd=%[path]"
 	}
 }
 
+func (c *updater) findOAuthBackend(namespace, uriPrefix string) *hatypes.Backend {
+	for _, backend := range c.haproxy.Backends().Items() {
+		for _, path := range backend.Paths {
+			if path.Backend.Namespace == namespace && strings.TrimRight(path.Path(), "/") == uriPrefix {
+				return path.Backend
+			}
+		}
+	}
+	return nil
+}
+
 func (c *updater) buildBackendPeers(d *backData) {
 	table := d.mapper.Get(ingtypes.BackPeersTable)
 	if table.Value == "" {
@@ -813,17 +826,6 @@ func (c *updater) buildBackendPeers(d *backData) {
 	d.backend.PeersTable = table.Value
 }
 
-func (c *updater) findBackend(namespace, uriPrefix string) *hatypes.HostBackend {
-	for _, host := range c.haproxy.Hosts().Items() {
-		for _, path := range host.Paths {
-			if strings.TrimRight(path.Path(), "/") == uriPrefix && path.Backend.Namespace == namespace {
-				return &path.Backend
-			}
-		}
-	}
-	return nil
-}
-
 var validDomainRegex = regexp.MustCompile(`^([A-Za-z0-9-]{1,63}\.)+[A-Za-z]{2,6}$`)
 
 func (c *updater) buildBackendProtocol(d *backData) {
@@ -1048,8 +1050,7 @@ func (c *updater) buildBackendSSL(d *backData) {
 func (c *updater) buildBackendSSLRedirect(d *backData) {
 	noTLSRedir := utils.Split(d.mapper.Get(ingtypes.GlobalNoTLSRedirectLocations).Value, ",")
 	for _, path := range d.backend.Paths {
-		redir := path.Host != nil && path.Host.UseTLS() &&
-			d.mapper.GetConfig(path.Link).Get(ingtypes.BackSSLRedirect).Bool()
+		redir := path.HasHTTPS && (path.Host == nil || !path.Host.IsHTTPS()) && d.mapper.GetConfig(path.Link).Get(ingtypes.BackSSLRedirect).Bool()
 		if redir {
 			for _, noredir := range noTLSRedir {
 				if strings.HasPrefix(path.Path(), noredir) {
diff --git a/pkg/converters/ingress/annotations/global.go b/pkg/converters/ingress/annotations/global.go
index 9b2ee412..4d91289f 100644
--- a/pkg/converters/ingress/annotations/global.go
+++ b/pkg/converters/ingress/annotations/global.go
@@ -67,7 +67,7 @@ var authProxyRegex = regexp.MustCompile(`^([A-Za-z_-]+):([0-9]{1,5})-([0-9]{1,5}
 func (c *updater) buildGlobalAuthProxy(d *globalData) {
 	proxystr := d.mapper.Get(ingtypes.GlobalAuthProxy).Value
 	proxy := authProxyRegex.FindStringSubmatch(proxystr)
-	authproxy := &c.haproxy.Frontend().AuthProxy
+	authproxy := &c.haproxy.Frontends().AuthProxy
 	if len(proxy) < 4 {
 		c.logger.Warn("invalid auth proxy configuration: %s", proxystr)
 		// start>end ensures that trying to create a frontend bind will fail
@@ -80,25 +80,6 @@ func (c *updater) buildGlobalAuthProxy(d *globalData) {
 	authproxy.RangeEnd, _ = strconv.Atoi(proxy[3])
 }
 
-func (c *updater) buildGlobalBind(d *globalData) {
-	d.global.Bind.AcceptProxy = d.mapper.Get(ingtypes.GlobalUseProxyProtocol).Bool()
-	d.global.Bind.TCPBindIP = d.mapper.Get(ingtypes.GlobalBindIPAddrTCP).Value
-	if bindHTTP := d.mapper.Get(ingtypes.GlobalBindHTTP).Value; bindHTTP != "" {
-		d.global.Bind.HTTPBind = bindHTTP
-	} else {
-		ip := d.mapper.Get(ingtypes.GlobalBindIPAddrHTTP).Value
-		port := d.mapper.Get(ingtypes.GlobalHTTPPort).Int()
-		d.global.Bind.HTTPBind = fmt.Sprintf("%s:%d", ip, port)
-	}
-	if bindHTTPS := d.mapper.Get(ingtypes.GlobalBindHTTPS).Value; bindHTTPS != "" {
-		d.global.Bind.HTTPSBind = bindHTTPS
-	} else {
-		ip := d.mapper.Get(ingtypes.GlobalBindIPAddrHTTP).Value
-		port := d.mapper.Get(ingtypes.GlobalHTTPSPort).Int()
-		d.global.Bind.HTTPSBind = fmt.Sprintf("%s:%d", ip, port)
-	}
-}
-
 func (c *updater) buildGlobalCloseSessions(d *globalData) {
 	durationCfg := d.mapper.Get(ingtypes.GlobalCloseSessionsDuration).Value
 	if durationCfg == "" {
@@ -362,7 +343,7 @@ func (c *updater) buildGlobalTimeout(d *globalData) {
 	}
 }
 
-func (c *updater) buildSecurity(d *globalData) {
+func (c *updater) buildGlobalSecurity(d *globalData) {
 	username := d.mapper.Get(ingtypes.GlobalUsername).Value
 	groupname := d.mapper.Get(ingtypes.GlobalGroupname).Value
 	if (username == "") != (groupname == "") {
@@ -407,27 +388,6 @@ func (c *updater) buildGlobalSSL(d *globalData) {
 	ssl.SSLRedirect = d.mapper.Get(ingtypes.BackSSLRedirect).Bool()
 }
 
-func (c *updater) buildGlobalHTTPStoHTTP(d *globalData) {
-	bind := d.mapper.Get(ingtypes.GlobalBindFrontingProxy).Value
-	if bind == "" {
-		port := d.mapper.Get(ingtypes.GlobalFrontingProxyPort).Int()
-		if port == 0 {
-			port = d.mapper.Get(ingtypes.GlobalHTTPStoHTTPPort).Int()
-		}
-		if port == 0 {
-			return
-		}
-		bind = fmt.Sprintf("%s:%d", d.mapper.Get(ingtypes.GlobalBindIPAddrHTTP).Value, port)
-	}
-	// TODO Change all `ToHTTP` naming to `FrontingProxy`
-	d.global.Bind.FrontingBind = bind
-	d.global.Bind.FrontingUseProto = d.mapper.Get(ingtypes.GlobalUseForwardedProto).Bool()
-	// Socket ID should be a high number to avoid collision
-	// between the same socket ID from distinct frontends
-	// TODO match socket and frontend ID in the backend
-	d.global.Bind.FrontingSockID = 10011
-}
-
 func (c *updater) buildGlobalModSecurity(d *globalData) {
 	d.global.ModSecurity.Endpoints = utils.Split(d.mapper.Get(ingtypes.GlobalModsecurityEndpoints).Value, ",")
 	d.global.ModSecurity.Timeout.Connect = c.validateTime(d.mapper.Get(ingtypes.GlobalModsecurityTimeoutConnect))
@@ -558,7 +518,9 @@ func (c *updater) buildGlobalCustomConfig(d *globalData) {
 }
 
 func (c *updater) buildGlobalCustomResponses(d *globalData) {
-	d.global.CustomHTTPResponses = c.buildHTTPResponses(hatypes.HTTPResponseGlobalID, d.mapper, keyScopeGlobal)
+	res := c.buildHTTPResponses(d.mapper, keyScopeGlobal)
+	res.ID = hatypes.HTTPResponseGlobalID
+	d.global.CustomHTTPResponses = res
 }
 
 func (c *updater) buildGlobalFastCGI(d *globalData) {
@@ -693,7 +655,7 @@ var customHTTPResponses = []struct {
 	{"504", 504, keyScopeBackend, "Gateway Timeout", ingtypes.BackHTTPResponse504, ""},
 }
 
-func (c *updater) buildHTTPResponses(id string, mapper *Mapper, scope keyScope) hatypes.HTTPResponses {
+func (c *updater) buildHTTPResponses(mapper *Mapper, scope keyScope) hatypes.HTTPResponses {
 	var haResponses, luaResponses []hatypes.HTTPResponse
 	for _, data := range customHTTPResponses {
 		if scope != keyScopeGlobal && data.scope != scope {
@@ -740,7 +702,7 @@ func (c *updater) buildHTTPResponses(id string, mapper *Mapper, scope keyScope)
 	var res hatypes.HTTPResponses
 	if len(haResponses) > 0 || len(luaResponses) > 0 {
 		res = hatypes.HTTPResponses{
-			ID:      id,
+			ID:      "", // this is being configured when listed on Frontends()/Backends().BuildHTTPResponses(), which is closer to the real usage
 			HAProxy: haResponses,
 			Lua:     luaResponses,
 		}
diff --git a/pkg/converters/ingress/annotations/host.go b/pkg/converters/ingress/annotations/host.go
index 9df36862..5c42e1dc 100644
--- a/pkg/converters/ingress/annotations/host.go
+++ b/pkg/converters/ingress/annotations/host.go
@@ -17,18 +17,58 @@ limitations under the License.
 package annotations
 
 import (
+	"fmt"
+
 	ingtypes "github.com/jcmoraisjr/haproxy-ingress/pkg/converters/ingress/types"
 	convtypes "github.com/jcmoraisjr/haproxy-ingress/pkg/converters/types"
 	"github.com/jcmoraisjr/haproxy-ingress/pkg/haproxy/types"
 )
 
+func (c *updater) buildHTTPFrontBind(d *frontData) {
+	d.front.AcceptProxy = d.mapper.Get(ingtypes.FrontUseProxyProtocol).Bool()
+	if bindHTTP := d.mapper.Get(ingtypes.FrontBindHTTP).Value; bindHTTP != "" {
+		d.front.Bind = bindHTTP
+	} else {
+		ip := d.mapper.Get(ingtypes.FrontBindIPAddrHTTP).Value
+		port := d.mapper.Get(ingtypes.FrontHTTPPort).Int()
+		d.front.Bind = fmt.Sprintf("%s:%d", ip, port)
+	}
+}
+
+func (c *updater) buildHTTPSFrontBind(d *frontData) {
+	d.front.AcceptProxy = d.mapper.Get(ingtypes.FrontUseProxyProtocol).Bool()
+	if bindHTTPS := d.mapper.Get(ingtypes.FrontBindHTTPS).Value; bindHTTPS != "" {
+		d.front.Bind = bindHTTPS
+	} else {
+		ip := d.mapper.Get(ingtypes.FrontBindIPAddrHTTP).Value
+		port := d.mapper.Get(ingtypes.FrontHTTPSPort).Int()
+		d.front.Bind = fmt.Sprintf("%s:%d", ip, port)
+	}
+}
+
+func (c *updater) buildHTTPFrontFrontingProxy(d *frontData) {
+	bind := d.mapper.Get(ingtypes.FrontBindFrontingProxy).Value
+	if bind == "" {
+		port := d.mapper.Get(ingtypes.FrontFrontingProxyPort).Int()
+		if port == 0 {
+			port = d.mapper.Get(ingtypes.FrontHTTPStoHTTPPort).Int()
+		}
+		if port == 0 {
+			return
+		}
+		bind = fmt.Sprintf("%s:%d", d.mapper.Get(ingtypes.FrontBindIPAddrHTTP).Value, port)
+	}
+	d.front.IsFrontingProxy = true
+	d.front.IsFrontingUseProto = d.mapper.Get(ingtypes.FrontUseForwardedProto).Bool()
+	d.front.Bind = bind
+}
+
 func (c *updater) buildHostAuthExternal(d *hostData) {
 	isFrontend := d.mapper.Get(ingtypes.BackAuthExternalPlacement).ToLower() == "frontend"
 	url := d.mapper.Get(ingtypes.BackAuthURL)
 	if isFrontend && url.Value != "" {
 		for _, path := range d.host.Paths {
-			path.AuthExt = &types.AuthExternal{}
-			c.setAuthExternal(d.mapper, path.AuthExt, url)
+			c.setAuthExternal(d.mapper, &path.AuthExtFront, url)
 		}
 	}
 }
@@ -111,43 +151,28 @@ func (c *updater) buildHostCertSigner(d *hostData) {
 
 func (c *updater) buildHostRedirect(d *hostData) {
 	// TODO need a host<->host tracking if a target is found
-	redir := d.mapper.Get(ingtypes.HostRedirectFrom)
-	if target := c.haproxy.Hosts().FindTargetRedirect(redir.Value, false); target != nil {
-		c.logger.Warn("ignoring redirect from '%s' on %v, it's already targeting to '%s'",
-			redir.Value, redir.Source, target.Hostname)
-	} else if len(d.host.Paths) > 0 {
-		d.host.Redirect.RedirectHost = redir.Value
-	}
-	redirRegex := d.mapper.Get(ingtypes.HostRedirectFromRegex)
-	if target := c.haproxy.Hosts().FindTargetRedirect(redirRegex.Value, true); target != nil {
-		c.logger.Warn("ignoring regex redirect from '%s' on %v, it's already targeting to '%s'",
-			redirRegex.Value, redirRegex.Source, target.Hostname)
-	} else if len(d.host.Paths) > 0 {
-		d.host.Redirect.RedirectHostRegex = redirRegex.Value
-	}
-}
-
-func (c *updater) buildHostSSLPassthrough(d *hostData) {
-	sslpassthrough := d.mapper.Get(ingtypes.HostSSLPassthrough)
-	if !sslpassthrough.Bool() {
-		return
-	}
-	rootPaths := d.host.FindPath("/")
-	if len(rootPaths) == 0 {
-		c.logger.Warn("skipping SSL of %s: root path was not configured", sslpassthrough.Source)
-		return
-	}
-	hostBackend := rootPaths[0].Backend
-	sslpassHTTPPort := d.mapper.Get(ingtypes.HostSSLPassthroughHTTPPort)
-	if sslpassHTTPPort.Source != nil {
-		httpBackend := c.haproxy.Backends().FindBackend(hostBackend.Namespace, hostBackend.Name, sslpassHTTPPort.Value)
-		if httpBackend != nil {
-			d.host.HTTPPassthroughBackend = httpBackend.ID
+	redirOnPort := func(port int32) {
+		f := c.haproxy.Frontends().FindFrontend(port)
+		if f == nil {
+			return
+		}
+		redir := d.mapper.Get(ingtypes.HostRedirectFrom)
+		if target := f.FindTargetRedirect(redir.Value, false); target != nil {
+			c.logger.Warn("ignoring redirect from '%s' port %d on %v, it's already targeting to '%s'",
+				redir.Value, port, redir.Source, target.Hostname)
+		} else if len(d.host.Paths) > 0 {
+			d.host.Redirect.RedirectHost = redir.Value
+		}
+		redirRegex := d.mapper.Get(ingtypes.HostRedirectFromRegex)
+		if target := f.FindTargetRedirect(redirRegex.Value, true); target != nil {
+			c.logger.Warn("ignoring regex redirect from '%s' port %d on %v, it's already targeting to '%s'",
+				redirRegex.Value, port, redirRegex.Source, target.Hostname)
+		} else if len(d.host.Paths) > 0 {
+			d.host.Redirect.RedirectHostRegex = redirRegex.Value
 		}
 	}
-	backend := c.haproxy.Backends().AcquireBackend(hostBackend.Namespace, hostBackend.Name, hostBackend.Port)
-	backend.ModeTCP = true
-	d.host.SetSSLPassthrough(true)
+	redirOnPort(d.mapper.Get(ingtypes.FrontHTTPPort).Int32())
+	redirOnPort(d.mapper.Get(ingtypes.FrontHTTPSPort).Int32())
 }
 
 func (c *updater) buildHostTLSConfig(d *hostData) {
@@ -164,5 +189,5 @@ func (c *updater) buildHostTLSConfig(d *hostData) {
 }
 
 func (c *updater) buildHostCustomResponses(d *hostData) {
-	d.host.CustomHTTPResponses = c.buildHTTPResponses(d.host.Hostname, d.mapper, keyScopeHost)
+	d.host.CustomHTTPResponses = c.buildHTTPResponses(d.mapper, keyScopeHost)
 }
diff --git a/pkg/converters/ingress/annotations/mapper.go b/pkg/converters/ingress/annotations/mapper.go
index 89762279..b80ec911 100644
--- a/pkg/converters/ingress/annotations/mapper.go
+++ b/pkg/converters/ingress/annotations/mapper.go
@@ -166,6 +166,10 @@ func (c *Mapper) GetConfig(path *hatypes.PathLink) *KeyConfig {
 	return config
 }
 
+func (c *Mapper) CopyConfig(dst, src *hatypes.PathLink) {
+	c.configByPath[dst.Hash()] = c.GetConfig(src)
+}
+
 // Get ...
 func (c *Mapper) Get(key string) *ConfigValue {
 	configs, found := c.findPathConfig(key)
@@ -249,9 +253,15 @@ func (cv *ConfigValue) Int() int {
 	return value
 }
 
+// Int32 ...
+func (cv *ConfigValue) Int32() int32 {
+	value, _ := strconv.Atoi(cv.Value)
+	return int32(value)
+}
+
 // Int64 ...
 func (cv *ConfigValue) Int64() int64 {
-	value, _ := strconv.ParseInt(cv.Value, 10, 0)
+	value, _ := strconv.ParseInt(cv.Value, 10, 64)
 	return value
 }
 
diff --git a/pkg/converters/ingress/annotations/updater.go b/pkg/converters/ingress/annotations/updater.go
index 09723561..5ab89d78 100644
--- a/pkg/converters/ingress/annotations/updater.go
+++ b/pkg/converters/ingress/annotations/updater.go
@@ -36,6 +36,7 @@ type Updater interface {
 	UpdatePeers(haproxyConfig haproxy.Config, mapper *Mapper)
 	UpdateTCPPortConfig(tcp *hatypes.TCPServicePort, mapper *Mapper)
 	UpdateTCPHostConfig(tcpPort *hatypes.TCPServicePort, tcpHost *hatypes.TCPServiceHost, mapper *Mapper)
+	UpdateFrontConfig(front *hatypes.Frontend, mapper *Mapper)
 	UpdateHostConfig(host *hatypes.Host, mapper *Mapper)
 	UpdateBackendConfig(backend *hatypes.Backend, mapper *Mapper)
 }
@@ -76,6 +77,11 @@ type tcpData struct {
 	mapper  *Mapper
 }
 
+type frontData struct {
+	front  *hatypes.Frontend
+	mapper *Mapper
+}
+
 type hostData struct {
 	host   *hatypes.Host
 	mapper *Mapper
@@ -160,6 +166,7 @@ func (c *updater) UpdateGlobalConfig(haproxyConfig haproxy.Config, mapper *Mappe
 		mapper:   mapper,
 	}
 	d.global.AdminSocket = c.options.AdminSocket
+	d.global.TCPBindIP = d.mapper.Get(ingtypes.GlobalBindIPAddrTCP).String()
 	d.global.LocalFSPrefix = c.options.LocalFSPrefix
 	d.global.MaxConn = mapper.Get(ingtypes.GlobalMaxConnections).Int()
 	d.global.DefaultBackendRedir = mapper.Get(ingtypes.GlobalDefaultBackendRedirect).String()
@@ -176,13 +183,8 @@ func (c *updater) UpdateGlobalConfig(haproxyConfig haproxy.Config, mapper *Mappe
 	d.global.Master.WorkerMaxReloads = mapper.Get(ingtypes.GlobalWorkerMaxReloads).Int()
 	d.global.StrictHost = mapper.Get(ingtypes.GlobalStrictHost).Bool()
 	d.global.UseHTX = mapper.Get(ingtypes.GlobalUseHTX).Bool()
-	//
-	c.haproxy.Frontend().RedirectFromCode = mapper.Get(ingtypes.GlobalRedirectFromCode).Int()
-	c.haproxy.Frontend().RedirectToCode = mapper.Get(ingtypes.GlobalRedirectToCode).Int()
-	//
 	c.buildGlobalAcme(d)
 	c.buildGlobalAuthProxy(d)
-	c.buildGlobalBind(d)
 	c.buildGlobalCloseSessions(d)
 	c.buildGlobalCustomConfig(d)
 	c.buildGlobalCustomResponses(d)
@@ -190,11 +192,10 @@ func (c *updater) UpdateGlobalConfig(haproxyConfig haproxy.Config, mapper *Mappe
 	c.buildGlobalDynamic(d)
 	c.buildGlobalFastCGI(d)
 	c.buildGlobalForwardFor(d)
-	c.buildGlobalHTTPStoHTTP(d)
 	c.buildGlobalModSecurity(d)
 	c.buildGlobalPathTypeOrder(d)
 	c.buildGlobalProc(d)
-	c.buildSecurity(d)
+	c.buildGlobalSecurity(d)
 	c.buildGlobalSSL(d)
 	c.buildGlobalStats(d)
 	c.buildGlobalSyslog(d)
@@ -202,16 +203,19 @@ func (c *updater) UpdateGlobalConfig(haproxyConfig haproxy.Config, mapper *Mappe
 }
 
 func (c *updater) UpdatePeers(haproxyConfig haproxy.Config, mapper *Mapper) {
+	// NOTE - Peers is updated without cleanup, so all the methods should be idempotent.
 	global := haproxyConfig.Global()
-	c.buildGlobalPeers(&globalData{
+	d := &globalData{
 		global: global,
 		mapper: mapper,
-	})
+	}
+	c.buildGlobalPeers(d)
 	// peers is currently a silly dependency to build global vars
 	c.vars = buildGlobalVars(global)
 }
 
 func (c *updater) UpdateTCPPortConfig(tcp *hatypes.TCPServicePort, mapper *Mapper) {
+	// NOTE - TCPPortConfig is updated without cleanup, so all the methods should be idempotent.
 	if config := mapper.Get(ingtypes.TCPConfigTCPService).Value; config != "" {
 		tcp.CustomConfig = utils.PatternLineToSlice(c.vars, config)
 	}
@@ -220,32 +224,48 @@ func (c *updater) UpdateTCPPortConfig(tcp *hatypes.TCPServicePort, mapper *Mappe
 }
 
 func (c *updater) UpdateTCPHostConfig(tcpPort *hatypes.TCPServicePort, tcpHost *hatypes.TCPServiceHost, mapper *Mapper) {
-	data := &tcpData{
+	// NOTE - TCPHostConfig is updated without cleanup, so all the methods should be idempotent.
+	d := &tcpData{
 		tcpPort: tcpPort,
 		tcpHost: tcpHost,
 		mapper:  mapper,
 	}
-	c.buildTCPAuthTLS(data)
+	c.buildTCPAuthTLS(d)
+}
+
+func (c *updater) UpdateFrontConfig(front *hatypes.Frontend, mapper *Mapper) {
+	// NOTE - FrontConfig is updated without cleanup, so all the methods should be idempotent.
+	d := &frontData{
+		front:  front,
+		mapper: mapper,
+	}
+	front.RedirectFromCode = mapper.Get(ingtypes.FrontRedirectFromCode).Int()
+	front.RedirectToCode = mapper.Get(ingtypes.FrontRedirectToCode).Int()
+	if front.IsHTTPS {
+		c.buildHTTPSFrontBind(d)
+	} else {
+		c.buildHTTPFrontBind(d)
+		c.buildHTTPFrontFrontingProxy(d)
+	}
 }
 
 func (c *updater) UpdateHostConfig(host *hatypes.Host, mapper *Mapper) {
-	data := &hostData{
+	d := &hostData{
 		host:   host,
 		mapper: mapper,
 	}
 	host.RootRedirect = mapper.Get(ingtypes.HostAppRoot).Value
 	host.Alias.AliasName = mapper.Get(ingtypes.HostServerAlias).Value
 	host.Alias.AliasRegex = mapper.Get(ingtypes.HostServerAliasRegex).Value
-	host.TLS.UseDefaultCrt = mapper.Get(ingtypes.HostSSLAlwaysAddHTTPS).Bool()
-	host.TLS.FollowRedirect = mapper.Get(ingtypes.HostSSLAlwaysFollowRedirect).Bool()
 	host.VarNamespace = mapper.Get(ingtypes.HostVarNamespace).Bool()
-	c.buildHostAuthExternal(data)
-	c.buildHostAuthTLS(data)
-	c.buildHostCertSigner(data)
-	c.buildHostRedirect(data)
-	c.buildHostSSLPassthrough(data)
-	c.buildHostTLSConfig(data)
-	c.buildHostCustomResponses(data)
+	c.buildHostAuthExternal(d)
+	c.buildHostCertSigner(d)
+	c.buildHostRedirect(d)
+	c.buildHostCustomResponses(d)
+	if host.IsHTTPS() {
+		c.buildHostAuthTLS(d)
+		c.buildHostTLSConfig(d)
+	}
 }
 
 func (c *updater) UpdateBackendConfig(backend *hatypes.Backend, mapper *Mapper) {
diff --git a/pkg/converters/ingress/defaults.go b/pkg/converters/ingress/defaults.go
index 6462046d..da7476d4 100644
--- a/pkg/converters/ingress/defaults.go
+++ b/pkg/converters/ingress/defaults.go
@@ -32,6 +32,12 @@ func createDefaults() map[string]string {
 	return map[string]string{
 		types.TCPTCPServiceLogFormat: "default",
 		//
+		types.FrontHTTPPort:          "80",
+		types.FrontHTTPSPort:         "443",
+		types.FrontRedirectFromCode:  "302",
+		types.FrontRedirectToCode:    "302",
+		types.FrontUseForwardedProto: "true",
+		//
 		types.HostAuthTLSStrict:           "true",
 		types.HostSSLAlwaysAddHTTPS:       "false",
 		types.HostSSLAlwaysFollowRedirect: "true",
@@ -89,8 +95,6 @@ func createDefaults() map[string]string {
 		types.GlobalFCGIEnabledApps:              "*",
 		types.GlobalForwardfor:                   "add",
 		types.GlobalHealthzPort:                  "10253",
-		types.GlobalHTTPPort:                     "80",
-		types.GlobalHTTPSPort:                    "443",
 		types.GlobalMasterExitOnFailure:          "true",
 		types.GlobalMaxConnections:               "2000",
 		types.GlobalModsecurityArgs:              "unique-id method path query req.ver req.hdrs_bin req.body_size req.body", // Ref: https://github.com/haproxy/spoa-modsecurity/blob/3c895f3e7dd291dba19d57ba054b277e6fb80ca4/README#L70
@@ -106,8 +110,6 @@ func createDefaults() map[string]string {
 		types.GlobalPathTypeOrder:                "exact,prefix,begin,regex",
 		types.GlobalPeersName:                    "ingress",
 		types.GlobalRealIPHdr:                    "X-Real-IP",
-		types.GlobalRedirectFromCode:             "302",
-		types.GlobalRedirectToCode:               "302",
 		types.GlobalSSLDHDefaultMaxSize:          "2048",
 		types.GlobalSSLHeadersPrefix:             "X-SSL",
 		types.GlobalSSLOptions:                   defaultSSLOptions,
@@ -119,7 +121,6 @@ func createDefaults() map[string]string {
 		types.GlobalTimeoutClientFin:             "50s",
 		types.GlobalTimeoutStop:                  "10m",
 		types.GlobalUseCPUMap:                    "true",
-		types.GlobalUseForwardedProto:            "true",
 		types.GlobalUseHTX:                       "true",
 		types.GlobalDefaultBackendRedirectCode:   "302",
 	}
diff --git a/pkg/converters/ingress/ingress.go b/pkg/converters/ingress/ingress.go
index f8925e25..2b32c7ce 100644
--- a/pkg/converters/ingress/ingress.go
+++ b/pkg/converters/ingress/ingress.go
@@ -19,8 +19,10 @@ package ingress
 import (
 	"fmt"
 	"hash/fnv"
+	"maps"
 	"reflect"
 	"regexp"
+	"slices"
 	"sort"
 	"strconv"
 	"strings"
@@ -75,6 +77,7 @@ func NewIngressConverter(options *convtypes.ConverterOptions, haproxy haproxy.Co
 		updater:            annotations.NewUpdater(haproxy, options),
 		globalConfig:       annotations.NewMapBuilder(options.Logger, defaultConfig).NewMapper(),
 		tcpsvcAnnotations:  map[*hatypes.TCPServicePort]*annotations.Mapper{},
+		frontAnnotations:   map[*hatypes.Frontend]*annotations.Mapper{},
 		hostAnnotations:    map[*hatypes.Host]*annotations.Mapper{},
 		backendAnnotations: map[*hatypes.Backend]*annotations.Mapper{},
 		ingressClasses:     map[string]*ingressClassConfig{},
@@ -96,6 +99,7 @@ type converter struct {
 	updater            annotations.Updater
 	globalConfig       *annotations.Mapper
 	tcpsvcAnnotations  map[*hatypes.TCPServicePort]*annotations.Mapper
+	frontAnnotations   map[*hatypes.Frontend]*annotations.Mapper
 	hostAnnotations    map[*hatypes.Host]*annotations.Mapper
 	backendAnnotations map[*hatypes.Backend]*annotations.Mapper
 	ingressClasses     map[string]*ingressClassConfig
@@ -109,7 +113,7 @@ func (c *converter) ReadAnnotations(backend *hatypes.Backend, services []*api.Se
 			Name:      service.Name,
 			Type:      convtypes.ResourceService,
 		}
-		_, _, ann := c.readAnnotations(source, service.Annotations)
+		_, _, _, ann := c.readAnnotations(source, service.Annotations)
 		for _, pathLink := range pathLinks {
 			conflict := mapper.AddAnnotations(source, pathLink, ann)
 			if len(conflict) > 0 {
@@ -144,9 +148,9 @@ func (c *converter) Sync(full bool) {
 }
 
 func (c *converter) defaultCrtNeedFullSync() bool {
-	frontend := c.haproxy.Frontend()
-	return frontend.DefaultCrtFile != c.defaultCrt.Filename ||
-		frontend.DefaultCrtHash != c.defaultCrt.SHA1Hash
+	f := c.haproxy.Frontends()
+	return f.DefaultCrtFile != c.defaultCrt.Filename ||
+		f.DefaultCrtHash != c.defaultCrt.SHA1Hash
 }
 
 func (c *converter) globalConfigNeedFullSync() bool {
@@ -181,15 +185,18 @@ func (c *converter) readDefaultCertificate() {
 }
 
 func (c *converter) syncDefaultCrt() {
-	frontend := c.haproxy.Frontend()
-	frontend.DefaultCrtFile = c.defaultCrt.Filename
-	frontend.DefaultCrtHash = c.defaultCrt.SHA1Hash
+	f := c.haproxy.Frontends()
+	f.DefaultCrtFile = c.defaultCrt.Filename
+	f.DefaultCrtHash = c.defaultCrt.SHA1Hash
 }
 
+// bareLink creates a pathlink with default path and match type params,
+// used to create generic, TCP, Frontend or Hostname based pathlinks.
+func bareLink() *hatypes.PathLink { return hatypes.CreatePathLink("/", hatypes.MatchExact) }
+
 func (c *converter) syncDefaultBackend() {
 	if c.options.DefaultBackend != "" {
-		pathLink := hatypes.CreateHostPathLink(hatypes.DefaultHost, "/", hatypes.MatchBegin)
-		if backend, err := c.addBackend(&c.defaultBackSource, pathLink, c.options.DefaultBackend, "", map[string]string{}); err == nil {
+		if backend, err := c.addBackend(&c.defaultBackSource, bareLink(), c.options.DefaultBackend, "", map[string]string{}, nil); err == nil {
 			c.haproxy.Backends().DefaultBackend = backend
 			c.tracker.TrackNames(c.defaultBackSource.Type, c.defaultBackSource.FullName(), convtypes.ResourceHAHostname, hatypes.DefaultHost)
 		} else {
@@ -210,8 +217,7 @@ func (c *converter) syncFull() {
 	for _, ing := range ingList {
 		c.syncIngress(ing)
 	}
-	c.fullSyncAnnotations()
-	c.syncEndpoints()
+	c.syncConfig()
 }
 
 func (c *converter) syncPartial() {
@@ -226,8 +232,8 @@ func (c *converter) syncPartial() {
 	dirtyStorages := trackedLinks[convtypes.ResourceAcmeData]
 
 	c.haproxy.TCPServices().RemoveAll(dirtyTCPServices)
-	c.haproxy.Hosts().RemoveAll(dirtyHosts)
-	c.haproxy.Frontend().RemoveAuthBackendByTarget(dirtyBacks)
+	c.haproxy.Frontends().RemoveAllHosts(dirtyHosts)
+	c.haproxy.Frontends().AuthProxy.RemoveAuthBackendByTarget(dirtyBacks)
 	c.haproxy.Backends().RemoveAll(dirtyBacks)
 	c.haproxy.Userlists().RemoveAll(dirtyUsers)
 	c.haproxy.AcmeData().Storages().RemoveAll(dirtyStorages)
@@ -281,8 +287,7 @@ func (c *converter) syncPartial() {
 	for _, ing := range ingList {
 		c.syncIngress(ing)
 	}
-	c.partialSyncAnnotations()
-	c.syncChangedEndpoints()
+	c.syncConfig()
 }
 
 // trackAddedIngress add tracking hostnames and backends to new ingress objects
@@ -375,20 +380,109 @@ func (c *converter) syncIngress(ing *networking.Ingress) {
 		Name:      ing.Name,
 		Type:      convtypes.ResourceIngress,
 	}
-	annTCP, annHost, annBack := c.readAnnotations(source, ing.Annotations)
+	annTCP, annFront, annHost, annBack := c.readAnnotations(source, ing.Annotations)
 	tcpServicePort, _ := strconv.Atoi(annTCP[ingtypes.TCPTCPServicePort])
 	if tcpServicePort == 0 {
-		c.syncIngressHTTP(source, ing, annHost, annBack)
+		c.syncIngressHTTP(source, ing, annFront, annHost, annBack)
 	} else {
 		c.syncIngressTCP(source, ing, tcpServicePort, annTCP, annBack)
 	}
 }
 
-func (c *converter) syncIngressHTTP(source *annotations.Source, ing *networking.Ingress, annHost, annBack map[string]string) {
+func (c *converter) acquireFrontend(source *annotations.Source, annFront, annHost map[string]string) *frontend {
+	link := bareLink()
+	mapper := c.mapBuilder.NewMapper()
+	_ = mapper.AddAnnotations(source, link, annFront)
+	_ = mapper.AddAnnotations(source, link, annHost)
+	f := c.haproxy.Frontends()
+	httpPort := mapper.Get(ingtypes.FrontFrontingProxyPort).Int32()
+	if httpPort == 0 {
+		httpPort = mapper.Get(ingtypes.FrontHTTPPort).Int32()
+	}
+	return &frontend{
+		f:           f,
+		innerHTTP:   f.AcquireFrontend(httpPort, false),
+		httpsPort:   mapper.Get(ingtypes.FrontHTTPSPort).Int32(),
+		alwaysTLS:   mapper.Get(ingtypes.HostSSLAlwaysAddHTTPS).Bool(),
+		followRedir: mapper.Get(ingtypes.HostSSLAlwaysFollowRedirect).Bool(),
+		hosts:       make(map[string]*host),
+	}
+}
+
+type frontend struct {
+	f *hatypes.Frontends
+	innerHTTP,
+	innerHTTPS *hatypes.Frontend
+	httpsPort   int32
+	alwaysTLS   bool
+	followRedir bool
+	hosts       map[string]*host
+}
+
+type host struct {
+	inner,
+	innerHTTPS *hatypes.Host
+	paths []*hatypes.Path
+	links []*hatypes.Path
+	redir []*hatypes.Path
+}
+
+func (f *frontend) FindHost(hostname string) *host {
+	if inner := f.innerHTTP.FindHost(hostname); inner != nil {
+		h := &host{inner: inner}
+		f.hosts[hostname] = h
+		return h
+	}
+	return nil
+}
+
+func (f *frontend) AcquireHost(hostname string) *host {
+	h, found := f.hosts[hostname]
+	if !found {
+		h = &host{inner: f.innerHTTP.AcquireHost(hostname)}
+		f.hosts[hostname] = h
+	}
+	return h
+}
+
+func (f *frontend) AcquireHostHTTPS(hostname string) *host {
+	if f.innerHTTPS == nil {
+		f.innerHTTPS = f.f.AcquireFrontend(f.httpsPort, true)
+	}
+	h := f.AcquireHost(hostname)
+	h.innerHTTPS = f.innerHTTPS.AcquireHost(hostname)
+	return h
+}
+
+func (f *frontend) RemoveAllHosts(hostnames []string) {
+	f.innerHTTP.RemoveAllHosts(hostnames)
+	if f.innerHTTPS != nil {
+		f.innerHTTPS.RemoveAllHosts(hostnames)
+	}
+	maps.DeleteFunc(f.hosts, func(h string, _ *host) bool {
+		return slices.Contains(hostnames, h)
+	})
+}
+
+func (h *host) AddPath(backend *hatypes.Backend, path string, match hatypes.MatchType) {
+	h.paths = append(h.paths, h.inner.AddPath(backend, path, match))
+}
+
+func (h *host) AddLink(backend *hatypes.Backend, link *hatypes.PathLink) {
+	h.links = append(h.links, h.inner.AddLink(backend, link))
+}
+
+func (h *host) AddRedirect(path string, match hatypes.MatchType, redirTo string) {
+	h.redir = append(h.redir, h.inner.AddRedirect(path, match, redirTo))
+}
+
+func (c *converter) syncIngressHTTP(source *annotations.Source, ing *networking.Ingress, annFront, annHost, annBack map[string]string) {
+	f := c.acquireFrontend(source, annFront, annHost)
+	defer c.syncHTTPS(f)
 	if ing.Spec.DefaultBackend != nil {
 		svcName, svcPort, err := readServiceNamePort(ing.Spec.DefaultBackend)
 		if err == nil {
-			err = c.addDefaultHostBackend(source, ing.Namespace+"/"+svcName, svcPort, annHost, annBack)
+			err = c.addDefaultHostBackend(f, source, ing.Namespace+"/"+svcName, svcPort, annFront, annHost, annBack)
 		}
 		if err != nil {
 			c.logger.Warn("skipping default backend of %v: %v", source, err)
@@ -401,14 +495,14 @@ func (c *converter) syncIngressHTTP(source *annotations.Source, ing *networking.
 		hostname := normalizeHostname(rule.Host, 0)
 		ingressClass := c.readIngressClass(source, ing.Spec.IngressClassName)
 		sslpassthrough, _ := strconv.ParseBool(annHost[ingtypes.HostSSLPassthrough])
-		host := c.addHost(hostname, source, annHost)
+		host := c.addHost(f, hostname, source, annFront, annHost, false)
 		for _, path := range rule.HTTP.Paths {
 			uri := path.Path
 			if uri == "" {
 				uri = "/"
 			}
 			match := c.readPathType(path, annBack[ingtypes.BackPathType])
-			pathLink := hatypes.CreateHostPathLink(hostname, uri, match)
+			pathLink := hatypes.CreatePathLink(uri, match).WithHTTPHost(host.inner)
 			if headerMatch := annBack[ingtypes.BackHTTPHeaderMatch]; headerMatch != "" {
 				c.addHeaderMatch(source, pathLink, headerMatch, false)
 			}
@@ -416,11 +510,11 @@ func (c *converter) syncIngressHTTP(source *annotations.Source, ing *networking.
 				c.addHeaderMatch(source, pathLink, headerMatch, true)
 			}
 			if sslpassthrough && uri == "/" {
-				if host.FindPath(uri) != nil {
+				if host.inner.FindPath(uri) != nil {
 					c.logger.Warn("skipping redeclared ssl-passthrough root path on %v", source)
 					continue
 				}
-			} else if host.FindPathWithLink(pathLink) != nil {
+			} else if host.inner.FindPathWithLink(pathLink) != nil {
 				c.logger.Warn("skipping redeclared path '%s' type '%s' on %v", uri, match, source)
 				continue
 			}
@@ -434,17 +528,38 @@ func (c *converter) syncIngressHTTP(source *annotations.Source, ing *networking.
 				continue
 			}
 			fullSvcName := ing.Namespace + "/" + svcName
-			backend, err := c.addBackendWithClass(source, pathLink, fullSvcName, svcPort, annBack, ingressClass)
+			backend, err := c.addBackend(source, pathLink, fullSvcName, svcPort, annBack, ingressClass)
 			if err != nil {
 				c.logger.Warn("skipping backend config of %v: %v", source, err)
 				continue
 			}
-			host.AddLink(backend, pathLink)
-			sslpasshttpport := annHost[ingtypes.HostSSLPassthroughHTTPPort]
-			if sslpassthrough && sslpasshttpport != "" {
-				if _, err := c.addBackend(source, pathLink, fullSvcName, sslpasshttpport, annBack); err != nil {
-					c.logger.Warn("skipping http port config of ssl-passthrough on %v: %v", source, err)
+			if sslpassthrough {
+				// TODO missing a better abstraction for ssl-passthrough handling
+				host := f.AcquireHostHTTPS(hostname)
+				host.innerHTTPS.SSLPassthrough = true
+				if uri == "/" {
+					// regular passthrough configuration
+					backend.ModeTCP = true
+					host.innerHTTPS.AddLink(backend, pathLink)
+					var hback *hatypes.Backend
+					if hport := annHost[ingtypes.HostSSLPassthroughHTTPPort]; hport != "" {
+						hback, err = c.addBackend(source, pathLink, fullSvcName, hport, annBack, nil)
+						if err != nil {
+							c.logger.Warn("skipping http port config of ssl-passthrough on %v: %v", source, err)
+							hback = nil
+						}
+					}
+					if hback == nil {
+						hback = c.haproxy.Backends().AcquireRedirectHTTPSBackend()
+					}
+					host.inner.AddLink(hback, pathLink)
+				} else {
+					// non root path, configure it on HTTP only
+					host.inner.AddLink(backend, pathLink)
 				}
+			} else {
+				// regular http/s request, non ssl-passthrough
+				host.AddLink(backend, pathLink)
 			}
 			// pre-building the auth-url backend
 			// TODO move to updater.buildBackendAuthExternal()
@@ -460,7 +575,7 @@ func (c *converter) syncIngressHTTP(source *annotations.Source, ing *networking.
 					if !strings.Contains(authSvcName, "/") {
 						authSvcName = ing.Namespace + "/" + authSvcName
 					}
-					_, err := c.addBackend(source, pathLink, authSvcName, urlPort, map[string]string{})
+					_, err := c.addBackend(source, pathLink, authSvcName, urlPort, map[string]string{}, nil)
 					if err != nil {
 						c.logger.Warn("skipping auth-url on %v: %v", source, err)
 					}
@@ -471,15 +586,16 @@ func (c *converter) syncIngressHTTP(source *annotations.Source, ing *networking.
 	for _, tls := range ing.Spec.TLS {
 		// tls secret
 		for _, hostname := range tls.Hosts {
-			host := c.addHost(hostname, source, annHost)
+			host := c.addHost(f, hostname, source, annFront, annHost, true)
 			tlsPath := c.addTLS(source, tls.SecretName)
-			if host.TLS.TLSHash == "" {
-				host.TLS.TLSFilename = tlsPath.Filename
-				host.TLS.TLSHash = tlsPath.SHA1Hash
-				host.TLS.TLSCommonName = tlsPath.Certificate.Subject.CommonName
-				host.TLS.TLSNotAfter = tlsPath.Certificate.NotAfter
-			} else if host.TLS.TLSHash != tlsPath.SHA1Hash {
-				msg := fmt.Sprintf("TLS of host '%s' was already assigned", host.Hostname)
+			hhttps := host.innerHTTPS
+			if hhttps.TLS.TLSHash == "" {
+				hhttps.TLS.TLSFilename = tlsPath.Filename
+				hhttps.TLS.TLSHash = tlsPath.SHA1Hash
+				hhttps.TLS.TLSCommonName = tlsPath.Certificate.Subject.CommonName
+				hhttps.TLS.TLSNotAfter = tlsPath.Certificate.NotAfter
+			} else if hhttps.TLS.TLSHash != tlsPath.SHA1Hash {
+				msg := fmt.Sprintf("TLS of host '%s' was already assigned", hhttps.Hostname)
 				if tls.SecretName != "" {
 					c.logger.Warn("skipping TLS secret '%s' of %v: %s", tls.SecretName, source, msg)
 				} else {
@@ -516,10 +632,36 @@ func (c *converter) syncIngressHTTP(source *annotations.Source, ing *networking.
 	}
 }
 
+func (c *converter) syncHTTPS(f *frontend) {
+	var https *hatypes.Frontend
+	for hostname, host := range f.hosts {
+		if f.alwaysTLS || host.innerHTTPS != nil {
+			if https == nil {
+				https = c.haproxy.Frontends().AcquireFrontend(f.httpsPort, true)
+			}
+			h := https.AcquireHost(hostname)
+			for _, srcpath := range host.paths {
+				srcpath.HasHTTPS = true
+				dstpath := h.AddPath(srcpath.Backend, srcpath.Path(), srcpath.Match())
+				c.backendAnnotations[srcpath.Backend].CopyConfig(dstpath.Link, srcpath.Link)
+			}
+			for _, srcpath := range host.links {
+				srcpath.HasHTTPS = true
+				dstpath := h.AddLink(srcpath.Backend, srcpath.Link)
+				c.backendAnnotations[srcpath.Backend].CopyConfig(dstpath.Link, srcpath.Link)
+			}
+			for _, srcpath := range host.redir {
+				srcpath.HasHTTPS = true
+				_ = h.AddRedirect(srcpath.Path(), srcpath.Match(), srcpath.RedirTo)
+			}
+		}
+	}
+}
+
 func (c *converter) syncIngressTCP(source *annotations.Source, ing *networking.Ingress, tcpServicePort int, annTCP, annBack map[string]string) {
 	addIngressBackend := func(rawHostname string, ingressBackend *networking.IngressBackend) error {
 		hostname := normalizeHostname(rawHostname, tcpServicePort)
-		tcpService, err := c.addTCPService(source, hostname, annTCP)
+		tcpService, tcpLink, err := c.addTCPService(source, hostname, annTCP)
 		if err != nil {
 			return err
 		}
@@ -536,9 +678,8 @@ func (c *converter) syncIngressTCP(source *annotations.Source, ing *networking.I
 			return fmt.Errorf("service '%s' on %v: backend for port '%d' was already assigned", svcName, source, tcpServicePort)
 		}
 		fullSvcName := ing.Namespace + "/" + svcName
-		pathLink := hatypes.CreateHostPathLink(hostname, "/", hatypes.MatchExact)
 		ingressClass := c.readIngressClass(source, ing.Spec.IngressClassName)
-		backend, err := c.addBackendWithClass(source, pathLink, fullSvcName, svcPort, annBack, ingressClass)
+		backend, err := c.addBackend(source, tcpLink, fullSvcName, svcPort, annBack, ingressClass)
 		if err != nil {
 			return err
 		}
@@ -604,59 +745,26 @@ func (c *converter) syncIngressTCP(source *annotations.Source, ing *networking.I
 	}
 }
 
-func (c *converter) syncEndpoints() {
-	for _, backend := range c.haproxy.Backends().Items() {
-		c.syncBackendEndpointCookies(backend)
-		c.syncBackendEndpointHashes(backend)
-	}
-}
-
-func (c *converter) syncChangedEndpoints() {
-	for _, backend := range c.haproxy.Backends().ItemsAdd() {
-		c.syncBackendEndpointCookies(backend)
-		c.syncBackendEndpointHashes(backend)
-	}
-}
-
-func (c *converter) fullSyncTCP() {
-	for _, tcpPort := range c.haproxy.TCPServices().Items() {
-		if ann, found := c.tcpsvcAnnotations[tcpPort]; found {
-			c.updater.UpdateTCPPortConfig(tcpPort, ann)
-			if tcpHost := tcpPort.DefaultHost(); tcpHost != nil {
-				c.updater.UpdateTCPHostConfig(tcpPort, tcpHost, ann)
-			}
-			for _, tcpHost := range tcpPort.Hosts() {
-				c.updater.UpdateTCPHostConfig(tcpPort, tcpHost, ann)
-			}
+func (c *converter) syncConfig() {
+	for tcpPort, mapper := range c.tcpsvcAnnotations {
+		c.updater.UpdateTCPPortConfig(tcpPort, mapper)
+		if tcpHost := tcpPort.DefaultHost(); tcpHost != nil {
+			c.updater.UpdateTCPHostConfig(tcpPort, tcpHost, mapper)
 		}
-	}
-}
-
-func (c *converter) fullSyncAnnotations() {
-	c.fullSyncTCP()
-	for _, host := range c.haproxy.Hosts().Items() {
-		if ann, found := c.hostAnnotations[host]; found {
-			c.updater.UpdateHostConfig(host, ann)
+		for _, tcpHost := range tcpPort.Hosts() {
+			c.updater.UpdateTCPHostConfig(tcpPort, tcpHost, mapper)
 		}
 	}
-	for _, backend := range c.haproxy.Backends().Items() {
-		if ann, found := c.backendAnnotations[backend]; found {
-			c.updater.UpdateBackendConfig(backend, ann)
-		}
+	for front, mapper := range c.frontAnnotations {
+		c.updater.UpdateFrontConfig(front, mapper)
 	}
-}
-
-func (c *converter) partialSyncAnnotations() {
-	c.fullSyncTCP()
-	for _, host := range c.haproxy.Hosts().ItemsAdd() {
-		if ann, found := c.hostAnnotations[host]; found {
-			c.updater.UpdateHostConfig(host, ann)
-		}
+	for host, mapper := range c.hostAnnotations {
+		c.updater.UpdateHostConfig(host, mapper)
 	}
-	for _, backend := range c.haproxy.Backends().ItemsAdd() {
-		if ann, found := c.backendAnnotations[backend]; found {
-			c.updater.UpdateBackendConfig(backend, ann)
-		}
+	for backend, mapper := range c.backendAnnotations {
+		c.updater.UpdateBackendConfig(backend, mapper)
+		c.syncBackendEndpointCookies(backend)
+		c.syncBackendEndpointHashes(backend)
 	}
 }
 
@@ -705,31 +813,42 @@ func (c *converter) readIngressClass(source *annotations.Source, ingressClassNam
 	return nil
 }
 
-func (c *converter) addDefaultHostBackend(source *annotations.Source, fullSvcName, svcPort string, annHost, annBack map[string]string) error {
+func (c *converter) addDefaultHostBackend(f *frontend, source *annotations.Source, fullSvcName, svcPort string, annFront, annHost, annBack map[string]string) error {
 	hostname := hatypes.DefaultHost
 	uri := "/"
 	match := hatypes.MatchBegin
-	if fr := c.haproxy.Hosts().FindHost(hostname); fr != nil {
-		if fr.FindPath(uri, match) != nil {
+
+	// existing stores if the host already existed before calling this func
+	var existing bool
+	if host := f.FindHost(hostname); host != nil {
+		if host.inner.FindPath(uri, match) != nil {
 			return fmt.Errorf("path %s was already defined on default host", uri)
 		}
+		existing = true
 	}
-	pathLink := hatypes.CreateHostPathLink(hostname, uri, match)
-	backend, err := c.addBackend(source, pathLink, fullSvcName, svcPort, annBack)
+
+	host := f.AcquireHost(hostname)
+	pathLink := hatypes.CreatePathLink(uri, match).WithHTTPHost(host.inner)
+	backend, err := c.addBackend(source, pathLink, fullSvcName, svcPort, annBack, nil)
 	if err != nil {
 		c.tracker.TrackNames(source.Type, source.FullName(), convtypes.ResourceService, fullSvcName)
+		if !existing {
+			// we needed to create it in order to configure a pathLink,
+			// so reverting the creation since we are not going to use it.
+			f.RemoveAllHosts([]string{hostname})
+		}
 		return err
 	}
-	host := c.addHost(hostname, source, annHost)
+	host = c.addHost(f, hostname, source, annFront, annHost, false)
 	host.AddPath(backend, uri, match)
 	return nil
 }
 
-func (c *converter) addTCPService(source *annotations.Source, hostname string, ann map[string]string) (*hatypes.TCPServiceHost, error) {
+func (c *converter) addTCPService(source *annotations.Source, hostname string, ann map[string]string) (*hatypes.TCPServiceHost, *hatypes.PathLink, error) {
 	tcpPort, tcpHost := c.haproxy.TCPServices().AcquireTCPService(hostname)
 	if !tcpHost.Backend.IsEmpty() {
 		tcpservice := strings.TrimPrefix(hostname, hatypes.DefaultHost)
-		return nil, fmt.Errorf("tcp service %s was already assigned to %s", tcpservice, tcpHost.Backend)
+		return nil, nil, fmt.Errorf("tcp service %s was already assigned to %s", tcpservice, tcpHost.Backend)
 	}
 	c.tracker.TrackNames(source.Type, source.FullName(), convtypes.ResourceHATCPService, hostname)
 	mapper, found := c.tcpsvcAnnotations[tcpPort]
@@ -737,29 +856,53 @@ func (c *converter) addTCPService(source *annotations.Source, hostname string, a
 		mapper = c.mapBuilder.NewMapper()
 		c.tcpsvcAnnotations[tcpPort] = mapper
 	}
-	conflict := mapper.AddAnnotations(source, hatypes.CreateHostPathLink(hostname, "/", hatypes.MatchExact), ann)
+	tcpLink := bareLink().WithTCPHost(tcpHost)
+	conflict := mapper.AddAnnotations(source, tcpLink, ann)
 	if len(conflict) > 0 {
 		c.logger.Warn("skipping tcp service annotation(s) from %v due to conflict: %v", source, conflict)
 	}
-	return tcpHost, nil
+	return tcpHost, tcpLink, nil
 }
 
-func (c *converter) addHost(hostname string, source *annotations.Source, ann map[string]string) *hatypes.Host {
+func (c *converter) addHost(f *frontend, hostname string, source *annotations.Source, annFront, annHost map[string]string, https bool) *host {
 	// TODO build a stronger tracking
-	host := c.haproxy.Hosts().AcquireHost(hostname)
 	c.tracker.TrackNames(source.Type, source.FullName(), convtypes.ResourceHAHostname, hostname)
-	mapper, found := c.hostAnnotations[host]
-	if !found {
-		mapper = c.mapBuilder.NewMapper()
-		c.hostAnnotations[host] = mapper
+	host := f.AcquireHost(hostname)
+
+	if conflicts := c.addFrontAnnotations(f.innerHTTP, source, annFront); len(conflicts) > 0 {
+		c.logger.Warn("skipping frontend annotation(s) from %v due to conflict: %v", source, conflicts)
 	}
-	conflict := mapper.AddAnnotations(source, hatypes.CreateHostPathLink(hostname, "/", hatypes.MatchExact), ann)
-	if len(conflict) > 0 {
-		c.logger.Warn("skipping host annotation(s) from %v due to conflict: %v", source, conflict)
+	if conflicts := c.addHostAnnotations(host.inner, source, annHost); len(conflicts) > 0 {
+		c.logger.Warn("skipping host annotation(s) from %v due to conflict: %v", source, conflicts)
 	}
+
+	if https {
+		host := f.AcquireHostHTTPS(hostname)
+		_ = c.addFrontAnnotations(f.innerHTTPS, source, annFront)
+		_ = c.addHostAnnotations(host.innerHTTPS, source, annHost)
+	}
+
 	return host
 }
 
+func (c *converter) addFrontAnnotations(frontend *hatypes.Frontend, source *annotations.Source, annFront map[string]string) (conflicts []string) {
+	mapperFront, foundFront := c.frontAnnotations[frontend]
+	if !foundFront {
+		mapperFront = c.mapBuilder.NewMapper()
+		c.frontAnnotations[frontend] = mapperFront
+	}
+	return mapperFront.AddAnnotations(source, bareLink().WithHTTPFront(frontend), annFront)
+}
+
+func (c *converter) addHostAnnotations(host *hatypes.Host, source *annotations.Source, annHost map[string]string) (conflicts []string) {
+	mapperHost, foundHost := c.hostAnnotations[host]
+	if !foundHost {
+		mapperHost = c.mapBuilder.NewMapper()
+		c.hostAnnotations[host] = mapperHost
+	}
+	return mapperHost.AddAnnotations(source, bareLink().WithHTTPHost(host), annHost)
+}
+
 func (c *converter) addHeaderMatch(source *annotations.Source, pathLink *hatypes.PathLink, headerMatch string, regex bool) {
 	var headers hatypes.HTTPHeaderMatch
 	for _, header := range utils.LineToSlice(headerMatch) {
@@ -787,13 +930,8 @@ func (c *converter) addHeaderMatch(source *annotations.Source, pathLink *hatypes
 	}
 }
 
-func (c *converter) addBackend(source *annotations.Source, pathLink *hatypes.PathLink, fullSvcName, svcPort string, ann map[string]string) (*hatypes.Backend, error) {
-	return c.addBackendWithClass(source, pathLink, fullSvcName, svcPort, ann, nil)
-}
-
-func (c *converter) addBackendWithClass(source *annotations.Source, pathLink *hatypes.PathLink, fullSvcName, svcPort string, ann map[string]string, ingressClass *networking.IngressClass) (*hatypes.Backend, error) {
+func (c *converter) addBackend(source *annotations.Source, pathLink *hatypes.PathLink, fullSvcName, svcPort string, ann map[string]string, ingressClass *networking.IngressClass) (*hatypes.Backend, error) {
 	// TODO build a stronger tracking
-	svc, err := c.cache.GetService(source.Namespace, fullSvcName)
 	hostname := pathLink.Hostname()
 	ctx := convtypes.ResourceHAHostname
 	if strings.Contains(hostname, ":") {
@@ -807,6 +945,7 @@ func (c *converter) addBackendWithClass(source *annotations.Source, pathLink *ha
 		{Context: convtypes.ResourceService, UniqueName: fullSvcName},
 		{Context: convtypes.ResourceEndpoints, UniqueName: fullSvcName},
 	}, ctx, hostname)
+	svc, err := c.cache.GetService(source.Namespace, fullSvcName)
 	if err != nil {
 		return nil, err
 	}
@@ -843,7 +982,7 @@ func (c *converter) addBackendWithClass(source *annotations.Source, pathLink *ha
 		c.backendAnnotations[backend] = mapper
 	}
 	// Starting with service annotations, giving precedence
-	_, _, svcann := c.readAnnotations(source, svc.Annotations)
+	_, _, _, svcann := c.readAnnotations(source, svc.Annotations)
 	mapper.AddAnnotations(&annotations.Source{
 		Namespace: namespace,
 		Name:      svcName,
@@ -1023,14 +1162,17 @@ func (c *converter) addEndpoints(svc *api.Service, svcPort *api.ServicePort, bac
 	return nil
 }
 
-func (c *converter) readAnnotations(source *annotations.Source, ann map[string]string) (annTCP, annHost, annBack map[string]string) {
+func (c *converter) readAnnotations(source *annotations.Source, ann map[string]string) (annTCP, annFront, annHost, annBack map[string]string) {
 	keys := c.readConfigKeys(source, ann)
 	annTCP = make(map[string]string, len(keys))
+	annFront = make(map[string]string, len(keys))
 	annHost = make(map[string]string, len(keys))
 	annBack = make(map[string]string, len(keys))
 	for key, value := range keys {
 		if _, isTCPAnn := ingtypes.AnnTCP[key]; isTCPAnn {
 			annTCP[key] = value
+		} else if _, isFrontAnn := ingtypes.AnnFront[key]; isFrontAnn {
+			annFront[key] = value
 		} else if _, isHostAnn := ingtypes.AnnHost[key]; isHostAnn {
 			annHost[key] = value
 			// TCP services read both TCP and Host scoped configuration keys
@@ -1045,7 +1187,7 @@ func (c *converter) readAnnotations(source *annotations.Source, ann map[string]s
 			annBack[key] = value
 		}
 	}
-	return annTCP, annHost, annBack
+	return annTCP, annFront, annHost, annBack
 }
 
 func (c *converter) readConfigKey(ann map[string]string, key string) string {
diff --git a/pkg/converters/ingress/types/annotations.go b/pkg/converters/ingress/types/annotations.go
index 2a0c52a7..64d026c9 100644
--- a/pkg/converters/ingress/types/annotations.go
+++ b/pkg/converters/ingress/types/annotations.go
@@ -34,6 +34,40 @@ var (
 	}
 )
 
+// Frontend Annotations
+const (
+	FrontBindFrontingProxy = "bind-fronting-proxy"
+	FrontBindHTTP          = "bind-http"
+	FrontBindHTTPS         = "bind-https"
+	FrontBindIPAddrHTTP    = "bind-ip-addr-http"
+	FrontFrontingProxyPort = "fronting-proxy-port"
+	FrontHTTPPort          = "http-port"
+	FrontHTTPSPort         = "https-port"
+	FrontHTTPStoHTTPPort   = "https-to-http-port"
+	FrontRedirectFromCode  = "redirect-from-code"
+	FrontRedirectToCode    = "redirect-to-code"
+	FrontUseForwardedProto = "use-forwarded-proto"
+	FrontUseProxyProtocol  = "use-proxy-protocol"
+)
+
+var (
+	// AnnFront ...
+	AnnFront = map[string]struct{}{
+		FrontBindFrontingProxy: {},
+		FrontBindHTTP:          {},
+		FrontBindHTTPS:         {},
+		FrontBindIPAddrHTTP:    {},
+		FrontFrontingProxyPort: {},
+		FrontHTTPPort:          {},
+		FrontHTTPSPort:         {},
+		FrontHTTPStoHTTPPort:   {},
+		FrontRedirectFromCode:  {},
+		FrontRedirectToCode:    {},
+		FrontUseForwardedProto: {},
+		FrontUseProxyProtocol:  {},
+	}
+)
+
 // Host Annotations
 const (
 	HostAcmePreferredChain      = "acme-preferred-chain"
@@ -64,28 +98,29 @@ const (
 var (
 	// AnnHost ...
 	AnnHost = map[string]struct{}{
-		HostAcmePreferredChain:     {},
-		HostAppRoot:                {},
-		HostAuthTLSErrorPage:       {},
-		HostAuthTLSSecret:          {},
-		HostAuthTLSStrict:          {},
-		HostAuthTLSVerifyClient:    {},
-		HostCertSigner:             {},
-		HostHTTPResponse421:        {},
-		HostHTTPResponse495:        {},
-		HostHTTPResponse496:        {},
-		HostRedirectFrom:           {},
-		HostRedirectFromRegex:      {},
-		HostServerAlias:            {},
-		HostServerAliasRegex:       {},
-		HostSSLAlwaysAddHTTPS:      {},
-		HostSSLCiphers:             {},
-		HostSSLCipherSuites:        {},
-		HostSSLOptionsHost:         {},
-		HostSSLPassthrough:         {},
-		HostSSLPassthroughHTTPPort: {},
-		HostTLSALPN:                {},
-		HostVarNamespace:           {},
+		HostAcmePreferredChain:      {},
+		HostAppRoot:                 {},
+		HostAuthTLSErrorPage:        {},
+		HostAuthTLSSecret:           {},
+		HostAuthTLSStrict:           {},
+		HostAuthTLSVerifyClient:     {},
+		HostCertSigner:              {},
+		HostHTTPResponse421:         {},
+		HostHTTPResponse495:         {},
+		HostHTTPResponse496:         {},
+		HostRedirectFrom:            {},
+		HostRedirectFromRegex:       {},
+		HostServerAlias:             {},
+		HostServerAliasRegex:        {},
+		HostSSLAlwaysAddHTTPS:       {},
+		HostSSLAlwaysFollowRedirect: {},
+		HostSSLCiphers:              {},
+		HostSSLCipherSuites:         {},
+		HostSSLOptionsHost:          {},
+		HostSSLPassthrough:          {},
+		HostSSLPassthroughHTTPPort:  {},
+		HostTLSALPN:                 {},
+		HostVarNamespace:            {},
 	}
 
 	// AnnDuo is the list of annotations that should be added
diff --git a/pkg/converters/ingress/types/global.go b/pkg/converters/ingress/types/global.go
index 3b0f4162..3f9eb9f3 100644
--- a/pkg/converters/ingress/types/global.go
+++ b/pkg/converters/ingress/types/global.go
@@ -25,11 +25,7 @@ const (
 	GlobalAcmeTermsAgreed              = "acme-terms-agreed"
 	GlobalAuthLogFormat                = "auth-log-format"
 	GlobalAuthProxy                    = "auth-proxy"
-	GlobalBindFrontingProxy            = "bind-fronting-proxy"
-	GlobalBindHTTP                     = "bind-http"
-	GlobalBindHTTPS                    = "bind-https"
 	GlobalBindIPAddrHealthz            = "bind-ip-addr-healthz"
-	GlobalBindIPAddrHTTP               = "bind-ip-addr-http"
 	GlobalBindIPAddrPrometheus         = "bind-ip-addr-prometheus"
 	GlobalBindIPAddrStats              = "bind-ip-addr-stats"
 	GlobalBindIPAddrTCP                = "bind-ip-addr-tcp"
@@ -62,16 +58,12 @@ const (
 	GlobalExternalHasLua               = "external-has-lua"
 	GlobalFCGIEnabledApps              = "fcgi-enabled-apps"
 	GlobalForwardfor                   = "forwardfor"
-	GlobalFrontingProxyPort            = "fronting-proxy-port"
 	GlobalGroupname                    = "groupname"
 	GlobalHealthzPort                  = "healthz-port"
 	GlobalHTTPLogFormat                = "http-log-format"
-	GlobalHTTPPort                     = "http-port"
 	GlobalHTTPResponse404              = "http-response-404"
 	GlobalHTTPResponsePrometheusRoot   = "http-response-prometheus-root"
 	GlobalHTTPSLogFormat               = "https-log-format"
-	GlobalHTTPSPort                    = "https-port"
-	GlobalHTTPStoHTTPPort              = "https-to-http-port"
 	GlobalLoadServerState              = "load-server-state"
 	GlobalMasterExitOnFailure          = "master-exit-on-failure"
 	GlobalMaxConnections               = "max-connections"
@@ -95,8 +87,6 @@ const (
 	GlobalPeersTableGlobal             = "peers-table-global"
 	GlobalPrometheusPort               = "prometheus-port"
 	GlobalRealIPHdr                    = "real-ip-hdr"
-	GlobalRedirectFromCode             = "redirect-from-code"
-	GlobalRedirectToCode               = "redirect-to-code"
 	GlobalSSLDHDefaultMaxSize          = "ssl-dh-default-max-size"
 	GlobalSSLDHParam                   = "ssl-dh-param"
 	GlobalSSLEngine                    = "ssl-engine"
@@ -119,10 +109,8 @@ const (
 	GlobalTimeoutStop                  = "timeout-stop"
 	GlobalUseChroot                    = "use-chroot"
 	GlobalUseCPUMap                    = "use-cpu-map"
-	GlobalUseForwardedProto            = "use-forwarded-proto"
 	GlobalUseHAProxyUser               = "use-haproxy-user"
 	GlobalUseHTX                       = "use-htx"
-	GlobalUseProxyProtocol             = "use-proxy-protocol"
 	GlobalUsername                     = "username"
 	GlobalWorkerMaxReloads             = "worker-max-reloads"
 )
diff --git a/pkg/converters/types/interfaces.go b/pkg/converters/types/interfaces.go
index 83e04bf6..cae6882a 100644
--- a/pkg/converters/types/interfaces.go
+++ b/pkg/converters/types/interfaces.go
@@ -45,6 +45,7 @@ type Cache interface {
 	GetHTTPRouteB1List() ([]*gatewayv1beta1.HTTPRoute, error)
 	GetHTTPRouteList() ([]*gatewayv1.HTTPRoute, error)
 	GetTCPRouteList() ([]*gatewayv1alpha2.TCPRoute, error)
+	GetTLSRouteList() ([]*gatewayv1alpha2.TLSRoute, error)
 	GetService(defaultNamespace, serviceName string) (*api.Service, error)
 	GetConfigMap(configMapName string) (*api.ConfigMap, error)
 	GetNamespace(name string) (*api.Namespace, error)
diff --git a/pkg/converters/types/options.go b/pkg/converters/types/options.go
index d002789d..32107951 100644
--- a/pkg/converters/types/options.go
+++ b/pkg/converters/types/options.go
@@ -44,6 +44,7 @@ type ConverterOptions struct {
 	HasGatewayB1     bool
 	HasGatewayV1     bool
 	HasTCPRouteA2    bool
+	HasTLSRouteA2    bool
 }
 
 // DynamicConfig ...
diff --git a/pkg/haproxy/config.go b/pkg/haproxy/config.go
index 73d21684..8c421c27 100644
--- a/pkg/haproxy/config.go
+++ b/pkg/haproxy/config.go
@@ -18,6 +18,7 @@ package haproxy
 
 import (
 	"fmt"
+	"path"
 	"reflect"
 	"sort"
 	"strings"
@@ -30,16 +31,15 @@ import (
 
 // Config ...
 type Config interface {
-	Frontend() *hatypes.Frontend
+	Frontends() *hatypes.Frontends
 	SyncConfig()
 	WriteTCPServicesMaps() error
-	WriteFrontendMaps() error
+	WriteFrontendsMaps() error
 	WriteBackendMaps() error
 	AcmeData() *hatypes.AcmeData
 	Global() *hatypes.Global
 	TCPBackends() *hatypes.TCPBackends
 	TCPServices() *hatypes.TCPServices
-	Hosts() *hatypes.Hosts
 	Backends() *hatypes.Backends
 	Userlists() *hatypes.Userlists
 	Clear()
@@ -54,8 +54,7 @@ type config struct {
 	// haproxy internal state
 	globalOld   *hatypes.Global
 	global      *hatypes.Global
-	frontend    *hatypes.Frontend
-	hosts       *hatypes.Hosts
+	frontends   *hatypes.Frontends
 	backends    *hatypes.Backends
 	tcpbackends *hatypes.TCPBackends
 	tcpservices *hatypes.TCPServices
@@ -76,8 +75,7 @@ func createConfig(options options) *config {
 		options:     options,
 		acmeData:    &hatypes.AcmeData{},
 		global:      &hatypes.Global{},
-		frontend:    &hatypes.Frontend{},
-		hosts:       hatypes.CreateHosts(),
+		frontends:   &hatypes.Frontends{},
 		backends:    hatypes.CreateBackends(options.shardCount),
 		tcpbackends: hatypes.CreateTCPBackends(),
 		tcpservices: hatypes.CreateTCPServices(),
@@ -85,8 +83,8 @@ func createConfig(options options) *config {
 	}
 }
 
-func (c *config) Frontend() *hatypes.Frontend {
-	return c.frontend
+func (c *config) Frontends() *hatypes.Frontends {
+	return c.frontends
 }
 
 // SyncConfig does final synchronization, just before write
@@ -94,53 +92,50 @@ func (c *config) Frontend() *hatypes.Frontend {
 // during ingress, services and endpoint parsing, but most of
 // them need to start after all objects are parsed.
 func (c *config) SyncConfig() {
-	if c.hosts.HasSSLPassthrough() {
-		// using ssl-passthrough config, so need a `mode tcp`
-		// frontend with `inspect-delay` and `req.ssl_sni`
-		bindName := "_https_socket"
-		c.frontend.Name = "_front_https__local"
-		c.frontend.BindName = bindName
-		c.frontend.BindSocket = fmt.Sprintf("unix@%s/var/run/haproxy/%s.sock", c.global.LocalFSPrefix, bindName)
-		c.frontend.AcceptProxy = true
-	} else {
-		// One single HAProxy's frontend and bind
-		c.frontend.Name = "_front_https"
-		c.frontend.BindName = "_public"
-		c.frontend.BindSocket = c.global.Bind.HTTPSBind
-		c.frontend.AcceptProxy = c.global.Bind.AcceptProxy
+	for _, f := range c.frontends.Items() {
+		c.syncFrontend(f)
 	}
-	for _, host := range c.hosts.ItemsAdd() {
-		if host.SSLPassthrough() {
-			// no action if ssl-passthrough
-			continue
-		}
-		if host.HasTLSAuth() {
-			for _, path := range host.Paths {
-				backend := c.backends.FindBackend(path.Backend.Namespace, path.Backend.Name, path.Backend.Port)
-				if backend != nil {
-					backend.TLS.HasTLSAuth = true
-				}
+	c.syncPeers()
+}
+
+func (c *config) syncFrontend(f *hatypes.Frontend) {
+	if f.IsHTTPS {
+		if f.HasSSLPassthrough() {
+			// using ssl-passthrough config, so need a `mode tcp`
+			// frontend with `inspect-delay` and `req.ssl_sni`
+			if f.Name == "_front_https" {
+				f.TLSProxyName = "_front__tls" // backward compatible name
+			} else {
+				f.TLSProxyName = fmt.Sprintf("_front__tls_%d", f.Port())
 			}
+			f.HTTPSSocket = fmt.Sprintf("unix@%s/var/run/haproxy/%s_socket.sock", c.global.LocalFSPrefix, f.Name)
+			f.HTTPSProxy = true
+		} else {
+			// One single HAProxy's frontend and bind
+			f.TLSProxyName = ""
+			f.HTTPSSocket = ""
+			f.HTTPSProxy = f.AcceptProxy
 		}
-		if c.global.StrictHost && host.FindPath("/", hatypes.MatchBegin) == nil {
-			var back *hatypes.Backend
-			defaultHost := c.hosts.DefaultHost()
+	}
+	for _, host := range f.HostsAdd() {
+		if !host.SSLPassthrough && c.global.StrictHost && host.FindPath("/", hatypes.MatchBegin) == nil {
+			back := c.backends.DefaultBackend
+			defaultHost := f.DefaultHost()
 			if defaultHost != nil {
-				if path := defaultHost.FindPath("/"); len(path) > 0 {
-					hback := path[0].Backend
-					back = c.backends.FindBackend(hback.Namespace, hback.Name, hback.Port)
+				path := defaultHost.FindPath("/")
+				if len(path) > 0 {
+					back = path[0].Backend
 				}
 			}
 			if back == nil {
-				// TODO c.defaultBackend can be nil; create a valid
-				// _error404 backend, remove `if nil` from host.AddPath()
-				// and from `for range host.Paths` on map building.
-				back = c.backends.DefaultBackend
+				back = c.backends.AcquireNotFoundBackend()
 			}
 			host.AddPath(back, "/", hatypes.MatchBegin)
 		}
 	}
+}
 
+func (c *config) syncPeers() {
 	peers := &c.global.Peers
 	if len(peers.Servers) > 0 {
 		// aggregating global and backend tables in a way haproxy.tmpl and peers.lua.tmpl can use.
@@ -194,71 +189,89 @@ func (c *config) WriteTCPServicesMaps() error {
 // used in the frontend. Should be called before write the main
 // config file. This func doesn't change model state, except the
 // link to the frontend maps.
-func (c *config) WriteFrontendMaps() error {
-	if c.frontend.Maps != nil && !c.hosts.Changed() {
-		// TODO Maps!=nil just to preserve the current behavior. Check if this can be removed.
-		// hosts are clean, maps are updated
-		return nil
+func (c *config) WriteFrontendsMaps() error {
+	for _, f := range c.frontends.Items() {
+		if f.HTTPMaps == nil || f.HTTPSMaps == nil || f.HostsChanged() {
+			if err := c.writeFrontendMaps(f); err != nil {
+				return err
+			}
+		}
 	}
+	return nil
+}
+
+func (c *config) writeFrontendMaps(f *hatypes.Frontend) error {
+	// This method is being called once per frontend, having distinct calls for HTTP and HTTPS.
+	// TODO Bear with us, we know this is currently not ideal. Work in progress, this is part
+	// of a major refactor that is decoupling the plain HTTP/s model used up to v0.16.
 	mapBuilder := hatypes.CreateMaps(c.global.MatchOrder)
-	mapsDir := c.options.mapsDir
-	fmaps := &hatypes.FrontendMaps{
-		HTTPHostMap:  mapBuilder.AddMap(mapsDir + "/_front_http_host.map"),
-		HTTPSHostMap: mapBuilder.AddMap(mapsDir + "/_front_https_host.map"),
-		//
-		RedirFromRootMap:  mapBuilder.AddMap(mapsDir + "/_front_redir_fromroot.map"),
-		RedirFromMap:      mapBuilder.AddMap(mapsDir + "/_front_redir_from.map"),
-		RedirRootSSLMap:   mapBuilder.AddMap(mapsDir + "/_front_redir_root_ssl.map"),
-		RedirToMap:        mapBuilder.AddMap(mapsDir + "/_front_redir_to.map"),
-		SSLPassthroughMap: mapBuilder.AddMap(mapsDir + "/_front_sslpassthrough.map"),
-		VarNamespaceMap:   mapBuilder.AddMap(mapsDir + "/_front_namespace.map"),
-		//
-		TLSAuthList:           mapBuilder.AddMap(mapsDir + "/_front_tls_auth.list"),
-		TLSNeedCrtList:        mapBuilder.AddMap(mapsDir + "/_front_tls_needcrt.list"),
-		TLSInvalidCrtPagesMap: mapBuilder.AddMap(mapsDir + "/_front_tls_invalidcrt_pages.map"),
-		TLSMissingCrtPagesMap: mapBuilder.AddMap(mapsDir + "/_front_tls_missingcrt_pages.map"),
-		//
-		DefaultHostMap: mapBuilder.AddMap(mapsDir + "/_front_defaulthost.map"),
+	mapsFilenamePrefix := path.Join(c.options.mapsDir, f.Name)
+	buildCommonMaps := func(m *hatypes.FrontendCommonMaps) {
+		*m = hatypes.FrontendCommonMaps{
+			DefaultHostMap:   mapBuilder.AddMap(mapsFilenamePrefix + "_defaulthost.map"),
+			RedirFromRootMap: mapBuilder.AddMap(mapsFilenamePrefix + "_redir_fromroot.map"),
+			RedirFromMap:     mapBuilder.AddMap(mapsFilenamePrefix + "_redir_from.map"),
+			RedirToMap:       mapBuilder.AddMap(mapsFilenamePrefix + "_redir_to.map"),
+			VarNamespaceMap:  mapBuilder.AddMap(mapsFilenamePrefix + "_namespace.map"),
+		}
 	}
-	// TODO crtList* to be removed after implement a template to the crt list
-	c.frontend.CrtListFile = mapsDir + "/_front_bind_crt.list"
-	var crtListItems []*hatypes.HostsMapEntry
-	crtListItems = append(crtListItems, &hatypes.HostsMapEntry{Key: c.frontend.DefaultCrtFile + " !*"})
-	hasVarNamespace := c.hosts.HasVarNamespace()
-	defaultHost := c.hosts.DefaultHost()
-	if defaultHost != nil && !defaultHost.SSLPassthrough() {
+	var httpMaps *hatypes.FrontendHTTPMaps
+	var httpsMaps *hatypes.FrontendHTTPSMaps
+	var commonMaps *hatypes.FrontendCommonMaps
+	if f.IsHTTPS {
+		httpsMaps = &hatypes.FrontendHTTPSMaps{
+			HTTPSHostMap:          mapBuilder.AddMap(mapsFilenamePrefix + "_host.map"),
+			SSLPassthroughMap:     mapBuilder.AddMap(mapsFilenamePrefix + "_sslpassthrough.map"),
+			TLSAuthList:           mapBuilder.AddMap(mapsFilenamePrefix + "_tls_auth.list"),
+			TLSNeedCrtList:        mapBuilder.AddMap(mapsFilenamePrefix + "_tls_needcrt.list"),
+			TLSInvalidCrtPagesMap: mapBuilder.AddMap(mapsFilenamePrefix + "_tls_invalidcrt_pages.map"),
+			TLSMissingCrtPagesMap: mapBuilder.AddMap(mapsFilenamePrefix + "_tls_missingcrt_pages.map"),
+		}
+		commonMaps = &httpsMaps.FrontendCommonMaps
+		buildCommonMaps(commonMaps)
+	} else {
+		httpMaps = &hatypes.FrontendHTTPMaps{
+			HTTPHostMap:     mapBuilder.AddMap(mapsFilenamePrefix + "_host.map"),
+			RedirRootSSLMap: mapBuilder.AddMap(mapsFilenamePrefix + "_redir_root_ssl.map"),
+		}
+		commonMaps = &httpMaps.FrontendCommonMaps
+		buildCommonMaps(commonMaps)
+	}
+	hasVarNamespace := f.HasVarNamespace()
+	defaultHost := f.DefaultHost()
+	if defaultHost != nil && !defaultHost.SSLPassthrough {
 		for _, path := range defaultHost.Paths {
 			// using DefaultHost ID as hostname, see types.maps.go/buildMapKey()
-			fmaps.DefaultHostMap.AddHostnamePathMapping(hatypes.DefaultHost, path, path.Backend.ID)
+			commonMaps.DefaultHostMap.AddHostnamePathMapping(hatypes.DefaultHost, path, path.Backend.ID)
 		}
 	}
-	for _, host := range c.hosts.BuildSortedItems() {
+	defaultCrtFile := c.frontends.DefaultCrtFile
+	var crtListItems []*hatypes.HostsMapEntry
+	if f.IsHTTPS {
+		// TODO crtList* to be removed after implement a template to the crt list
+		f.CrtListFile = mapsFilenamePrefix + "_bind_crt.list"
+		crtListItems = append(crtListItems, &hatypes.HostsMapEntry{Key: defaultCrtFile + " !*"})
+	}
+	for _, host := range f.BuildSortedHosts() {
 		for _, path := range host.Paths {
-			backendID := path.Backend.ID
 			// IMPLEMENT check if host.Alias.AliasName was already used as a hostname
-			if backendID != "" {
-				if host.SSLPassthrough() {
+			if path.Backend != nil {
+				backendID := path.Backend.ID
+				if f.IsHTTPS && host.SSLPassthrough {
 					// no ssl offload, cannot inspect incoming path, so tracking root only
 					if path.Path() == "/" {
-						fmaps.SSLPassthroughMap.AddHostnameMapping(host.Hostname, backendID)
-						// the backend of the root path is the ssl-passthrough, which speaks TLS,
-						// so we cannot use it in the HTTP map. Change to the configured HTTP port
-						// in that server (if declared) or use a redirect otherwise.
-						backendID = host.HTTPPassthroughBackend
-						if backendID == "" {
-							backendID = "_redirect_https"
-						}
+						httpsMaps.SSLPassthroughMap.AddHostnameMapping(host.Hostname, backendID)
 					}
-				} else if host.HasTLS() {
-					// ssl offload in place
-					fmaps.HTTPSHostMap.AddHostnamePathMapping(host.Hostname, path, backendID)
-					fmaps.HTTPSHostMap.AddAliasPathMapping(host.Alias, path, backendID)
+				} else if f.IsHTTPS {
+					httpsMaps.HTTPSHostMap.AddHostnamePathMapping(host.Hostname, path, backendID)
+					httpsMaps.HTTPSHostMap.AddAliasPathMapping(host.Alias, path, backendID)
+				} else {
+					httpMaps.HTTPHostMap.AddHostnamePathMapping(host.Hostname, path, backendID)
+					httpMaps.HTTPHostMap.AddAliasPathMapping(host.Alias, path, backendID)
 				}
-				fmaps.HTTPHostMap.AddHostnamePathMapping(host.Hostname, path, backendID)
-				fmaps.HTTPHostMap.AddAliasPathMapping(host.Alias, path, backendID)
 			} else if path.RedirTo != "" {
-				fmaps.RedirToMap.AddHostnamePathMapping(host.Hostname, path, path.RedirTo)
-				fmaps.RedirToMap.AddAliasPathMapping(host.Alias, path, path.RedirTo)
+				commonMaps.RedirToMap.AddHostnamePathMapping(host.Hostname, path, path.RedirTo)
+				commonMaps.RedirToMap.AddAliasPathMapping(host.Alias, path, path.RedirTo)
 			}
 			if hasVarNamespace {
 				// add "-" on missing paths to avoid overlap
@@ -268,30 +281,30 @@ func (c *config) WriteFrontendMaps() error {
 				} else {
 					ns = "-"
 				}
-				fmaps.VarNamespaceMap.AddHostnamePathMapping(host.Hostname, path, ns)
+				commonMaps.VarNamespaceMap.AddHostnamePathMapping(host.Hostname, path, ns)
 			}
 		}
-		if host.SSLPassthrough() {
+		if host.SSLPassthrough {
 			continue
 		}
 		if host.Redirect.RedirectHost != "" {
-			fmaps.RedirFromMap.AddHostnameMapping(host.Redirect.RedirectHost, host.Hostname)
+			commonMaps.RedirFromMap.AddHostnameMapping(host.Redirect.RedirectHost, host.Hostname)
 		}
 		if host.Redirect.RedirectHostRegex != "" {
-			fmaps.RedirFromMap.AddHostnameMappingRegex(host.Redirect.RedirectHostRegex, host.Hostname)
+			commonMaps.RedirFromMap.AddHostnameMappingRegex(host.Redirect.RedirectHostRegex, host.Hostname)
 		}
-		if host.HasTLSAuth() {
+		if f.IsHTTPS && host.HasTLSAuth() {
 			if host.TLS.CAVerify != hatypes.CAVerifySkipCheck {
-				fmaps.TLSAuthList.AddHostnameMapping(host.Hostname, "")
+				httpsMaps.TLSAuthList.AddHostnameMapping(host.Hostname, "")
 			}
 			if !host.TLS.CAVerifyOptional() {
-				fmaps.TLSNeedCrtList.AddHostnameMapping(host.Hostname, "")
+				httpsMaps.TLSNeedCrtList.AddHostnameMapping(host.Hostname, "")
 			}
 			page := host.TLS.CAErrorPage
 			if page != "" {
-				fmaps.TLSInvalidCrtPagesMap.AddHostnameMapping(host.Hostname, page)
+				httpsMaps.TLSInvalidCrtPagesMap.AddHostnameMapping(host.Hostname, page)
 				if !host.TLS.CAVerifyOptional() {
-					fmaps.TLSMissingCrtPagesMap.AddHostnameMapping(host.Hostname, page)
+					httpsMaps.TLSMissingCrtPagesMap.AddHostnameMapping(host.Hostname, page)
 				}
 			}
 		}
@@ -303,30 +316,30 @@ func (c *config) WriteFrontendMaps() error {
 			redirectssl := func() bool {
 				redir := c.global.SSL.SSLRedirect
 				for _, path := range host.FindPath("/") {
-					if backend := c.backends.Items()[path.Backend.ID]; backend != nil {
-						if bpath := backend.FindBackendPath(path.Link); bpath != nil {
-							redir = bpath.SSLRedirect
-							if !path.Link.ComposeMatch() {
-								// give precedence for root path without method, header or cookie matching
-								return redir
-							}
-						}
+					// any root path `/` is fine ...
+					redir = path.SSLRedirect
+					if !path.Link.IsComposeMatch() {
+						// ... but gives precedence for a root path `/` without method, header or cookie matching
+						return redir
 					}
 				}
 				return redir
 			}
-			if redirectssl() {
-				fmaps.RedirRootSSLMap.AddHostnameMapping(host.Hostname, "")
+			if !f.IsHTTPS && redirectssl() {
+				httpMaps.RedirRootSSLMap.AddHostnameMapping(host.Hostname, "")
 			}
-			fmaps.RedirFromRootMap.AddHostnameMapping(host.Hostname, host.RootRedirect)
+			commonMaps.RedirFromRootMap.AddHostnameMapping(host.Hostname, host.RootRedirect)
+		}
+		if !f.IsHTTPS {
+			continue
 		}
 		//
 		tls := host.TLS
 		crtFile := tls.TLSFilename
 		if crtFile == "" {
-			crtFile = c.frontend.DefaultCrtFile
+			crtFile = defaultCrtFile
 		}
-		if crtFile != c.frontend.DefaultCrtFile ||
+		if crtFile != defaultCrtFile ||
 			tls.ALPN != "" ||
 			tls.CAFilename != "" ||
 			tls.Ciphers != "" ||
@@ -366,13 +379,16 @@ func (c *config) WriteFrontendMaps() error {
 			crtListItems = append(crtListItems, &hatypes.HostsMapEntry{Key: crtListEntry})
 		}
 	}
-	if err := c.options.mapsTemplate.WriteOutput(crtListItems, c.frontend.CrtListFile); err != nil {
-		return err
+	if f.IsHTTPS {
+		if err := c.options.mapsTemplate.WriteOutput(crtListItems, f.CrtListFile); err != nil {
+			return err
+		}
 	}
 	if err := writeMaps(mapBuilder, c.options.mapsTemplate); err != nil {
 		return err
 	}
-	c.frontend.Maps = fmaps
+	f.HTTPMaps = httpMaps
+	f.HTTPSMaps = httpsMaps
 	return nil
 }
 
@@ -388,29 +404,22 @@ func (c *config) WriteBackendMaps() error {
 	}
 	mapBuilder := hatypes.CreateMaps(c.global.MatchOrder)
 	for _, backend := range c.backends.ItemsAdd() {
-		if backend.NeedACL() {
-			mapsPrefix := c.options.mapsDir + "/_back_" + backend.ID
-			pathsMap := mapBuilder.AddMap(mapsPrefix + "_idpath.map")
-			pathsDefaultHostMap := mapBuilder.AddMap(mapsPrefix + "_idpathdef.map")
-			for _, path := range backend.Paths {
-				// IMPLEMENT add HostPath link into the backend path
-				h := c.hosts.FindHost(path.Hostname())
-				if h == nil {
-					continue
-				}
-				p := h.FindPathWithLink(path.Link)
-				if p == nil {
-					continue
-				}
+		if !backend.NeedACL() {
+			continue
+		}
+		mapsFilenamePrefix := path.Join(c.options.mapsDir, "_back_"+backend.ID)
+		for _, pathsMap := range backend.PathsMaps() {
+			frontend := pathsMap.Frontends[0]
+			pathsMap.ReqMap = mapBuilder.AddMap(mapsFilenamePrefix + frontend + "_req.map")
+			pathsMap.DefMap = mapBuilder.AddMap(mapsFilenamePrefix + frontend + "_def.map")
+			for _, path := range pathsMap.Paths {
 				if path.IsDefaultHost() {
-					// using DefaultHost ID as hostname, see types.maps.go/buildMapKey()
-					pathsDefaultHostMap.AddHostnamePathMapping(hatypes.DefaultHost, p, path.ID)
+					// using DefaultHost ID as hostname, see types/maps.go/buildMapKey()
+					pathsMap.DefMap.AddHostnamePathMapping(hatypes.DefaultHost, path, path.ID)
 				} else {
-					pathsMap.AddHostnamePathMapping(path.Hostname(), p, path.ID)
+					pathsMap.ReqMap.AddHostnamePathMapping(path.Hostname(), path, path.ID)
 				}
 			}
-			backend.PathsMap = pathsMap
-			backend.PathsDefaultHostMap = pathsDefaultHostMap
 		}
 	}
 	return writeMaps(mapBuilder, c.options.mapsTemplate)
@@ -419,8 +428,7 @@ func (c *config) WriteBackendMaps() error {
 func writeMaps(maps *hatypes.HostsMaps, template *template.Config) error {
 	for _, hmap := range maps.Items {
 		for _, matchFile := range hmap.MatchFiles() {
-			filename := matchFile.Filename()
-			if err := template.WriteOutput(matchFile.Values(), filename); err != nil {
+			if err := template.WriteOutput(matchFile.Values(), matchFile.Filename()); err != nil {
 				return err
 			}
 		}
@@ -444,10 +452,6 @@ func (c *config) TCPServices() *hatypes.TCPServices {
 	return c.tcpservices
 }
 
-func (c *config) Hosts() *hatypes.Hosts {
-	return c.hosts
-}
-
 func (c *config) Backends() *hatypes.Backends {
 	return c.backends
 }
@@ -468,7 +472,7 @@ func (c *config) Clear() {
 }
 
 func (c *config) Shrink() {
-	c.hosts.Shrink()
+	c.frontends.Shrink()
 	c.backends.Shrink()
 }
 
@@ -481,8 +485,7 @@ func (c *config) Commit() {
 		}
 		c.globalOld = &globalOld
 	}
-	c.frontend.Commit()
-	c.hosts.Commit()
+	c.frontends.Commit()
 	c.backends.Commit()
 	c.tcpbackends.Commit()
 	c.tcpservices.Commit()
diff --git a/pkg/haproxy/dynupdate.go b/pkg/haproxy/dynupdate.go
index 68b2e76f..d4c841ff 100644
--- a/pkg/haproxy/dynupdate.go
+++ b/pkg/haproxy/dynupdate.go
@@ -88,14 +88,16 @@ func (d *dynUpdater) checkConfigChange() bool {
 	if d.config.tcpservices.Changed() {
 		diff = append(diff, "tcp-services")
 	}
-	if d.config.frontend.Changed() {
-		diff = append(diff, "frontend")
+	if d.config.frontends.Changed() {
+		diff = append(diff, "frontends")
 	}
 	if d.config.userlists.Changed() {
 		diff = append(diff, "userlists")
 	}
-	if !d.frontendUpdated() {
-		diff = append(diff, "hosts")
+	for _, f := range d.config.frontends.Items() {
+		if !d.frontendUpdated(f) {
+			diff = append(diff, fmt.Sprintf("hosts (%s)", f.Name))
+		}
 	}
 	if !d.backendUpdated() {
 		diff = append(diff, "backends")
@@ -107,14 +109,14 @@ func (d *dynUpdater) checkConfigChange() bool {
 	return true
 }
 
-func (d *dynUpdater) frontendUpdated() bool {
+func (d *dynUpdater) frontendUpdated(f *hatypes.Frontend) bool {
 	updated := true
 
-	hosts := make(map[string]*hostPair, len(d.config.hosts.ItemsDel()))
-	for _, host := range d.config.hosts.ItemsDel() {
+	hosts := make(map[string]*hostPair, len(f.HostsDel()))
+	for _, host := range f.HostsDel() {
 		hosts[host.Hostname] = &hostPair{old: host}
 	}
-	for _, host := range d.config.hosts.ItemsAdd() {
+	for _, host := range f.HostsAdd() {
 		id := host.Hostname
 		h, found := hosts[id]
 		if !found {
diff --git a/pkg/haproxy/instance.go b/pkg/haproxy/instance.go
index 96ea5726..2213f049 100644
--- a/pkg/haproxy/instance.go
+++ b/pkg/haproxy/instance.go
@@ -312,7 +312,7 @@ func (i *instance) HAProxyUpdate(timer *utils.Timer) error {
 		i.metrics.IncUpdateNoop()
 		return fmt.Errorf("error building tcp services maps: %w", err)
 	}
-	if err := i.config.WriteFrontendMaps(); err != nil {
+	if err := i.config.WriteFrontendsMaps(); err != nil {
 		i.metrics.IncUpdateNoop()
 		return fmt.Errorf("error building frontend maps: %w", err)
 	}
@@ -441,22 +441,24 @@ func (i *instance) Shutdown() {
 }
 
 func (i *instance) logChanged() {
-	hostsAdd := i.config.Hosts().ItemsAdd()
-	if len(hostsAdd) < 100 {
-		hostsDel := i.config.Hosts().ItemsDel()
-		hosts := make([]string, 0, len(hostsAdd))
-		for host := range hostsAdd {
-			hosts = append(hosts, host)
-		}
-		for host := range hostsDel {
-			if _, found := hostsAdd[host]; !found {
+	for _, f := range i.config.Frontends().Items() {
+		hostsAdd := f.HostsAdd()
+		if len(hostsAdd) < 100 {
+			hostsDel := f.HostsDel()
+			hosts := make([]string, 0, len(hostsAdd))
+			for host := range hostsAdd {
 				hosts = append(hosts, host)
 			}
+			for host := range hostsDel {
+				if _, found := hostsAdd[host]; !found {
+					hosts = append(hosts, host)
+				}
+			}
+			sort.Strings(hosts)
+			i.logger.InfoV(2, "updating %d host(s) on frontend %s: %v", len(hosts), f.Name, hosts)
+		} else {
+			i.logger.InfoV(2, "updating %d hosts on frontend %s", len(hostsAdd), f.Name)
 		}
-		sort.Strings(hosts)
-		i.logger.InfoV(2, "updating %d host(s): %v", len(hosts), hosts)
-	} else {
-		i.logger.InfoV(2, "updating %d hosts", len(hostsAdd))
 	}
 	backsAdd := i.config.Backends().ItemsAdd()
 	if len(backsAdd) < 100 {
@@ -616,7 +618,7 @@ func (i *instance) buildCustomHTTPResponses() (responsesList []httpResponseOverr
 	for _, beResponse := range i.config.Backends().BuildHTTPResponses() {
 		addAll(&beResponse)
 	}
-	for _, hostResponse := range i.config.Hosts().BuildHTTPResponses() {
+	for _, hostResponse := range i.config.Frontends().BuildHTTPResponses() {
 		addAll(&hostResponse)
 	}
 	addAll(&i.config.Global().CustomHTTPResponses)
@@ -641,24 +643,25 @@ func (i *instance) updateSuccessful(success bool) {
 }
 
 func (i *instance) updateCertExpiring() {
-	hostsAdd := i.config.Hosts().ItemsAdd()
-	hostsDel := i.config.Hosts().ItemsDel()
-	if !i.config.Hosts().HasCommit() {
+	if !i.config.Frontends().HasCommit() {
 		// TODO the time between this reset and finishing to repopulate the gauge would lead
 		// to incomplete data scraped by Prometheus. This however happens only when a full parsing
 		// happens - edit globals, edit default crt, invalid data coming from lister events
 		i.metrics.ClearCertExpire()
 	}
-	for hostname, oldHost := range hostsDel {
-		if oldHost.TLS.HasTLS() {
+	for _, f := range i.config.Frontends().Items() {
+		if !f.IsHTTPS {
+			continue
+		}
+		hostsAdd := f.HostsAdd()
+		hostsDel := f.HostsDel()
+		for hostname, oldHost := range hostsDel {
 			curHost, found := hostsAdd[hostname]
 			if !found || oldHost.TLS.TLSCommonName != curHost.TLS.TLSCommonName {
 				i.metrics.SetCertExpireDate(hostname, oldHost.TLS.TLSCommonName, nil)
 			}
 		}
-	}
-	for hostname, curHost := range hostsAdd {
-		if curHost.TLS.HasTLS() {
+		for hostname, curHost := range hostsAdd {
 			oldHost, found := hostsDel[hostname]
 			if !found || oldHost.TLS.TLSCommonName != curHost.TLS.TLSCommonName || oldHost.TLS.TLSNotAfter != curHost.TLS.TLSNotAfter {
 				i.metrics.SetCertExpireDate(hostname, curHost.TLS.TLSCommonName, &curHost.TLS.TLSNotAfter)
diff --git a/pkg/haproxy/types/backend.go b/pkg/haproxy/types/backend.go
index b0c3fb2b..20e5ae99 100644
--- a/pkg/haproxy/types/backend.go
+++ b/pkg/haproxy/types/backend.go
@@ -20,6 +20,7 @@ import (
 	"fmt"
 	"math/rand"
 	"reflect"
+	"slices"
 	"sort"
 	"strings"
 )
@@ -144,30 +145,10 @@ func (b *Backend) CookieAffinity() bool {
 	return !b.ModeTCP && b.Cookie.Name != "" && !b.Cookie.Dynamic
 }
 
-// FindBackendPath ...
-func (b *Backend) FindBackendPath(link *PathLink) *BackendPath {
-	// IMPLEMENT change to a map
-	for _, p := range b.Paths {
-		if p.Link.Equals(link) {
-			return p
-		}
-	}
-	return nil
-}
-
-// AddBackendPath ...
-func (b *Backend) AddBackendPath(link *PathLink) *BackendPath {
-	backendPath := b.FindBackendPath(link)
-	if backendPath != nil {
-		return backendPath
-	}
-	backendPath = &BackendPath{
-		ID:   fmt.Sprintf("path%02d", len(b.Paths)+1),
-		Link: link,
-	}
-	b.Paths = append(b.Paths, backendPath)
+func (b *Backend) AddPath(path *Path) {
+	path.ID = fmt.Sprintf("path%02d", len(b.Paths)+1)
+	b.Paths = append(b.Paths, path)
 	sortPaths(b.Paths, false)
-	return backendPath
 }
 
 // Hostnames ...
@@ -188,19 +169,22 @@ func (b *Backend) Hostnames() []string {
 	return hosts
 }
 
-func sortPaths(paths []*BackendPath, pathReverse bool) {
-	// Ascending order of hostnames and reverse order (if pathReverse) of paths within the same hostname
+func sortPaths(paths []*Path, pathReverse bool) {
+	// Ascending order of frontend+hostnames and reverse order (if pathReverse) of paths within the same hostname
 	// reverse order in order to avoid overlap of sub-paths
 	sort.Slice(paths, func(i, j int) bool {
 		l1 := paths[i].Link
 		l2 := paths[j].Link
-		if l1.hostname == l2.hostname {
-			if pathReverse {
-				return l1.path > l2.path
+		if l1.frontend == l2.frontend {
+			if l1.hostname == l2.hostname {
+				if pathReverse {
+					return l1.path > l2.path
+				}
+				return l1.path < l2.path
 			}
-			return l1.path < l2.path
+			return l1.hostname < l2.hostname
 		}
-		return l1.hostname < l2.hostname
+		return l1.frontend < l2.frontend
 	})
 }
 
@@ -234,6 +218,15 @@ func (b *Backend) HasModsec() bool {
 	return false
 }
 
+func (b *Backend) HasHTTPRequests() bool {
+	for _, path := range b.Paths {
+		if !path.Host.frontend.IsHTTPS {
+			return true
+		}
+	}
+	return false
+}
+
 // HasSSLRedirect ...
 func (b *Backend) HasSSLRedirect() bool {
 	for _, path := range b.Paths {
@@ -245,7 +238,7 @@ func (b *Backend) HasSSLRedirect() bool {
 }
 
 // HasSSLRedirectPaths ...
-func (b *Backend) HasSSLRedirectPaths(paths []*BackendPath) bool {
+func (b *Backend) HasSSLRedirectPaths(paths []*Path) bool {
 	for _, path := range paths {
 		if path.SSLRedirect {
 			return true
@@ -254,16 +247,66 @@ func (b *Backend) HasSSLRedirectPaths(paths []*BackendPath) bool {
 	return false
 }
 
+func (b *Backend) HasTLSAuth() bool {
+	for _, path := range b.Paths {
+		if path.Host.HasTLSAuth() {
+			return true
+		}
+	}
+	return false
+}
+
+type Has int
+
+// these same consts are also used in haproxy.tmpl, change there if changing here.
+const (
+	HasNone Has = iota
+	HasSome
+	HasOnly
+)
+
+func (b *Backend) HasFrontingProxy() Has {
+	return b.hasInPath(func(path *Path) bool { return path.Host.frontend.IsFrontingProxy })
+}
+
+func (b *Backend) HasFrontingUseProto() Has {
+	return b.hasInPath(func(path *Path) bool { return path.Host.frontend.IsFrontingUseProto })
+}
+
+func (b *Backend) hasInPath(has func(path *Path) bool) Has {
+	var count int
+	for i, path := range b.Paths {
+		if has(path) {
+			count++
+		}
+		if i > 0 && count > 0 && i >= count {
+			return HasSome
+		}
+	}
+	if count == 0 {
+		return HasNone
+	}
+	return HasOnly
+}
+
+func (b *Backend) PathConfigs() map[string]*BackendPathConfig {
+	if b.pathsConfigs == nil {
+		b.pathsConfigs = b.createPathConfig()
+	}
+	return b.pathsConfigs
+}
+
 // PathConfig ...
 func (b *Backend) PathConfig(attr string) *BackendPathConfig {
-	b.ensurePathConfig(attr)
-	return b.pathConfig[attr]
+	if _, found := b.PathConfigs()[attr]; !found {
+		panic(fmt.Errorf("field does not exist: %s", attr))
+	}
+	return b.pathsConfigs[attr]
 }
 
 // NeedACL ...
 func (b *Backend) NeedACL() bool {
-	b.ensurePathConfig("")
-	for _, path := range b.pathConfig {
+	for _, path := range b.PathConfigs() {
 		if path.NeedACL() {
 			return true
 		}
@@ -271,45 +314,74 @@ func (b *Backend) NeedACL() bool {
 	return false
 }
 
-func (b *Backend) ensurePathConfig(attr string) {
-	if b.pathConfig == nil {
-		b.pathConfig = b.createPathConfig()
-	}
-	if attr == "" {
-		return
+func (b *Backend) NeedFrontendACL() bool {
+	return len(b.PathsMaps()) > 1
+}
+
+func (b *Backend) PathsMaps() []*BackendPathsMaps {
+	if b.pathsMaps == nil {
+		b.pathsMaps = b.createPathsMaps()
 	}
-	if _, found := b.pathConfig[attr]; !found {
-		panic(fmt.Errorf("field does not exist: %s", attr))
+	return b.pathsMaps
+}
+
+func (b *Backend) createPathsMaps() []*BackendPathsMaps {
+	var pathsMaps []*BackendPathsMaps
+	for _, path := range b.Paths {
+		frontend := path.FrontendName()
+		i := slices.IndexFunc(pathsMaps, func(b *BackendPathsMaps) bool { return slices.Contains(b.Frontends, frontend) })
+		if i < 0 {
+			i = len(pathsMaps)
+			pathsMaps = append(pathsMaps, &BackendPathsMaps{
+				Frontends: []string{frontend},
+			})
+		}
+		backMap := pathsMaps[i]
+		backMap.Paths = append(backMap.Paths, path)
 	}
+	// Deduplicate maps with the exact same paths, a common pattern on models configured via Ingress API.
+	// This deduplication reduces the size of the backend configuration.
+	pathsMaps = slices.CompactFunc(pathsMaps, func(m1, m2 *BackendPathsMaps) bool {
+		if slices.EqualFunc(m1.Paths, m2.Paths, func(p1, p2 *Path) bool { return p1.Equals(p2) }) {
+			m2.Frontends = append(m2.Frontends, m1.Frontends...)
+			return true
+		}
+		return false
+	})
+	sort.Slice(pathsMaps, func(i, j int) bool {
+		return pathsMaps[i].Frontends[0] < pathsMaps[j].Frontends[0]
+	})
+	return pathsMaps
 }
 
 func (b *Backend) createPathConfig() map[string]*BackendPathConfig {
 	pathconfig := make(map[string]*BackendPathConfig, len(b.Paths))
-	pathType := reflect.TypeOf(BackendPath{})
-	for i := 0; i < pathType.NumField(); i++ {
-		name := pathType.Field(i).Name
-		// filter out core fields
-		if name != "ID" && name != "Link" && name != "Host" {
-			pathconfig[name] = &BackendPathConfig{}
+	pathType := reflect.TypeOf(Path{})
+	for i := range pathType.NumField() {
+		field := pathType.Field(i)
+		if field.Tag.Get("class") != "core" {
+			pathconfig[field.Name] = &BackendPathConfig{}
 		}
 	}
-	for _, path := range b.Paths {
-		pathValue := reflect.ValueOf(*path)
-		for name, config := range pathconfig {
-			newconfig := pathValue.FieldByName(name).Interface()
-			hasconfig := false
-			for _, item := range config.items {
-				if reflect.DeepEqual(item.config, newconfig) {
-					item.paths = append(item.paths, path)
-					hasconfig = true
-					break
+	for _, pathsMap := range b.PathsMaps() {
+		for _, path := range pathsMap.Paths {
+			pathValue := reflect.ValueOf(*path)
+			for name, config := range pathconfig {
+				newconfig := pathValue.FieldByName(name).Interface()
+				hasconfig := false
+				for _, item := range config.items {
+					if reflect.DeepEqual(item.config, newconfig) {
+						item.paths = append(item.paths, path)
+						hasconfig = true
+						break
+					}
+				}
+				if !hasconfig {
+					config.items = append(config.items, &BackendPathItem{
+						paths:  []*Path{path},
+						config: newconfig,
+					})
 				}
-			}
-			if !hasconfig {
-				config.items = append(config.items, &BackendPathItem{
-					paths:  []*BackendPath{path},
-					config: newconfig,
-				})
 			}
 		}
 	}
@@ -331,7 +403,7 @@ func (b *BackendPathConfig) Items() []interface{} {
 }
 
 // Paths ...
-func (b *BackendPathConfig) Paths(index int) []*BackendPath {
+func (b *BackendPathConfig) Paths(index int) []*Path {
 	return b.items[index].paths
 }
 
@@ -382,23 +454,43 @@ func (ep *Endpoint) IsEmpty() bool {
 	return ep.IP == "127.0.0.1"
 }
 
+func (p *Path) Equals(other *Path) bool {
+	vthis := reflect.ValueOf(*p)
+	vother := reflect.ValueOf(*other)
+	pathType := reflect.TypeOf(Path{})
+	for i := range pathType.NumField() {
+		if pathType.Field(i).Tag.Get("class") == "core" {
+			continue
+		}
+		if !reflect.DeepEqual(vthis.Field(i).Interface(), vother.Field(i).Interface()) {
+			return false
+		}
+	}
+	return true
+}
+
+// FrontendName ...
+func (p *Path) FrontendName() string {
+	return p.Link.frontend
+}
+
 // Hostname ...
-func (p *BackendPath) Hostname() string {
+func (p *Path) Hostname() string {
 	return p.Link.hostname
 }
 
 // IsDefaultHost ...
-func (p *BackendPath) IsDefaultHost() bool {
+func (p *Path) IsDefaultHost() bool {
 	return p.Link.IsDefaultHost()
 }
 
 // Path ...
-func (p *BackendPath) Path() string {
+func (p *Path) Path() string {
 	return p.Link.path
 }
 
 // Match ...
-func (p *BackendPath) Match() MatchType {
+func (p *Path) Match() MatchType {
 	return p.Link.match
 }
 
@@ -413,7 +505,7 @@ func (ep *TCPEndpoint) String() string {
 }
 
 // String ...
-func (p *BackendPath) String() string {
+func (p *Path) String() string {
 	return fmt.Sprintf("%+v", *p)
 }
 
diff --git a/pkg/haproxy/types/backends.go b/pkg/haproxy/types/backends.go
index c35e1b70..17c621ba 100644
--- a/pkg/haproxy/types/backends.go
+++ b/pkg/haproxy/types/backends.go
@@ -31,7 +31,7 @@ func CreateBackends(shardCount int) *Backends {
 	for i := range shards {
 		shards[i] = map[string]*Backend{}
 	}
-	return &Backends{
+	backends := &Backends{
 		items:         map[string]*Backend{},
 		itemsAdd:      map[string]*Backend{},
 		itemsDel:      map[string]*Backend{},
@@ -39,6 +39,8 @@ func CreateBackends(shardCount int) *Backends {
 		shards:        shards,
 		changedShards: map[int]bool{},
 	}
+	backends.DefaultBackend = backends.AcquireNotFoundBackend()
+	return backends
 }
 
 // Items ...
@@ -112,8 +114,8 @@ func backendsMatch(back1, back2 *Backend) bool {
 		return true
 	}
 	b1copy := *back1
-	b1copy.PathsMap = back2.PathsMap
-	b1copy.pathConfig = back2.pathConfig
+	b1copy.pathsMaps = back2.pathsMaps
+	b1copy.pathsConfigs = back2.pathsConfigs
 	b1copy.Endpoints = back2.Endpoints
 	if !reflect.DeepEqual(&b1copy, back2) {
 		return false
@@ -228,7 +230,7 @@ func (b *Backends) BuildUsedAuthBackends() map[string]bool {
 	usedNames := map[string]bool{}
 	for _, backend := range b.items {
 		for _, path := range backend.Paths {
-			name := path.AuthExternal.AuthBackendName
+			name := path.AuthExtBack.AuthBackendName
 			if name != "" {
 				usedNames[name] = true
 			}
@@ -242,6 +244,7 @@ func (b *Backends) BuildHTTPResponses() (responses []HTTPResponses) {
 	// Cache? Need to handle partial update. Leave it simple? Make at least some performance tests.
 	for _, backend := range b.items {
 		res := &backend.CustomHTTPResponses
+		res.ID = backend.ID
 		if len(res.HAProxy) > 0 || len(res.Lua) > 0 {
 			responses = append(responses, HTTPResponses{
 				ID:      res.ID,
@@ -273,6 +276,32 @@ func (b *Backends) AcquireBackend(namespace, name, port string) *Backend {
 	return backend
 }
 
+func (b *Backends) HasBackend(name string) bool {
+	switch name {
+	case "_redirect_https":
+		return b.httpsRedir != nil
+	case "_error404":
+		return b.error404 != nil
+	}
+	return false
+}
+
+// AcquireHTTPSRedirectBackend ...
+func (b *Backends) AcquireRedirectHTTPSBackend() *Backend {
+	if b.httpsRedir == nil {
+		b.httpsRedir = createBackend(0, "_redirect_https", "", "") // this is hardcoded in the template, outside the backend list
+	}
+	return b.httpsRedir
+}
+
+// AcquireNotFoundBackend ...
+func (b *Backends) AcquireNotFoundBackend() *Backend {
+	if b.error404 == nil {
+		b.error404 = createBackend(0, "_error404", "", "") // this is also hardcoded in the template
+	}
+	return b.error404
+}
+
 // AcquireAuthBackend ...
 func (b *Backends) AcquireAuthBackend(ipList []string, port int, hostname string) *Backend {
 	sort.Strings(ipList)
@@ -367,5 +396,8 @@ func createBackend(shards int, namespace, name, port string) *Backend {
 }
 
 func buildID(namespace, name, port string) string {
+	if name == "" && port == "" {
+		return namespace
+	}
 	return namespace + "_" + name + "_" + port
 }
diff --git a/pkg/haproxy/types/frontend.go b/pkg/haproxy/types/frontend.go
index 637161d5..d83f35e6 100644
--- a/pkg/haproxy/types/frontend.go
+++ b/pkg/haproxy/types/frontend.go
@@ -18,12 +18,320 @@ package types
 
 import (
 	"fmt"
+	"reflect"
+	"slices"
 	"sort"
 )
 
+func (f *Frontends) AcquireFrontend(port int32, isHTTPS bool) *Frontend {
+	var has bool
+	for _, frontend := range f.items {
+		if frontend.port == port {
+			return frontend
+		}
+		if !has && frontend.IsHTTPS == isHTTPS {
+			has = true
+		}
+	}
+	var name string
+	if isHTTPS {
+		name = "_front_https"
+	} else {
+		name = "_front_http"
+	}
+	if has {
+		name = fmt.Sprintf("%s_%d", name, port)
+	}
+	frontend := &Frontend{
+		Name:     name,
+		Bind:     fmt.Sprintf(":%d", port),
+		IsHTTPS:  isHTTPS,
+		port:     port,
+		hosts:    map[string]*Host{},
+		hostsAdd: map[string]*Host{},
+		hostsDel: map[string]*Host{},
+	}
+	f.items = append(f.items, frontend)
+	return frontend
+}
+
+func (f *Frontends) FindFrontend(port int32) *Frontend {
+	for _, frontend := range f.items {
+		if frontend.port == port {
+			return frontend
+		}
+	}
+	return nil
+}
+
+func (f *Frontends) Items() []*Frontend {
+	return f.items
+}
+
+func (f *Frontends) Commit() {
+	for _, frontend := range f.items {
+		frontend.Commit()
+	}
+	f.items = slices.DeleteFunc(f.items, func(f *Frontend) bool { return len(f.hosts) == 0 })
+	f.AuthProxy.changed = false
+}
+
+func (f *Frontends) HasCommit() bool {
+	for _, frontend := range f.items {
+		if frontend.HasCommit() {
+			return true
+		}
+	}
+	return false
+}
+
+func (f *Frontends) Changed() bool {
+	return f.AuthProxy.changed
+}
+
+func (f *Frontends) HasHTTPResponses() bool {
+	for _, f := range f.items {
+		for _, host := range f.hosts {
+			res := &host.CustomHTTPResponses
+			if len(res.HAProxy) > 0 || len(res.Lua) > 0 {
+				return true
+			}
+		}
+	}
+	return false
+}
+
+func (f *Frontends) HasSomeFrontingProxy() bool {
+	total := len(f.items)
+	var count int
+	for _, front := range f.items {
+		if front.IsFrontingProxy {
+			count++
+		}
+	}
+	return count > 0 && count < total
+}
+
+func (f *Frontends) HasSomeFrontingUseProto() bool {
+	total := len(f.items)
+	var count int
+	for _, front := range f.items {
+		if front.IsFrontingProxy && front.IsFrontingUseProto {
+			count++
+		}
+	}
+	return count > 0 && count < total
+}
+
+func (f *Frontends) BuildHTTPResponses() (responses []HTTPResponses) {
+	for _, f := range f.items {
+		for _, host := range f.hosts {
+			res := &host.CustomHTTPResponses
+			res.ID = fmt.Sprintf("%s--%s", f.Name, host.Hostname)
+			if len(res.HAProxy) > 0 || len(res.Lua) > 0 {
+				responses = append(responses, HTTPResponses{
+					ID:      res.ID,
+					HAProxy: res.HAProxy,
+					Lua:     res.Lua,
+				})
+			}
+		}
+	}
+	// predictable response
+	sort.Slice(responses, func(i, j int) bool {
+		return responses[i].ID < responses[j].ID
+	})
+	return responses
+}
+
+func (f *Frontends) RemoveAllHosts(hostnames []string) {
+	for _, frontend := range f.items {
+		frontend.RemoveAllHosts(hostnames)
+	}
+}
+
+func (f *Frontends) Shrink() {
+	for _, frontend := range f.items {
+		frontend.ShrinkHosts()
+	}
+}
+
+// AcquireHost ...
+func (f *Frontend) AcquireHost(hostname string) *Host {
+	if host := f.FindHost(hostname); host != nil {
+		return host
+	}
+	host := f.createHost(hostname)
+	f.hosts[hostname] = host
+	f.hostsAdd[hostname] = host
+	return host
+}
+
+// FindHost ...
+func (f *Frontend) FindHost(hostname string) *Host {
+	return f.hosts[hostname]
+}
+
+// RemoveAllHosts ...
+func (f *Frontend) RemoveAllHosts(hostnames []string) {
+	for _, hostname := range hostnames {
+		if item, found := f.hosts[hostname]; found {
+			f.hostsDel[hostname] = item
+			delete(f.hosts, hostname)
+		}
+	}
+}
+
+func (f *Frontend) RemoveAllLinks(pathlinks ...*PathLink) {
+	for _, link := range pathlinks {
+		h := f.FindHost(link.hostname)
+		if h != nil {
+			h.Paths = slices.DeleteFunc(h.Paths, func(p *Path) bool { return p.Link.Equals(link) })
+			if len(h.Paths) == 0 {
+				f.RemoveAllHosts([]string{link.hostname})
+			}
+		}
+	}
+}
+
+// FindTargetRedirect ...
+func (f *Frontend) FindTargetRedirect(redirfrom string, isRegex bool) *Host {
+	if redirfrom == "" {
+		return nil
+	}
+	// TODO this'd be somewhat expensive on full parsing,
+	// tens of thousands of ingress and most of them using redirect
+	if isRegex {
+		for _, host := range f.hosts {
+			if host.Redirect.RedirectHostRegex == redirfrom {
+				return host
+			}
+		}
+		return nil
+	}
+	for _, host := range f.hosts {
+		if host.Redirect.RedirectHost == redirfrom {
+			return host
+		}
+	}
+	return nil
+}
+
+// ShrinkHosts removes matching added and deleted hosts from the changing hashmap
+// tracker that has the same content. A matching added+deleted pair means
+// that a hostname was reparsed but its content wasn't changed.
+func (f *Frontend) ShrinkHosts() {
+	for name, del := range f.hostsDel {
+		if add, found := f.hostsAdd[name]; found {
+			if reflect.DeepEqual(add, del) {
+				f.hosts[name] = del
+				delete(f.hostsAdd, name)
+				delete(f.hostsDel, name)
+			}
+		}
+	}
+}
+
+// Commit ...
+func (f *Frontend) Commit() {
+	f.hostsAdd = map[string]*Host{}
+	f.hostsDel = map[string]*Host{}
+	f.hasCommit = true
+}
+
+// HasCommit ...
+func (f *Frontend) HasCommit() bool {
+	return f.hasCommit
+}
+
+// HostsChanged ...
+func (f *Frontend) HostsChanged() bool {
+	return len(f.hostsAdd) > 0 || len(f.hostsDel) > 0
+}
+
+func (f *Frontend) createHost(hostname string) *Host {
+	return &Host{
+		Hostname: hostname,
+		frontend: f,
+	}
+}
+
+// BuildSortedHosts ...
+func (f *Frontend) BuildSortedHosts() []*Host {
+	items := make([]*Host, len(f.hosts))
+	var i int
+	for hostname, item := range f.hosts {
+		if hostname != DefaultHost {
+			items[i] = item
+			i++
+		}
+	}
+	items = items[:i]
+	if len(items) == 0 {
+		return nil
+	}
+	sort.Slice(items, func(i, j int) bool {
+		return items[i].Hostname < items[j].Hostname
+	})
+	return items
+}
+
+func (f *Frontend) Port() int32 {
+	return f.port
+}
+
+// Hosts ...
+func (f *Frontend) Hosts() map[string]*Host {
+	return f.hosts
+}
+
+// HostsAdd ...
+func (f *Frontend) HostsAdd() map[string]*Host {
+	return f.hostsAdd
+}
+
+// HostsDel ...
+func (f *Frontend) HostsDel() map[string]*Host {
+	return f.hostsDel
+}
+
+// DefaultHost ...
+func (f *Frontend) DefaultHost() *Host {
+	return f.hosts[DefaultHost]
+}
+
+// HasTLSAuth ...
+func (f *Frontend) HasTLSAuth() bool {
+	for _, host := range f.hosts {
+		if host.HasTLSAuth() {
+			return true
+		}
+	}
+	return false
+}
+
+// HasSSLPassthrough ...
+func (f *Frontend) HasSSLPassthrough() bool {
+	for _, host := range f.hosts {
+		if host.SSLPassthrough {
+			return true
+		}
+	}
+	return false
+}
+
+// HasVarNamespace ...
+func (f *Frontend) HasVarNamespace() bool {
+	for _, host := range f.hosts {
+		if host.VarNamespace {
+			return true
+		}
+	}
+	return false
+}
+
 // AcquireAuthBackendName ...
-func (f *Frontend) AcquireAuthBackendName(backend BackendID) (authBackendName string, err error) {
-	proxy := &f.AuthProxy
+func (proxy *AuthProxy) AcquireAuthBackendName(backend BackendID) (authBackendName string, err error) {
 	freePort := proxy.RangeStart
 	for _, bind := range proxy.BindList {
 		if bind.Backend == backend {
@@ -47,13 +355,13 @@ func (f *Frontend) AcquireAuthBackendName(backend BackendID) (authBackendName st
 	sort.Slice(proxy.BindList, func(i, j int) bool {
 		return proxy.BindList[i].LocalPort < proxy.BindList[j].LocalPort
 	})
-	f.changed = true
+	proxy.changed = true
 	return bind.AuthBackendName, nil
 }
 
 // RemoveAuthBackendExcept ...
-func (f *Frontend) RemoveAuthBackendExcept(used map[string]bool) {
-	bindList := f.AuthProxy.BindList
+func (proxy *AuthProxy) RemoveAuthBackendExcept(used map[string]bool) {
+	bindList := proxy.BindList
 	var i int
 	for _, bind := range bindList {
 		if used[bind.AuthBackendName] {
@@ -61,12 +369,12 @@ func (f *Frontend) RemoveAuthBackendExcept(used map[string]bool) {
 			i++
 		}
 	}
-	f.AuthProxy.BindList = bindList[:i]
+	proxy.BindList = bindList[:i]
 }
 
 // RemoveAuthBackendByTarget ...
-func (f *Frontend) RemoveAuthBackendByTarget(backends []string) {
-	bindList := f.AuthProxy.BindList
+func (proxy *AuthProxy) RemoveAuthBackendByTarget(backends []string) {
+	bindList := proxy.BindList
 	var i int
 	for _, bind := range bindList {
 		if !hasBackend(backends, bind.Backend.String()) {
@@ -74,7 +382,7 @@ func (f *Frontend) RemoveAuthBackendByTarget(backends []string) {
 			i++
 		}
 	}
-	f.AuthProxy.BindList = bindList[:i]
+	proxy.BindList = bindList[:i]
 }
 
 func hasBackend(backends []string, backend string) bool {
@@ -86,16 +394,6 @@ func hasBackend(backends []string, backend string) bool {
 	return false
 }
 
-// Changed ...
-func (f *Frontend) Changed() bool {
-	return f.changed
-}
-
-// Commit ...
-func (f *Frontend) Commit() {
-	f.changed = false
-}
-
 // String ...
 func (f *Frontend) String() string {
 	return fmt.Sprintf("%+v", *f)
diff --git a/pkg/haproxy/types/global.go b/pkg/haproxy/types/global.go
index 8fdd70db..4a8ccf39 100644
--- a/pkg/haproxy/types/global.go
+++ b/pkg/haproxy/types/global.go
@@ -141,13 +141,3 @@ func (dns *DNSResolver) String() string {
 func (dns *DNSNameserver) String() string {
 	return fmt.Sprintf("%+v", *dns)
 }
-
-// ShareHTTPPort ...
-func (b GlobalBindConfig) ShareHTTPPort() bool {
-	return b.HasFrontingProxy() && b.HTTPBind == b.FrontingBind
-}
-
-// HasFrontingProxy ...
-func (b GlobalBindConfig) HasFrontingProxy() bool {
-	return b.FrontingBind != ""
-}
diff --git a/pkg/haproxy/types/host.go b/pkg/haproxy/types/host.go
index aac24fac..ba0b1fdd 100644
--- a/pkg/haproxy/types/host.go
+++ b/pkg/haproxy/types/host.go
@@ -18,221 +18,13 @@ package types
 
 import (
 	"fmt"
-	"reflect"
 	"sort"
-)
-
-// CreateHosts ...
-func CreateHosts() *Hosts {
-	return &Hosts{
-		items:    map[string]*Host{},
-		itemsAdd: map[string]*Host{},
-		itemsDel: map[string]*Host{},
-	}
-}
-
-// CreatePathLink ...
-func CreatePathLink(path string, match MatchType) *PathLink {
-	return CreateHostPathLink("", path, match)
-}
-
-// CreateHostPathLink ...
-func CreateHostPathLink(hostname, path string, match MatchType) *PathLink {
-	pathlink := &PathLink{
-		hostname: hostname,
-		path:     path,
-		match:    match,
-	}
-	pathlink.updatehash()
-	return pathlink
-}
-
-// AcquireHost ...
-func (h *Hosts) AcquireHost(hostname string) *Host {
-	if host := h.FindHost(hostname); host != nil {
-		return host
-	}
-	host := h.createHost(hostname)
-	h.items[hostname] = host
-	h.itemsAdd[hostname] = host
-	return host
-}
-
-// FindHost ...
-func (h *Hosts) FindHost(hostname string) *Host {
-	return h.items[hostname]
-}
-
-// RemoveAll ...
-func (h *Hosts) RemoveAll(hostnames []string) {
-	for _, hostname := range hostnames {
-		if item, found := h.items[hostname]; found {
-			h.releaseHost(item)
-			h.itemsDel[hostname] = item
-			delete(h.items, hostname)
-		}
-	}
-}
-
-// FindTargetRedirect ...
-func (h *Hosts) FindTargetRedirect(redirfrom string, isRegex bool) *Host {
-	if redirfrom == "" {
-		return nil
-	}
-	// TODO this'd be somewhat expensive on full parsing,
-	// tens of thousands of ingress and most of them using redirect
-	if isRegex {
-		for _, host := range h.items {
-			if host.Redirect.RedirectHostRegex == redirfrom {
-				return host
-			}
-		}
-		return nil
-	}
-	for _, host := range h.items {
-		if host.Redirect.RedirectHost == redirfrom {
-			return host
-		}
-	}
-	return nil
-}
-
-// Shrink removes matching added and deleted hosts from the changing hashmap
-// tracker that has the same content. A matching added+deleted pair means
-// that a hostname was reparsed but its content wasn't changed.
-func (h *Hosts) Shrink() {
-	for name, del := range h.itemsDel {
-		if add, found := h.itemsAdd[name]; found {
-			if reflect.DeepEqual(add, del) {
-				h.items[name] = del
-				delete(h.itemsAdd, name)
-				delete(h.itemsDel, name)
-			}
-		}
-	}
-}
-
-// Commit ...
-func (h *Hosts) Commit() {
-	h.itemsAdd = map[string]*Host{}
-	h.itemsDel = map[string]*Host{}
-	h.hasCommit = true
-}
-
-// HasCommit ...
-func (h *Hosts) HasCommit() bool {
-	return h.hasCommit
-}
-
-// Changed ...
-func (h *Hosts) Changed() bool {
-	return len(h.itemsAdd) > 0 || len(h.itemsDel) > 0
-}
-
-func (h *Hosts) createHost(hostname string) *Host {
-	return &Host{
-		Hostname: hostname,
-		hosts:    h,
-		TLS: HostTLSConfig{
-			// TODO revisit instance_test to allow change this default value to `false`
-			UseDefaultCrt: true,
-		},
-	}
-}
-
-// BuildSortedItems ...
-func (h *Hosts) BuildSortedItems() []*Host {
-	items := make([]*Host, len(h.items))
-	var i int
-	for hostname, item := range h.items {
-		if hostname != DefaultHost {
-			items[i] = item
-			i++
-		}
-	}
-	items = items[:i]
-	sort.Slice(items, func(i, j int) bool {
-		return items[i].Hostname < items[j].Hostname
-	})
-	if len(items) == 0 {
-		return nil
-	}
-	return items
-}
-
-// Items ...
-func (h *Hosts) Items() map[string]*Host {
-	return h.items
-}
-
-// ItemsAdd ...
-func (h *Hosts) ItemsAdd() map[string]*Host {
-	return h.itemsAdd
-}
-
-// ItemsDel ...
-func (h *Hosts) ItemsDel() map[string]*Host {
-	return h.itemsDel
-}
-
-// DefaultHost ...
-func (h *Hosts) DefaultHost() *Host {
-	return h.items[DefaultHost]
-}
-
-// releaseHost does a reverse update on the Hosts state
-// due to the removal of a Host item
-func (h *Hosts) releaseHost(host *Host) {
-	if host.sslPassthrough {
-		h.sslPassthroughCount--
-	}
-}
-
-// HasTLSAuth ...
-func (h *Hosts) HasTLSAuth() bool {
-	for _, host := range h.items {
-		if host.TLS.CAFilename != "" {
-			return true
-		}
-	}
-	return false
-}
-
-// HasSSLPassthrough ...
-func (h *Hosts) HasSSLPassthrough() bool {
-	return h.sslPassthroughCount > 0
-}
-
-// HasVarNamespace ...
-func (h *Hosts) HasVarNamespace() bool {
-	for _, host := range h.items {
-		if host.VarNamespace {
-			return true
-		}
-	}
-	return false
-}
 
-func (h *Hosts) BuildHTTPResponses() (responses []HTTPResponses) {
-	for _, host := range h.items {
-		res := &host.CustomHTTPResponses
-		if len(res.HAProxy) > 0 || len(res.Lua) > 0 {
-			responses = append(responses, HTTPResponses{
-				ID:      res.ID,
-				HAProxy: res.HAProxy,
-				Lua:     res.Lua,
-			})
-		}
-	}
-	// predictable response
-	sort.Slice(responses, func(i, j int) bool {
-		return responses[i].ID < responses[j].ID
-	})
-	return responses
-}
+	"k8s.io/utils/ptr"
+)
 
 // FindPath ...
-func (h *Host) FindPath(path string, match ...MatchType) (paths []*HostPath) {
+func (h *Host) FindPath(path string, match ...MatchType) (paths []*Path) {
 	for _, p := range h.Paths {
 		if p.Link.path == path && p.Link.headers == nil && p.hasMatch(match) {
 			paths = append(paths, p)
@@ -242,7 +34,7 @@ func (h *Host) FindPath(path string, match ...MatchType) (paths []*HostPath) {
 }
 
 // FindPathWithLink ...
-func (h *Host) FindPathWithLink(link *PathLink) (path *HostPath) {
+func (h *Host) FindPathWithLink(link *PathLink) (path *Path) {
 	for _, p := range h.Paths {
 		if p.Link.Equals(link) {
 			return p
@@ -252,66 +44,41 @@ func (h *Host) FindPathWithLink(link *PathLink) (path *HostPath) {
 }
 
 // AddPath ...
-func (h *Host) AddPath(backend *Backend, path string, match MatchType) *HostPath {
-	return h.addPath(path, match, backend, "")
-}
-
-// AddLink ...
-func (h *Host) AddLink(backend *Backend, link *PathLink) *PathLink {
-	newlink := *link
-	newlink.WithHostname(h.Hostname)
-	_ = h.addLink(backend, &newlink, "")
-	return &newlink
+func (h *Host) AddPath(backend *Backend, path string, match MatchType) *Path {
+	return h.addLink(backend, CreatePathLink(path, match), "")
 }
 
-// AddLinkRedirect ...
-func (h *Host) AddLinkRedirect(link *PathLink, redirTo string) *PathLink {
-	newlink := *link
-	newlink.WithHostname(h.Hostname)
-	_ = h.addLink(nil, &newlink, redirTo)
-	return &newlink
+// AddRedirect ...
+func (h *Host) AddRedirect(path string, match MatchType, redirTo string) *Path {
+	return h.addLink(nil, CreatePathLink(path, match), redirTo)
 }
 
-// AddRedirect ...
-func (h *Host) AddRedirect(path string, match MatchType, redirTo string) {
-	_ = h.addPath(path, match, nil, redirTo)
+// AddLink ...
+func (h *Host) AddLink(backend *Backend, link *PathLink) *Path {
+	return h.addLink(backend, link, "")
 }
 
-type hostResolver struct {
-	useDefaultCrt  *bool
-	followRedirect *bool
-	crtFilename    *string
+// AddLinkRedirect ...
+func (h *Host) AddLinkRedirect(link *PathLink, redirTo string) *Path {
+	return h.addLink(nil, link, redirTo)
 }
 
-func (h *Host) addPath(path string, match MatchType, backend *Backend, redirTo string) *HostPath {
-	link := CreateHostPathLink(h.Hostname, path, match)
-	return h.addLink(backend, link, redirTo)
+func (h *Host) IsHTTPS() bool {
+	return h.frontend.IsHTTPS
 }
 
-func (h *Host) addLink(backend *Backend, link *PathLink, redirTo string) *HostPath {
-	var hback HostBackend
-	if backend != nil {
-		hback = HostBackend{
-			ID:        backend.ID,
-			Namespace: backend.Namespace,
-			Name:      backend.Name,
-			Port:      backend.Port,
-		}
-		bpath := backend.AddBackendPath(link)
-		bpath.Host = &hostResolver{
-			useDefaultCrt:  &h.TLS.UseDefaultCrt,
-			followRedirect: &h.TLS.FollowRedirect,
-			crtFilename:    &h.TLS.TLSFilename,
-		}
-	} else if redirTo == "" {
-		hback = HostBackend{ID: "_error404"}
-	}
-	path := &HostPath{
+func (h *Host) addLink(backend *Backend, link *PathLink, redirTo string) *Path {
+	link = ptr.To(*link).WithHTTPHost(h)
+	path := &Path{
 		Link:    link,
-		Backend: hback,
+		Host:    h,
 		RedirTo: redirTo,
 		order:   len(h.Paths),
 	}
+	if backend != nil {
+		backend.AddPath(path)
+		path.Backend = backend
+	}
 	h.Paths = append(h.Paths, path)
 	// reverse order in order to avoid overlap of sub-paths
 	sort.Slice(h.Paths, func(i, j int) bool {
@@ -326,7 +93,7 @@ func (h *Host) addLink(backend *Backend, link *PathLink, redirTo string) *HostPa
 }
 
 // RemovePath ...
-func (h *Host) RemovePath(hpath *HostPath) {
+func (h *Host) RemovePath(hpath *Path) {
 	var j int
 	for i := range h.Paths {
 		if j < i {
@@ -341,60 +108,17 @@ func (h *Host) RemovePath(hpath *HostPath) {
 	}
 }
 
-// HasTLS ...
-func (h *Host) HasTLS() bool {
-	return h.TLS.UseDefaultCrt || h.TLS.TLSHash != ""
-}
-
-func (h *hostResolver) UseTLS() bool {
-	// whether the ingress resource has the `tls:` entry for the host
-	hasTLSEntry := *h.crtFilename != ""
-
-	// whether the user has globally or locally configured auto TLS for the host
-	autoTLSEnabled := *h.useDefaultCrt && *h.followRedirect
-
-	return hasTLSEntry || autoTLSEnabled
-}
-
 // HasTLSAuth ...
 func (h *Host) HasTLSAuth() bool {
 	return h.TLS.CAHash != ""
 }
 
-// SSLPassthrough ...
-func (h *Host) SSLPassthrough() bool {
-	return h.sslPassthrough
-}
-
-// SetSSLPassthrough ...
-func (h *Host) SetSSLPassthrough(value bool) {
-	if h.sslPassthrough == value {
-		return
-	}
-	if value {
-		h.hosts.sslPassthroughCount++
-	} else {
-		h.hosts.sslPassthroughCount--
-	}
-	h.sslPassthrough = value
-}
-
-// Path ...
-func (h *HostPath) Path() string {
-	return h.Link.path
-}
-
 // Headers ...
-func (h *HostPath) Headers() HTTPHeaderMatch {
+func (h *Path) Headers() HTTPHeaderMatch {
 	return h.Link.headers
 }
 
-// Match ...
-func (h *HostPath) Match() MatchType {
-	return h.Link.match
-}
-
-func (h *HostPath) hasMatch(match []MatchType) bool {
+func (h *Path) hasMatch(match []MatchType) bool {
 	if len(match) == 0 {
 		return true
 	}
@@ -406,93 +130,6 @@ func (h *HostPath) hasMatch(match []MatchType) bool {
 	return false
 }
 
-func (l *PathLink) updatehash() {
-	hash := l.hostname + "\n" + l.path + "\n" + string(l.match)
-	for _, h := range l.headers {
-		hash += "\n" + "h:" + h.Name + ":" + h.Value
-		if h.Regex {
-			hash += "(regex)"
-		}
-	}
-	l.hash = PathLinkHash(hash)
-}
-
-// Hash ...
-func (l *PathLink) Hash() PathLinkHash {
-	return l.hash
-}
-
-// Equals ...
-func (l *PathLink) Equals(other *PathLink) bool {
-	if l == nil || other == nil {
-		return l == other
-	}
-	return l.hash == other.hash
-}
-
-// ComposeMatch returns true if the pathLink has composing match,
-// by adding method, header or cookie match.
-func (l *PathLink) ComposeMatch() bool {
-	return len(l.headers) > 0
-}
-
-// WithHostname ...
-func (l *PathLink) WithHostname(hostname string) *PathLink {
-	l.hostname = hostname
-	l.updatehash()
-	return l
-}
-
-// WithHeadersMatch ...
-func (l *PathLink) WithHeadersMatch(headers HTTPHeaderMatch) *PathLink {
-	l.headers = headers
-	l.updatehash()
-	return l
-}
-
-// AddHeadersMatch ...
-func (l *PathLink) AddHeadersMatch(headers HTTPHeaderMatch) *PathLink {
-	l.headers = append(l.headers, headers...)
-	l.updatehash()
-	return l
-}
-
-// Hostname ...
-func (l *PathLink) Hostname() string {
-	return l.hostname
-}
-
-// IsEmpty ...
-func (l *PathLink) IsEmpty() bool {
-	return l.hostname == "" && l.path == ""
-}
-
-// IsDefaultHost ...
-func (l *PathLink) IsDefaultHost() bool {
-	return l.hostname == DefaultHost
-}
-
-// Less ...
-func (l *PathLink) Less(other *PathLink, reversePath bool) bool {
-	if l.hostname == other.hostname {
-		if reversePath {
-			return l.path > other.path
-		}
-		return l.path < other.path
-	}
-	return l.hostname < other.hostname
-}
-
-// HAMatch ...
-func (l *PathLink) HAMatch() string {
-	return haMatchMethod(l.match)
-}
-
-// Key ...
-func (l *PathLink) Key() string {
-	return buildMapKey(l.match, l.hostname, l.path)
-}
-
 // String ...
 func (h *Host) String() string {
 	return fmt.Sprintf("%+v", *h)
diff --git a/pkg/haproxy/types/maps.go b/pkg/haproxy/types/maps.go
index 0b0143de..a522de71 100644
--- a/pkg/haproxy/types/maps.go
+++ b/pkg/haproxy/types/maps.go
@@ -65,24 +65,24 @@ func (hm *HostsMap) addHostnameMappingMatch(hostname, target string, match Match
 }
 
 // AddHostnamePathMapping ...
-func (hm *HostsMap) AddHostnamePathMapping(hostname string, hostPath *HostPath, target string) {
+func (hm *HostsMap) AddHostnamePathMapping(hostname string, path *Path, target string) {
 	hostname, hasWildcard := convertWildcardToRegex(hostname)
-	path := hostPath.Path()
-	match := hostPath.Match()
+	strpath := path.Path()
+	match := path.Match()
 	// TODO paths of a wildcard hostname will always have less precedence
 	// despite the match type because the whole hostname+path will fill a
 	// MatchRegex map, which has the lesser precedence in the template.
 	if hasWildcard {
-		path = convertPathToRegex(hostPath)
+		strpath = convertPathToRegex(path)
 		match = MatchRegex
 	} else if match == MatchRegex {
 		hostname = "^" + regexp.QuoteMeta(hostname) + "$"
 	}
-	hm.addTarget(hostname, path, hostPath.Link.headers, hostPath.order, target, match)
+	hm.addTarget(hostname, strpath, path.Link.headers, path.order, target, match)
 }
 
 // AddAliasPathMapping ...
-func (hm *HostsMap) AddAliasPathMapping(alias HostAliasConfig, path *HostPath, target string) {
+func (hm *HostsMap) AddAliasPathMapping(alias HostAliasConfig, path *Path, target string) {
 	if alias.AliasName != "" {
 		hm.AddHostnamePathMapping(alias.AliasName, path, target)
 	}
@@ -106,21 +106,21 @@ func convertWildcardToRegex(hostname string) (h string, hasWildcard bool) {
 // regex has an implicit starting `^` char due to ingress path
 // validation - paths need to start with a slash. There is no
 // implicit `$`, so regex behaves pretty much like `begin`.
-func convertPathToRegex(hostPath *HostPath) string {
-	path := hostPath.Path()
-	switch hostPath.Match() {
+func convertPathToRegex(path *Path) string {
+	strpath := path.Path()
+	switch path.Match() {
 	case MatchBegin:
-		return regexp.QuoteMeta(path)
+		return regexp.QuoteMeta(strpath)
 	case MatchExact:
-		return regexp.QuoteMeta(path) + "$"
+		return regexp.QuoteMeta(strpath) + "$"
 	case MatchPrefix:
-		path = regexp.QuoteMeta(path)
-		if strings.HasSuffix(path, "/") {
-			return path
+		strpath = regexp.QuoteMeta(strpath)
+		if strings.HasSuffix(strpath, "/") {
+			return strpath
 		}
-		return path + "(/.*)?"
+		return strpath + "(/.*)?"
 	case MatchRegex:
-		return path
+		return strpath
 	}
 	panic("unsupported match type")
 }
diff --git a/pkg/haproxy/types/pathlink.go b/pkg/haproxy/types/pathlink.go
new file mode 100644
index 00000000..dd72f683
--- /dev/null
+++ b/pkg/haproxy/types/pathlink.go
@@ -0,0 +1,134 @@
+/*
+Copyright 2025 The HAProxy Ingress Controller Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package types
+
+import (
+	"fmt"
+)
+
+func CreatePathLink(path string, match MatchType) *PathLink {
+	pathlink := &PathLink{
+		hostname: DefaultHost,
+		path:     path,
+		match:    match,
+	}
+	pathlink.updatehash()
+	return pathlink
+}
+
+func (l *PathLink) updatehash() {
+	hash := l.frontend + "\n" + l.hostname + "\n" + l.path + "\n" + string(l.match)
+	for _, h := range l.headers {
+		hash += "\n" + "h:" + h.Name + ":" + h.Value
+		if h.Regex {
+			hash += "(regex)"
+		}
+	}
+	l.hash = PathLinkHash(hash)
+}
+
+// Hash ...
+func (l *PathLink) Hash() PathLinkHash {
+	return l.hash
+}
+
+// Equals ...
+func (l *PathLink) Equals(other *PathLink) bool {
+	if l == nil || other == nil {
+		return l == other
+	}
+	return l.hash == other.hash
+}
+
+// IsComposeMatch returns true if the pathLink has composing match,
+// by adding method, header or cookie match.
+func (l *PathLink) IsComposeMatch() bool {
+	return len(l.headers) > 0
+}
+
+// WithHTTPFront ...
+func (l *PathLink) WithHTTPFront(front *Frontend) *PathLink {
+	l.frontend = front.Name
+	l.updatehash()
+	return l
+}
+
+// WithHTTPHost ...
+func (l *PathLink) WithHTTPHost(host *Host) *PathLink {
+	l.frontend = host.frontend.Name
+	l.hostname = host.Hostname
+	l.updatehash()
+	return l
+}
+
+// WithTCPHost ...
+func (l *PathLink) WithTCPHost(tcphost *TCPServiceHost) *PathLink {
+	l.frontend = fmt.Sprintf("_tcp_%d", tcphost.tcpport.port)
+	l.hostname = fmt.Sprintf("%s:%d", tcphost.hostname, tcphost.tcpport.port)
+	l.updatehash()
+	return l
+}
+
+// WithHeadersMatch ...
+func (l *PathLink) WithHeadersMatch(headers HTTPHeaderMatch) *PathLink {
+	l.headers = headers
+	l.updatehash()
+	return l
+}
+
+// AddHeadersMatch ...
+func (l *PathLink) AddHeadersMatch(headers HTTPHeaderMatch) *PathLink {
+	l.headers = append(l.headers, headers...)
+	l.updatehash()
+	return l
+}
+
+// Hostname ...
+func (l *PathLink) Hostname() string {
+	return l.hostname
+}
+
+// IsEmpty ...
+func (l *PathLink) IsEmpty() bool {
+	return l.hostname == "" && l.path == ""
+}
+
+// IsDefaultHost ...
+func (l *PathLink) IsDefaultHost() bool {
+	return l.hostname == DefaultHost
+}
+
+// Less ...
+func (l *PathLink) Less(other *PathLink, reversePath bool) bool {
+	if l.hostname == other.hostname {
+		if reversePath {
+			return l.path > other.path
+		}
+		return l.path < other.path
+	}
+	return l.hostname < other.hostname
+}
+
+// HAMatch ...
+func (l *PathLink) HAMatch() string {
+	return haMatchMethod(l.match)
+}
+
+// Key ...
+func (l *PathLink) Key() string {
+	return buildMapKey(l.match, l.hostname, l.path)
+}
diff --git a/pkg/haproxy/types/tcpservices.go b/pkg/haproxy/types/tcpservices.go
index 2836b00a..c6fe1f9c 100644
--- a/pkg/haproxy/types/tcpservices.go
+++ b/pkg/haproxy/types/tcpservices.go
@@ -32,18 +32,16 @@ func CreateTCPServices() *TCPServices {
 // AcquireTCPService ...
 func (s *TCPServices) AcquireTCPService(service string) (*TCPServicePort, *TCPServiceHost) {
 	hostname, port := splitService(service)
-	tcpPort := s.acquireTCPPort(port)
-	tcpHost, found := tcpPort.acquireHost(hostname)
-	if !found {
-		s.changed = true
-	}
+	tcpPort := s.AcquireTCPPort(port)
+	tcpHost := tcpPort.AcquireTLSHost(hostname)
 	return tcpPort, tcpHost
 }
 
-func (s *TCPServices) acquireTCPPort(port int) *TCPServicePort {
+func (s *TCPServices) AcquireTCPPort(port int) *TCPServicePort {
 	tcpPort, found := s.items[port]
 	if !found {
 		tcpPort = &TCPServicePort{
+			svc:   s,
 			port:  port,
 			hosts: make(map[string]*TCPServiceHost),
 			TLS:   make(map[string]*TCPServiceTLSConfig),
@@ -120,6 +118,12 @@ func (s *TCPServices) RemoveAll(services []string) {
 	}
 }
 
+func (s *TCPServices) RemoveAllLinks(pathlinks ...*PathLink) {
+	for _, link := range pathlinks {
+		s.RemoveService(link.hostname)
+	}
+}
+
 // Changed ...
 func (s *TCPServices) Changed() bool {
 	return s.changed
@@ -134,20 +138,25 @@ func (s *TCPServicePort) isEmpty() bool {
 	return s.defaultHost == nil && len(s.hosts) == 0
 }
 
-func (s *TCPServicePort) acquireHost(hostname string) (tcpHost *TCPServiceHost, found bool) {
+func (s *TCPServicePort) AcquireDefaultHost() *TCPServiceHost {
+	return s.AcquireTLSHost(DefaultHost)
+}
+
+func (s *TCPServicePort) AcquireTLSHost(hostname string) *TCPServiceHost {
 	if hostname == DefaultHost && s.defaultHost != nil {
-		return s.defaultHost, true
+		return s.defaultHost
 	}
-	tcpHost, found = s.hosts[hostname]
+	tcpHost, found := s.hosts[hostname]
 	if !found {
-		tcpHost = &TCPServiceHost{hostname: hostname}
+		tcpHost = &TCPServiceHost{tcpport: s, hostname: hostname}
 		if hostname == DefaultHost {
 			s.defaultHost = tcpHost
 		} else {
 			s.hosts[hostname] = tcpHost
 		}
+		s.svc.changed = true
 	}
-	return tcpHost, found
+	return tcpHost
 }
 
 // Port ...
diff --git a/pkg/haproxy/types/types.go b/pkg/haproxy/types/types.go
index 716a20d0..2a8baad8 100644
--- a/pkg/haproxy/types/types.go
+++ b/pkg/haproxy/types/types.go
@@ -52,7 +52,6 @@ type Acme struct {
 
 // Global ...
 type Global struct {
-	Bind                    GlobalBindConfig
 	Procs                   ProcsConfig
 	Syslog                  SyslogConfig
 	MaxConn                 int
@@ -77,6 +76,7 @@ type Global struct {
 	Prometheus              PromConfig
 	Security                SecurityConfig
 	Stats                   StatsConfig
+	TCPBindIP               string
 	CloseSessionsDuration   time.Duration
 	TimeoutStopDuration     time.Duration
 	StrictHost              bool
@@ -96,17 +96,6 @@ type Global struct {
 	CustomTCP               []string
 }
 
-// GlobalBindConfig ...
-type GlobalBindConfig struct {
-	AcceptProxy      bool
-	HTTPBind         string
-	HTTPSBind        string
-	TCPBindIP        string
-	FrontingBind     string
-	FrontingSockID   int
-	FrontingUseProto bool
-}
-
 // ProcsConfig ...
 type ProcsConfig struct {
 	Nbproc          int
@@ -313,6 +302,7 @@ type TCPServices struct {
 
 // TCPServicePort ...
 type TCPServicePort struct {
+	svc          *TCPServices
 	port         int
 	hosts        map[string]*TCPServiceHost
 	defaultHost  *TCPServiceHost
@@ -326,6 +316,7 @@ type TCPServicePort struct {
 
 // TCPServiceHost ...
 type TCPServiceHost struct {
+	tcpport  *TCPServicePort
 	hostname string
 	Backend  BackendID
 }
@@ -441,28 +432,36 @@ type HostsMaps struct {
 	matchOrder []MatchType
 }
 
-// FrontendMaps ...
-type FrontendMaps struct {
-	HTTPHostMap  *HostsMap
-	HTTPSHostMap *HostsMap
-	//
-	RedirFromRootMap  *HostsMap
-	RedirRootSSLMap   *HostsMap
-	RedirFromMap      *HostsMap
-	RedirToMap        *HostsMap
-	SSLPassthroughMap *HostsMap
-	VarNamespaceMap   *HostsMap
-	//
+// FrontendCommonMaps ...
+type FrontendCommonMaps struct {
+	DefaultHostMap   *HostsMap
+	RedirFromMap     *HostsMap
+	RedirFromRootMap *HostsMap
+	RedirToMap       *HostsMap
+	VarNamespaceMap  *HostsMap
+}
+
+// FrontendHTTPMaps ...
+type FrontendHTTPMaps struct {
+	FrontendCommonMaps
+	HTTPHostMap     *HostsMap
+	RedirRootSSLMap *HostsMap
+}
+
+// FrontendHTTPSMaps ...
+type FrontendHTTPSMaps struct {
+	FrontendCommonMaps
+	HTTPSHostMap          *HostsMap
+	SSLPassthroughMap     *HostsMap
 	TLSAuthList           *HostsMap
-	TLSNeedCrtList        *HostsMap
 	TLSInvalidCrtPagesMap *HostsMap
 	TLSMissingCrtPagesMap *HostsMap
-	//
-	DefaultHostMap *HostsMap
+	TLSNeedCrtList        *HostsMap
 }
 
 // AuthProxy ...
 type AuthProxy struct {
+	changed    bool
 	BindList   []*AuthProxyBind
 	Name       string
 	RangeEnd   int
@@ -477,36 +476,50 @@ type AuthProxyBind struct {
 	SocketID        int
 }
 
+// Frontends ...
+type Frontends struct {
+	AuthProxy      AuthProxy
+	DefaultCrtFile string
+	DefaultCrtHash string
+	//
+	items []*Frontend
+}
+
 // Frontend ...
 type Frontend struct {
-	changed     bool
-	Maps        *FrontendMaps
-	Name        string
-	BindName    string
-	BindSocket  string
-	BindID      int
+	HTTPMaps  *FrontendHTTPMaps
+	HTTPSMaps *FrontendHTTPSMaps
+	Name      string
+	IsHTTPS   bool
+	//
+	// Bind related
+	Bind        string
 	AcceptProxy bool
-	AuthProxy   AuthProxy
 	//
-	DefaultCrtFile string
-	DefaultCrtHash string
-	CrtListFile    string
+	// HTTPS related
+	CrtListFile string
+	//
+	// Passthrough related
+	TLSProxyName string
+	HTTPSSocket  string
+	HTTPSProxy   bool
 	//
-	RedirectFromCode int
-	RedirectToCode   int
+	IsFrontingProxy    bool
+	IsFrontingUseProto bool
+	RedirectFromCode   int
+	RedirectToCode     int
+	//
+	// Hosts related
+	hosts,
+	hostsAdd,
+	hostsDel map[string]*Host
+	hasCommit bool
+	port      int32
 }
 
 // DefaultHost ...
 const DefaultHost = "<default>"
 
-// Hosts ...
-type Hosts struct {
-	items, itemsAdd, itemsDel map[string]*Host
-	//
-	sslPassthroughCount int
-	hasCommit           bool
-}
-
 // Host ...
 //
 // Wildcard `*` hostname is a catch all and will be used if no other hostname,
@@ -515,18 +528,17 @@ type Hosts struct {
 // a default 404 page generated by HAProxy will be used.
 type Host struct {
 	Hostname string
-	Paths    []*HostPath
+	Paths    []*Path
 	//
-	Alias                  HostAliasConfig
-	CustomHTTPResponses    HTTPResponses
-	Redirect               HostRedirectConfig
-	HTTPPassthroughBackend string
-	RootRedirect           string
-	TLS                    HostTLSConfig
-	VarNamespace           bool
+	Alias               HostAliasConfig
+	CustomHTTPResponses HTTPResponses
+	Redirect            HostRedirectConfig
+	RootRedirect        string
+	SSLPassthrough      bool
+	TLS                 HostTLSConfig
+	VarNamespace        bool
 	//
-	hosts          *Hosts
-	sslPassthrough bool
+	frontend *Frontend
 }
 
 // MatchType ...
@@ -569,34 +581,13 @@ type PathLinkHash string
 // or should not be applied.
 type PathLink struct {
 	hash     PathLinkHash
+	frontend string
 	hostname string
 	path     string
 	match    MatchType
 	headers  HTTPHeaderMatch
 }
 
-// HostPath ...
-//
-// Root context `/` path is a catch all and will be used if no other path
-// matches the request on this host. If a root context path is not
-// declared, the default backend will be used. If the default backend is
-// empty, a default 404 page generated by HAProxy will be used.
-type HostPath struct {
-	order   int
-	Link    *PathLink
-	AuthExt *AuthExternal
-	Backend HostBackend
-	RedirTo string
-}
-
-// HostBackend ...
-type HostBackend struct {
-	ID        string
-	Namespace string
-	Name      string
-	Port      string
-}
-
 // HostAliasConfig ...
 type HostAliasConfig struct {
 	AliasName  string
@@ -612,9 +603,7 @@ type HostRedirectConfig struct {
 // HostTLSConfig ...
 type HostTLSConfig struct {
 	TLSConfig
-	CAErrorPage    string
-	UseDefaultCrt  bool
-	FollowRedirect bool
+	CAErrorPage string
 }
 
 // EndpointNaming ...
@@ -643,6 +632,8 @@ type Backends struct {
 	authBackends   map[string]*Backend
 	shards         []map[string]*Backend
 	changedShards  map[int]bool
+	httpsRedir     *Backend
+	error404       *Backend
 	DefaultBackend *Backend
 }
 
@@ -674,10 +665,9 @@ type Backend struct {
 	//
 	// Paths
 	//
-	Paths               []*BackendPath
-	PathsMap            *HostsMap
-	PathsDefaultHostMap *HostsMap
-	pathConfig          map[string]*BackendPathConfig
+	Paths        []*Path
+	pathsMaps    []*BackendPathsMaps
+	pathsConfigs map[string]*BackendPathConfig
 	//
 	// per backend config
 	//
@@ -724,6 +714,13 @@ type BlueGreenConfig struct {
 	HeaderName string
 }
 
+type BackendPathsMaps struct {
+	Frontends []string
+	ReqMap    *HostsMap
+	DefMap    *HostsMap
+	Paths     []*Path
+}
+
 // BackendPathConfig ...
 type BackendPathConfig struct {
 	items []*BackendPathItem
@@ -731,33 +728,33 @@ type BackendPathConfig struct {
 
 // BackendPathItem ...
 type BackendPathItem struct {
-	paths  []*BackendPath
+	paths  []*Path
 	config interface{}
 }
 
-// HostResolver ...
-type HostResolver interface {
-	UseTLS() bool
-}
-
 // BackendPath ...
-type BackendPath struct {
+type Path struct {
 	//
-	// core fields, filter out new fields in `Backend.createPathConfig()`
+	// core fields
 	//
-	ID   string
-	Link *PathLink
-	Host HostResolver
+	order    int       `class:"core"`
+	ID       string    `class:"core"`
+	Link     *PathLink `class:"core"`
+	Host     *Host     `class:"core"`
+	Backend  *Backend  `class:"core"`
+	HasHTTPS bool      `class:"core"`
 	//
 	// config fields
 	//
 	AllowedIPHTTP AccessConfig
 	AuthHTTP      AuthHTTP
-	AuthExternal  AuthExternal
+	AuthExtFront  AuthExternal
+	AuthExtBack   AuthExternal
 	Cors          Cors
 	DeniedIPHTTP  AccessConfig
 	HSTS          HSTS
 	MaxBodySize   int64
+	RedirTo       string
 	RewriteURL    string
 	SSLRedirect   bool
 	WAF           WAF
@@ -845,7 +842,6 @@ type BackendTimeoutConfig struct {
 type BackendTLSConfig struct {
 	AddCertHeader    bool
 	FingerprintLower bool
-	HasTLSAuth       bool
 	Sha2Bits         int
 }
 
diff --git a/rootfs/etc/templates/haproxy/haproxy.tmpl b/rootfs/etc/templates/haproxy/haproxy.tmpl
index c7c05e9a..1061c408 100644
--- a/rootfs/etc/templates/haproxy/haproxy.tmpl
+++ b/rootfs/etc/templates/haproxy/haproxy.tmpl
@@ -24,9 +24,7 @@
     {{- $tcpservices := $cfg.TCPServices.BuildSortedItems }}
     {{- $backends := $cfg.Backends }}
     {{- $backendItems := $backends.BuildSortedItems }}
-    {{- $frontend := $cfg.Frontend }}
-    {{- $fmaps := $frontend.Maps }}
-    {{- $hosts := $cfg.Hosts }}
+    {{- $frontends := $cfg.Frontends }}
     {{- template "global" map $global }}
     {{- if $global.Peers.Servers }}
         {{- template "peers" map $global }}
@@ -46,8 +44,8 @@
     {{- if $backendItems }}
         {{- template "backends" map $global $backendItems true }}
     {{- end }}
-    {{- template "backend-support" map $global $hosts $backends }}
-    {{- template "frontends" map $global $frontend $hosts $fmaps $backends.DefaultBackend $tcpservices }}
+    {{- template "backend-support" map $global $backends }}
+    {{- template "frontends" map $global $frontends $backends $tcpservices }}
     {{- template "frontend-support" map $global }}
 {{- else if and .Global .Backends }}
     {{- $global := .Global }}
@@ -303,7 +301,7 @@ userlist {{ $userlist.Name }}
 {{- $proxy_name := printf "_tcp_%s_%d" $backend.Name $backend.Port }}
 listen {{ $proxy_name }}
 {{- $ssl := $backend.SSL }}
-    bind {{ $global.Bind.TCPBindIP }}:{{ $backend.Port }}
+    bind {{ $global.TCPBindIP }}:{{ $backend.Port }}
         {{- if $ssl.Filename }} ssl crt {{ $ssl.Filename }}
             {{- if $ssl.CAFilename }} ca-file {{ $ssl.CAFilename }} verify required
                 {{- if $ssl.CRLFilename }} crl-file {{ $ssl.CRLFilename }}{{ end }}
@@ -471,45 +469,62 @@ backend {{ $backend.ID }}
 {{- else }}{{/*** if $backend.ModeTCP ***/}}
 
 {{- /*------------------------------------*/}}
-{{- $hasPlainHTTPSocket := not $global.Bind.ShareHTTPPort }}
-{{- $hasFrontingProxy := $global.Bind.HasFrontingProxy }}
-{{- $frontingUseProto := and $hasFrontingProxy $global.Bind.FrontingUseProto }}
-{{- $frontingIgnoreProto := and $hasFrontingProxy (not $global.Bind.FrontingUseProto) }}
-{{- if $frontingUseProto }}
-{{- if $hasPlainHTTPSocket }}
-    acl fronting-proxy so_id {{ $global.Bind.FrontingSockID }}
-{{- else }}
-    acl fronting-proxy var(txn.proto) -m found
+{{- $hasNone := 0 }}
+{{- $hasSome := 1 }}
+{{- $hasOnly := 2 }}
+{{- $hasFrontingProxy := $backend.HasFrontingProxy }}
+{{- $hasFrontingUseProto := $backend.HasFrontingUseProto }}
+{{- if $hasFrontingProxy }}
+{{- if eq $hasFrontingProxy $hasSome }}
+    acl fronting-proxy var(req.fronting_proxy) -m found
 {{- end }}
+{{- if eq $hasFrontingUseProto $hasSome }}
+    acl https-request var(req.fronting_proto) -m str ignore
 {{- end }}
-{{- if and (not $frontingIgnoreProto) (or $backend.HasHSTS $backend.HasSSLRedirect) }}
-    acl https-request ssl_fc
-{{- if $frontingUseProto }}
-    acl https-request var(txn.proto) -m str https
+{{- if $hasFrontingUseProto }}
+    acl https-request hdr(X-Forwarded-Proto) https
 {{- end }}
+{{- else if or $backend.HasHSTS $backend.HasSSLRedirect }}
+    acl https-request ssl_fc
 {{- end }}
-{{- if $backend.TLS.HasTLSAuth }}
+{{- $needOffloadACL := and ($backend.HasHTTPRequests) (not $backend.HasSSLRedirect) }}
+{{- $hasTLSAuth := $backend.HasTLSAuth }}
+{{- if and $needOffloadACL $hasTLSAuth }}
     acl local-offload ssl_fc
 {{- end }}
 
 {{- /*------------------------------------*/}}
 {{- if $backend.NeedACL }}
-{{- range $path := $backend.Paths }}
-    # {{ $path.ID }} = {{ $path.Hostname }}{{ $path.Path }}
+{{- $needFrontendACL := $backend.NeedFrontendACL }}
+{{- range $pathsMap := $backend.PathsMaps }}
+{{- range $path := $pathsMap.Paths }}
+    # {{ $path.ID }} = {{ if $needFrontendACL }}fe:{{ join "/" $pathsMap.Frontends }} -- host/path:{{ end }}{{ $path.Hostname }}{{ $path.Path }}
+{{- end }}
+{{- end }}
+{{- if $needFrontendACL }}
+    http-request set-var-fmt(req.fe) "%f"
 {{- end }}
-{{- range $match := $backend.PathsMap.MatchFiles }}
+{{- range $pathsMap := $backend.PathsMaps }}
+{{- range $match := $pathsMap.ReqMap.MatchFiles }}
     http-request set-var(txn.pathID) var(req.base)
         {{- if $match.Lower }},lower{{ end }}
         {{- "" }},map_{{ $match.Method }}({{ $match.Filename }})
-        {{- template "httpFilters" map $match "txn.pathID" 1 }}
+        {{- if $needFrontendACL }} if { var(req.fe) -m str {{ join " " $pathsMap.Frontends }} }{{ end }}
+        {{- template "httpFilters" map $match "txn.pathID" (not $needFrontendACL) }}
 {{- end }}
-{{- $pathsHasHost := $backend.PathsMap.HasHost }}
-{{- range $match := $backend.PathsDefaultHostMap.MatchFiles }}
+{{- $pathsHasHost := $pathsMap.ReqMap.HasHost }}
+{{- range $match := $pathsMap.DefMap.MatchFiles }}
     http-request set-var(txn.pathID) str(<default>\#),concat(,req.path)
         {{- if $match.Lower }},lower{{ end }}
         {{- "" }},map_{{ $match.Method }}({{ $match.Filename }})
-        {{- if or $pathsHasHost (not $match.First) }} if !{ var(txn.pathID) -m found }{{ end }}
-        {{- template "httpFilters" map $match "" 0 }}
+        {{- if or $needFrontendACL $pathsHasHost (not $match.First) }} if
+            {{- if $needFrontendACL }} { var(req.fe) -m str {{ join " " $pathsMap.Frontends }} }{{ end }}
+            {{- if or $pathsHasHost (not $match.First) }} !{ var(txn.pathID) -m found }{{ end }}
+            {{- template "httpFilters" map $match "" 0 }}
+        {{- else }}
+            {{- template "httpFilters" map $match "" 1 }}
+        {{- end }}
+{{- end }}
 {{- end }}
 {{- end }}
 
@@ -526,21 +541,24 @@ backend {{ $backend.ID }}
    * */}}
 
 {{- /*------------------------------------*/}}
-{{- if $frontingUseProto }}
+{{- if $hasFrontingUseProto }}
     http-request redirect scheme https
         {{- if $global.SSL.RedirectCode }} code {{ $global.SSL.RedirectCode }}{{ end }}
-        {{- "" }} if fronting-proxy !{ var(txn.proto) -m str https }
+        {{- if or (eq $hasFrontingProxy $hasSome) ($hasFrontingUseProto) }} if
+            {{- if eq $hasFrontingProxy $hasSome }} fronting-proxy{{ end }}
+            {{- if $hasFrontingUseProto }} !https-request{{ end }}
+        {{- end }}
 {{- end }}
 
 {{- /*------------------------------------*/}}
-{{- if not $frontingIgnoreProto }}
+{{- if ne $hasFrontingProxy $hasOnly }}
 {{- $sslredirCfg := $backend.PathConfig "SSLRedirect" }}
 {{- range $i, $sslredir := $sslredirCfg.Items }}
 {{- if $sslredir }}
 {{- range $pathIDs := $sslredirCfg.PathIDs $i }}
     http-request redirect scheme https
         {{- if $global.SSL.RedirectCode }} code {{ $global.SSL.RedirectCode }}{{ end }}
-        {{- "" }} if{{ if $hasFrontingProxy }} !fronting-proxy{{ end }} !https-request
+        {{- "" }} if {{ if $hasFrontingProxy }}!fronting-proxy{{ else }}!https-request{{ end }}
         {{- if $pathIDs }} { var(txn.pathID) -m str {{ $pathIDs }} }{{ end }}
 {{- end }}
 {{- end }}
@@ -670,20 +688,19 @@ backend {{ $backend.ID }}
 {{- end }}
 
 {{- /*------------------------------------*/}}
-{{- if $backend.TLS.HasTLSAuth }}
-{{- $needSSLACL := not $backend.HasSSLRedirect }}
-    http-request set-header {{ $global.SSL.HeadersPrefix }}-Client-CN   %{+Q}[ssl_c_s_dn(cn)]{{ if $needSSLACL }}   if local-offload{{ end }}
-    http-request set-header {{ $global.SSL.HeadersPrefix }}-Client-DN   %{+Q}[ssl_c_s_dn]{{ if $needSSLACL }}       if local-offload{{ end }}
+{{- if $hasTLSAuth }}
+    http-request set-header {{ $global.SSL.HeadersPrefix }}-Client-CN   %{+Q}[ssl_c_s_dn(cn)]{{ if $needOffloadACL }}   if local-offload{{ end }}
+    http-request set-header {{ $global.SSL.HeadersPrefix }}-Client-DN   %{+Q}[ssl_c_s_dn]{{ if $needOffloadACL }}       if local-offload{{ end }}
     http-request set-header {{ $global.SSL.HeadersPrefix }}-Client-SHA1 %{+Q}[ssl_c_sha1,hex
         {{- if $backend.TLS.FingerprintLower }},lower{{ end }}]
-        {{- if $needSSLACL }}   if local-offload{{ end }}
+        {{- if $needOffloadACL }}   if local-offload{{ end }}
 {{- if $backend.TLS.Sha2Bits }}
     http-request set-header {{ $global.SSL.HeadersPrefix }}-Client-SHA2 %{+Q}[ssl_c_der,sha2({{ $backend.TLS.Sha2Bits }}),hex
         {{- if $backend.TLS.FingerprintLower }},lower{{ end }}]
-        {{- if $needSSLACL }} if local-offload{{ end }}
+        {{- if $needOffloadACL }} if local-offload{{ end }}
 {{- end }}
 {{- if $backend.TLS.AddCertHeader }}
-    http-request set-header {{ $global.SSL.HeadersPrefix }}-Client-Cert %{+Q}[ssl_c_der,base64]{{ if $needSSLACL }} if local-offload{{ end }}
+    http-request set-header {{ $global.SSL.HeadersPrefix }}-Client-Cert %{+Q}[ssl_c_der,base64]{{ if $needOffloadACL }} if local-offload{{ end }}
 {{- end }}
 {{- end }}
 
@@ -705,7 +722,7 @@ backend {{ $backend.ID }}
 {{- end }}
 
 {{- /*------------------------------------*/}}
-{{- $authCfg := $backend.PathConfig "AuthExternal" }}
+{{- $authCfg := $backend.PathConfig "AuthExtBack" }}
 {{- range $i, $auth := $authCfg.Items }}
 {{- range $pathIDs := $authCfg.PathIDs $i }}
 {{- template "authExternal" map $auth (iif (eq $pathIDs "") "" (printf "{ var(txn.pathID) -m str %s }" $pathIDs)) }}
@@ -742,7 +759,7 @@ backend {{ $backend.ID }}
 {{- $needACL := $rewriteCfg.NeedACL }}
 {{- range $i, $rewrite := $rewriteCfg.Items }}
 {{- if $rewrite }}
-{{- range $path := ($rewriteCfg.Paths $i) }}
+{{- range $path := $rewriteCfg.Paths $i }}
 {{- if eq $rewrite "/" }}
     http-request replace-path ^{{ $path.Path }}/?(.*)$     {{ $rewrite }}\1
         {{- if $needACL }}     if { var(txn.pathID) -m str {{ $path.ID }} }{{ end }}
@@ -759,13 +776,14 @@ backend {{ $backend.ID }}
 {{- range $i, $hsts := $hstsCfg.Items }}
 {{- if $hsts.Enabled }}
 {{- $paths := $hstsCfg.Paths $i }}
-{{- $needSSLACL := and (not $frontingIgnoreProto) (not ($backend.HasSSLRedirectPaths $paths)) }}
+{{- $needSSLACL := not ($backend.HasSSLRedirectPaths $paths) }}
 {{- range $pathIDs := $hstsCfg.PathIDs $i }}
     http-response set-header Strict-Transport-Security "max-age={{ $hsts.MaxAge }}
         {{- if $hsts.Subdomains }}; includeSubDomains{{ end }}
         {{- if $hsts.Preload }}; preload{{ end }}"
-        {{- if or $needSSLACL $pathIDs }} if
-            {{- if $needSSLACL }} https-request{{ end }}
+        {{- if or $needSSLACL $pathIDs (eq $hasFrontingProxy $hasSome) }} if
+            {{- if eq $hasFrontingProxy $hasSome }} !fronting-proxy{{ end }}
+            {{- if and (not $hasFrontingProxy) $needSSLACL }} https-request{{ end }}
             {{- if $pathIDs }} { var(txn.pathID) -m str {{ $pathIDs }} }{{ end }}
         {{- end }}
 {{- end }}
@@ -903,10 +921,9 @@ backend {{ $backend.ID }}
 
 {{- define "backend-support" }}
 {{- $global := .p1 }}
-{{- $hosts := .p2 }}
-{{- $backends := .p3 }}
+{{- $backends := .p2 }}
 
-{{- if $hosts.HasSSLPassthrough }}
+{{- if $backends.HasBackend "_redirect_https" }}
 
   # # # # # # # # # # # # # # # # # # #
 # #
@@ -935,7 +952,7 @@ backend _acme_challenge
     server _acme_server unix@{{ $global.Acme.Socket }}
 {{- end }}
 
-{{- if not $backends.DefaultBackend }}
+{{- if $backends.HasBackend "_error404" }}
 
   # # # # # # # # # # # # # # # # # # #
 # #
@@ -958,11 +975,9 @@ backend _error404
 
 {{- define "frontends" }}
 {{- $global := .p1 }}
-{{- $frontend := .p2 }}
-{{- $hosts := .p3 }}
-{{- $fmaps := .p4 }}
-{{- $defaultbackend := .p5 }}
-{{- $tcpservices := .p6 }}
+{{- $frontends := .p2 }}
+{{- $backends := .p3 }}
+{{- $tcpservices := .p4 }}
 
 
   # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
@@ -971,9 +986,9 @@ backend _error404
 # #   FRONTENDS
 # #
 #
-{{- if $frontend.AuthProxy.BindList }}
+{{- if $frontends.AuthProxy.BindList }}
 
-{{- $proxy := $frontend.AuthProxy }}
+{{- $proxy := $frontends.AuthProxy }}
 {{- $use_socket := gt (len $proxy.BindList) 1 }}
 
   # # # # # # # # # # # # # # # # # # #
@@ -1032,7 +1047,7 @@ frontend {{ $proxy.Name }}
 {{- $proxy_name := printf "_front_tcp_%d" $tcpport.Port }}
 frontend {{ $proxy_name }}
 {{- $hasTLS := $tcpport.HasTLS }}
-    bind {{ $global.Bind.TCPBindIP }}:{{ $tcpport.Port }}
+    bind {{ $global.TCPBindIP }}:{{ $tcpport.Port }}
         {{- if $tcpport.ProxyProt }} accept-proxy{{ end }}
         {{- if $hasTLS }}
             {{- "" }} ssl crt-list {{ $global.LocalFSPrefix }}/etc/haproxy/crtlist_tcp_{{ $tcpport.Port }}.list
@@ -1091,16 +1106,26 @@ frontend {{ $proxy_name }}
 {{- end }}{{/* range $tcpservices */}}
 {{- end }}{{/* has $tcpservices */}}
 
-{{- if $hosts.HasSSLPassthrough }}
+{{- $hasHTTPResponses := $frontends.HasHTTPResponses }}
+{{- $hasSomeFrontingProxy := $frontends.HasSomeFrontingProxy }}
+{{- $hasSomeFrontingUseProto := $frontends.HasSomeFrontingUseProto }}
+{{- range $frontend := $frontends.Items }}
+
+{{- $isFrontingProxy := $frontend.IsFrontingProxy }}
+{{- $isFrontingUseProto := $frontend.IsFrontingUseProto }}
+{{- $httpmaps := $frontend.HTTPMaps }}
+{{- $httpsmaps := $frontend.HTTPSMaps }}
+{{- $hasSSLPassthrough := $frontend.HasSSLPassthrough }}
+
+{{- if and $frontend.IsHTTPS $hasSSLPassthrough }}
 
   # # # # # # # # # # # # # # # # # # #
 # #
 #     TCP/TLS frontend
 #
-{{- $proxy__front__tls := "_front__tls" }}
-listen {{ $proxy__front__tls }}
+listen {{ $frontend.TLSProxyName }}
     mode tcp
-    bind {{ $global.Bind.HTTPSBind }}{{ if $global.Bind.AcceptProxy }} accept-proxy{{ end }}
+    bind {{ $frontend.Bind }}{{ if $frontend.AcceptProxy }} accept-proxy{{ end }}
 
 {{- /*------------------------------------*/}}
 {{- if $global.Syslog.Endpoint }}
@@ -1117,14 +1142,14 @@ listen {{ $proxy__front__tls }}
     tcp-request inspect-delay 5s
 
 {{- /*------------------------------------*/}}
-{{- range $match := $fmaps.SSLPassthroughMap.MatchFiles }}
+{{- range $match := $frontend.HTTPSMaps.SSLPassthroughMap.MatchFiles }}
     tcp-request content set-var(req.sslpassback) req.ssl_sni,lower
         {{- "" }},map_{{ $match.Method }}({{ $match.Filename }})
         {{- if not $match.First }} if !{ var(req.sslpassback) -m found }{{ end }}
 {{- end }}
 
 {{- /*------------------------------------*/}}
-{{- range $snippet := index $global.CustomProxy $proxy__front__tls }}
+{{- range $snippet := index $global.CustomProxy $frontend.TLSProxyName }}
     {{ $snippet }}
 {{- end }}
 
@@ -1133,7 +1158,7 @@ listen {{ $proxy__front__tls }}
 
 {{- /*------------------------------------*/}}
     use_backend %[var(req.sslpassback)] if { var(req.sslpassback) -m found }
-{{- $defaultHost := $hosts.DefaultHost }}
+{{- $defaultHost := $frontend.DefaultHost }}
 {{- if $defaultHost }}
 {{- if $defaultHost.SSLPassthrough }}
 {{- range $path := $defaultHost.Paths }}
@@ -1143,39 +1168,19 @@ listen {{ $proxy__front__tls }}
 {{- end }}
 {{- end }}
 {{- end }}
-    server _default_server{{ $frontend.BindName }} {{ $frontend.BindSocket }} send-proxy-v2
+    server _default_server{{ $frontend.Name }}_socket {{ $frontend.HTTPSSocket }} send-proxy-v2
 {{- end }}{{/* HasSSLPassthrough */}}
 
-{{- if $fmaps }}
-
-{{- $hasFrontingProxy := $global.Bind.HasFrontingProxy }}
-{{- $frontingUseProto := and $hasFrontingProxy $global.Bind.FrontingUseProto }}
-{{- $frontingIgnoreProto := and $hasFrontingProxy (not $global.Bind.FrontingUseProto) }}
+{{- if $httpmaps }}
 
   # # # # # # # # # # # # # # # # # # #
 # #
-#     HTTP{{ if $hasFrontingProxy }} & Fronting Proxy{{ end }} frontend
+#     {{ if $isFrontingProxy }}Fronting Proxy{{ else }}HTTP{{ end }} frontend
 #
-{{- $proxy__front_http := "_front_http" }}
-frontend {{ $proxy__front_http }}
+frontend {{ $frontend.Name }}
     mode http
-{{- $hasPlainHTTPSocket := not $global.Bind.ShareHTTPPort }}
-{{- if and $global.Bind.HTTPBind $hasPlainHTTPSocket }}
-    bind {{ $global.Bind.HTTPBind }}{{ if $global.Bind.AcceptProxy }} accept-proxy{{ end }}
-{{- end }}
-{{- if $global.Bind.FrontingBind }}
-    bind {{ $global.Bind.FrontingBind }}
-        {{- if and $hasPlainHTTPSocket $global.Bind.FrontingSockID }} id {{ $global.Bind.FrontingSockID }}{{ end }}
-        {{- if $global.Bind.AcceptProxy }} accept-proxy{{ end }}
-{{- end }}
-
-{{- /*------------------------------------*/}}
-{{- if $frontingUseProto }}
-{{- if $hasPlainHTTPSocket }}
-    acl fronting-proxy so_id {{ $global.Bind.FrontingSockID }}
-{{- else }}
-    acl fronting-proxy var(txn.proto) -m found
-{{- end }}
+{{- with $frontend.Bind }}
+    bind {{ . }}{{ if $frontend.AcceptProxy }} accept-proxy{{ end }}
 {{- end }}
 
 {{- /*------------------------------------*/}}
@@ -1198,18 +1203,21 @@ frontend {{ $proxy__front_http }}
 {{- end }}
 
 {{- /*------------------------------------*/}}
+{{- if and $isFrontingProxy $hasSomeFrontingProxy }}
+    http-request set-var(req.fronting_proxy) str(1)
+{{- end }}
+{{- if $hasSomeFrontingUseProto }}
+    http-request set-var(req.fronting_proto) str({{ if $isFrontingUseProto }}use{{ else }}ignore{{ end }})
+{{- end }}
     http-request set-var(req.path) path
     http-request set-var(req.host) hdr(host),field(1,:),lower
     http-request set-var(req.base) var(req.host),concat(\#,req.path)
-{{- if $frontingUseProto }}
-    http-request set-var(txn.proto) hdr(X-Forwarded-Proto)
-{{- end }}
 
 {{- $acmeexclusive := and $global.Acme.Enabled (not $global.Acme.Shared) }}
 
 {{- /*------------------------------------*/}}
-{{- if $fmaps.RedirRootSSLMap.HasHost }}
-{{- range $match := $fmaps.RedirRootSSLMap.MatchFiles }}
+{{- if $httpmaps.RedirRootSSLMap.HasHost }}
+{{- range $match := $httpmaps.RedirRootSSLMap.MatchFiles }}
     http-request redirect scheme https
         {{- if $global.SSL.RedirectCode }} code {{ $global.SSL.RedirectCode }}{{ end }}
         {{- "" }} if{{ if $acmeexclusive }} !acme-challenge{{ end }}
@@ -1218,8 +1226,8 @@ frontend {{ $proxy__front_http }}
 {{- end }}
 
 {{- /*------------------------------------*/}}
-{{- if $fmaps.RedirFromRootMap.HasHost }}
-{{- range $match := $fmaps.RedirFromRootMap.MatchFiles }}
+{{- if $httpmaps.RedirFromRootMap.HasHost }}
+{{- range $match := $httpmaps.RedirFromRootMap.MatchFiles }}
     http-request set-var(req.rootredir) var(req.host)
         {{- "" }},map_{{ $match.Method }}({{ $match.Filename }})
         {{- if not $match.First }} if !{ var(req.rootredir) -m found }{{ end }}
@@ -1230,8 +1238,8 @@ frontend {{ $proxy__front_http }}
 {{- end }}
 
 {{- /*------------------------------------*/}}
-{{- if $fmaps.VarNamespaceMap.HasHost }}
-{{- range $match := $fmaps.VarNamespaceMap.MatchFiles }}
+{{- if $httpmaps.VarNamespaceMap.HasHost }}
+{{- range $match := $httpmaps.VarNamespaceMap.MatchFiles }}
     http-request set-var(txn.namespace) var(req.base)
         {{- if $match.Lower }},lower{{ end }}
         {{- "" }},map_{{ $match.Method }}({{ $match.Filename }})
@@ -1242,35 +1250,29 @@ frontend {{ $proxy__front_http }}
 {{- end }}
 
 {{- /*------------------------------------*/}}
-{{- if not $frontingIgnoreProto }}
+{{- if not $isFrontingProxy }}
     http-request set-header X-Forwarded-Proto http
-        {{- if $hasFrontingProxy }} if !fronting-proxy{{ end }}
     http-request del-header {{ $global.SSL.HeadersPrefix }}-Client-CN
-        {{- if $hasFrontingProxy }} if !fronting-proxy{{ end }}
     http-request del-header {{ $global.SSL.HeadersPrefix }}-Client-DN
-        {{- if $hasFrontingProxy }} if !fronting-proxy{{ end }}
     http-request del-header {{ $global.SSL.HeadersPrefix }}-Client-SHA1
-        {{- if $hasFrontingProxy }} if !fronting-proxy{{ end }}
     http-request del-header {{ $global.SSL.HeadersPrefix }}-Client-SHA2
-        {{- if $hasFrontingProxy }} if !fronting-proxy{{ end }}
     http-request del-header {{ $global.SSL.HeadersPrefix }}-Client-Cert
-        {{- if $hasFrontingProxy }} if !fronting-proxy{{ end }}
 {{- end }}
 
 {{- /*------------------------------------*/}}
-{{- template "redirectTo" map $global $frontend $fmaps }}
+{{- template "redirectTo" map $global $frontend $httpmaps }}
 
 {{- /*------------------------------------*/}}
-{{- template "authExternalFrontend" map $hosts }}
+{{- template "authExternalFrontend" map $frontend }}
 
 {{- /*------------------------------------*/}}
-{{- range $match := $fmaps.HTTPHostMap.MatchFiles }}
+{{- range $match := $httpmaps.HTTPHostMap.MatchFiles }}
     http-request set-var(req.backend) var(req.base)
         {{- if $match.Lower }},lower{{ end }}
         {{- "" }},map_{{ $match.Method }}({{ $match.Filename }})
         {{- template "httpFilters" map $match "req.backend" 1 }}
 {{- end }}
-{{- range $match := $fmaps.DefaultHostMap.MatchFiles }}
+{{- range $match := $httpmaps.DefaultHostMap.MatchFiles }}
     http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path)
         {{- if $match.Lower }},lower{{ end }}
         {{- "" }},map_{{ $match.Method }}({{ $match.Filename }})
@@ -1279,7 +1281,7 @@ frontend {{ $proxy__front_http }}
 {{- end }}
 
 {{- /*------------------------------------*/}}
-{{- template "redirectFrom" map $global $frontend $fmaps "req.backend" }}
+{{- template "redirectFrom" map $global $frontend $httpmaps "req.backend" }}
 
 {{- /*------------------------------------*/}}
 {{- template "sourceIP" map $global }}
@@ -1288,7 +1290,7 @@ frontend {{ $proxy__front_http }}
 {{- range $snippet := $global.CustomFrontendLate }}
     {{ $snippet }}
 {{- end }}
-{{- range $snippet := index $global.CustomProxy $proxy__front_http }}
+{{- range $snippet := index $global.CustomProxy $frontend.Name }}
     {{ $snippet }}
 {{- end }}
 
@@ -1301,29 +1303,25 @@ frontend {{ $proxy__front_http }}
     use_backend _acme_challenge if acme-challenge
 {{- end }}
 
-{{- if $hosts.DefaultHost }}
-{{- if $hosts.DefaultHost.HTTPPassthroughBackend }}
-    use_backend {{ $hosts.DefaultHost.HTTPPassthroughBackend }}
-{{- end }}
-{{- end }}
-{{- template "defaultbackend" map $hosts $defaultbackend }}
+{{- template "defaultbackend" map $frontend $backends }}
+{{- end }}{{/* if $httpmaps */}}
+
+{{- if $httpsmaps }}
 
   # # # # # # # # # # # # # # # # # # #
 # #
 #     HTTPS frontend
 #
-frontend {{ $frontend.Name }}
+{{- $httpsFrontendName := printf "%s%s" $frontend.Name (iif $hasSSLPassthrough "__local" "") }}
+frontend {{ $httpsFrontendName }}
     mode http
 
 {{- /*------------------------------------*/}}
-{{- if $frontend.BindSocket }}
-    bind {{ $frontend.BindSocket }}
-        {{- if $frontend.BindID }} id {{ $frontend.BindID }}{{ end }}
-        {{- if $frontend.AcceptProxy }} accept-proxy{{ end }}
+    bind {{ default $frontend.Bind $frontend.HTTPSSocket }}
+        {{- if $frontend.HTTPSProxy }} accept-proxy{{ end }}
         {{- "" }} ssl alpn {{ $global.SSL.ALPN }}
         {{- "" }} crt-list {{ $frontend.CrtListFile }}
         {{- "" }} ca-ignore-err all crt-ignore-err all
-{{- end }}
 
 {{- /*------------------------------------*/}}
 {{- if $global.Syslog.Endpoint }}
@@ -1345,19 +1343,19 @@ frontend {{ $frontend.Name }}
     http-request set-var(req.base) var(req.host),concat(\#,req.path)
 
 {{- /*------------------------------------*/}}
-{{- template "redirectTo" map $global $frontend $fmaps }}
+{{- template "redirectTo" map $global $frontend $httpsmaps }}
 
 {{- /*------------------------------------*/}}
-{{- template "authExternalFrontend" map $hosts }}
+{{- template "authExternalFrontend" map $frontend }}
 
 {{- /*------------------------------------*/}}
-{{- range $match := $fmaps.HTTPSHostMap.MatchFiles }}
+{{- range $match := $httpsmaps.HTTPSHostMap.MatchFiles }}
     http-request set-var(req.hostbackend) var(req.base)
         {{- if $match.Lower }},lower{{ end }}
         {{- "" }},map_{{ $match.Method }}({{ $match.Filename }})
         {{- template "httpFilters" map $match "req.hostbackend" 1 }}
 {{- end }}
-{{- range $match := $fmaps.DefaultHostMap.MatchFiles }}
+{{- range $match := $httpsmaps.DefaultHostMap.MatchFiles }}
     http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path)
         {{- if $match.Lower }},lower{{ end }}
         {{- "" }},map_{{ $match.Method }}({{ $match.Filename }})
@@ -1366,11 +1364,11 @@ frontend {{ $frontend.Name }}
 {{- end }}
 
 {{- /*------------------------------------*/}}
-{{- template "redirectFrom" map $global $frontend $fmaps "req.hostbackend" }}
+{{- template "redirectFrom" map $global $frontend $httpsmaps "req.hostbackend" }}
 
 {{- /*------------------------------------*/}}
-{{- if $fmaps.RedirFromRootMap.HasHost }}
-{{- range $match := $fmaps.RedirFromRootMap.MatchFiles }}
+{{- if $httpsmaps.RedirFromRootMap.HasHost }}
+{{- range $match := $httpsmaps.RedirFromRootMap.MatchFiles }}
     http-request set-var(req.rootredir) var(req.host)
         {{- "" }},map_{{ $match.Method }}({{ $match.Filename }})
         {{- if not $match.First }} if !{ var(req.rootredir) -m found }{{ end }}
@@ -1379,8 +1377,8 @@ frontend {{ $frontend.Name }}
 {{- end }}
 
 {{- /*------------------------------------*/}}
-{{- if $fmaps.VarNamespaceMap.HasHost }}
-{{- range $match := $fmaps.VarNamespaceMap.MatchFiles }}
+{{- if $httpsmaps.VarNamespaceMap.HasHost }}
+{{- range $match := $httpsmaps.VarNamespaceMap.MatchFiles }}
     http-request set-var(txn.namespace) var(req.base)
         {{- if $match.Lower }},lower{{ end }}
         {{- "" }},map_{{ $match.Method }}({{ $match.Filename }})
@@ -1402,30 +1400,30 @@ frontend {{ $frontend.Name }}
 {{- template "sourceIP" map $global }}
 
 {{- /*------------------------------------*/}}
-{{- $hasTLSAuth := or $hosts.HasTLSAuth  }}
+{{- $hasTLSAuth := or $frontend.HasTLSAuth  }}
 {{- if $hasTLSAuth }}
-{{- $mandatory := $fmaps.TLSNeedCrtList.HasHost }}
+{{- $mandatory := $httpsmaps.TLSNeedCrtList.HasHost }}
     acl tls-has-crt ssl_c_used
 
 {{- if $mandatory }}
-{{- range $match := $fmaps.TLSNeedCrtList.MatchFiles }}
+{{- range $match := $httpsmaps.TLSNeedCrtList.MatchFiles }}
     acl tls-need-crt ssl_fc_sni -i -m {{ $match.Method }} -f {{ $match.Filename }}
 {{- end }}
 {{- end }}
 
-{{- range $match := $fmaps.TLSNeedCrtList.MatchFiles }}
+{{- range $match := $httpsmaps.TLSNeedCrtList.MatchFiles }}
     acl tls-host-need-crt var(req.host) -i -m {{ $match.Method }} -f {{ $match.Filename }}
 {{- end }}
     acl tls-has-invalid-crt ssl_c_verify gt 0
-{{- range $match := $fmaps.TLSAuthList.MatchFiles }}
+{{- range $match := $httpsmaps.TLSAuthList.MatchFiles }}
     acl tls-check-crt ssl_fc_sni -i -m {{ $match.Method }} -f {{ $match.Filename }}
 {{- end }}
 
 {{- if $mandatory }}
-{{- if not $fmaps.TLSMissingCrtPagesMap.HasHost }}
+{{- if not $httpsmaps.TLSMissingCrtPagesMap.HasHost }}
     http-request set-var(req.tls_nocrt_redir) str(_internal) if !tls-has-crt tls-need-crt
 {{- end }}
-{{- range $match := $fmaps.TLSMissingCrtPagesMap.MatchFiles }}
+{{- range $match := $httpsmaps.TLSMissingCrtPagesMap.MatchFiles }}
     http-request set-var(req.tls_nocrt_redir) ssl_fc_sni,lower
         {{- "" }},map_{{ $match.Method }}({{ $match.Filename }},_internal) if
         {{- if $match.First }} !tls-has-crt tls-need-crt
@@ -1433,21 +1431,21 @@ frontend {{ $frontend.Name }}
 {{- end }}
 {{- end }}
 
-{{- if $fmaps.TLSAuthList.HasHost }}
-{{- if not $fmaps.TLSInvalidCrtPagesMap.HasHost }}
+{{- if $httpsmaps.TLSAuthList.HasHost }}
+{{- if not $httpsmaps.TLSInvalidCrtPagesMap.HasHost }}
     http-request set-var(req.tls_invalidcrt_redir) str(_internal) if tls-has-invalid-crt tls-check-crt
 {{- end }}
-{{- range $match := $fmaps.TLSInvalidCrtPagesMap.MatchFiles }}
+{{- range $match := $httpsmaps.TLSInvalidCrtPagesMap.MatchFiles }}
     http-request set-var(req.tls_invalidcrt_redir) ssl_fc_sni,lower
         {{- "" }},map_{{ $match.Method }}({{ $match.Filename }},_internal) if
         {{- if $match.First }} tls-has-invalid-crt tls-check-crt
         {{- else }} { var(req.tls_invalidcrt_redir) -m str _internal }{{ end }}
 {{- end }}
-{{- if and $mandatory $fmaps.TLSMissingCrtPagesMap.HasHost }}
+{{- if and $mandatory $httpsmaps.TLSMissingCrtPagesMap.HasHost }}
     http-request redirect location %[var(req.tls_nocrt_redir)] code 303
         {{- "" }} if { var(req.tls_nocrt_redir) -m found } !{ var(req.tls_nocrt_redir) -m str _internal }
 {{- end }}
-{{- if $fmaps.TLSInvalidCrtPagesMap.HasHost }}
+{{- if $httpsmaps.TLSInvalidCrtPagesMap.HasHost }}
     http-request redirect location %[var(req.tls_invalidcrt_redir)] code 303
         {{- "" }} if { var(req.tls_invalidcrt_redir) -m found } !{ var(req.tls_invalidcrt_redir) -m str _internal }
 {{- end }}
@@ -1459,19 +1457,19 @@ frontend {{ $frontend.Name }}
 {{- range $snippet := $global.CustomFrontendLate }}
     {{ $snippet }}
 {{- end }}
-{{- range $snippet := index $global.CustomProxy $frontend.Name }}
+{{- range $snippet := index $global.CustomProxy $httpsFrontendName }}
     {{ $snippet }}
 {{- end }}
 
 {{- /*------------------------------------*/}}
 {{- if $hasTLSAuth }}
-{{- if $hosts.BuildHTTPResponses }}
-    http-request set-var(txn.lua_scope) var(req.host)
+{{- if $hasHTTPResponses }}
+    http-request set-var(txn.lua_scope) str({{ $frontend.Name }}),concat(--,req.host)
 {{- end }}
     http-request use-service lua.send-421 if
         {{- "" }} tls-has-crt { ssl_fc_has_sni } !{ ssl_fc_sni,strcmp(req.host) eq 0 }
-{{- if $fmaps.TLSAuthList.HasHost }}
-{{- if $fmaps.TLSNeedCrtList.HasHost }}
+{{- if $httpsmaps.TLSAuthList.HasHost }}
+{{- if $httpsmaps.TLSNeedCrtList.HasHost }}
     http-request use-service lua.send-496 if
         {{- "" }} { var(req.tls_nocrt_redir) -m str _internal }
 {{- /* HTTP 421 instead of 404 if missing sni or the provided one doesn't read client crt. */}}
@@ -1491,9 +1489,10 @@ frontend {{ $frontend.Name }}
     use_backend %[var(req.snibackend)]
         {{- "" }} if { var(req.snibackend) -m found }
 {{- end }}
-{{- template "defaultbackend" map $hosts $defaultbackend }}
+{{- template "defaultbackend" map $frontend $backends }}
 
-{{- end }}{{/* has $fmaps */}}
+{{- end }}{{/* if $httpsmaps */}}
+{{- end }}{{/* range $frontends */}}
 {{- end }}{{/* define "frontends" */}}
 
 {{- /*------------------------------------*/}}
@@ -1598,11 +1597,11 @@ frontend {{ $frontend.Name }}
 {{- /*------------------------------------*/}}
 {{- /*------------------------------------*/}}
 {{- define "authExternalFrontend" }}
-{{- $hosts := .p1 }}
-{{- range $host := $hosts.Items }}
+{{- $frontend := .p1 }}
+{{- range $host := $frontend.Hosts }}
 {{- range $path := $host.Paths }}
-{{- if $path.AuthExt }}
-{{- template "authExternal" map $path.AuthExt (printf "{ var(req.base) -m str %s '%s' }" $path.Link.HAMatch $path.Link.Key) }}
+{{- if $path.AuthExtFront.AuthPath }}
+{{- template "authExternal" map $path.AuthExtFront (printf "{ var(req.base) -m str %s '%s' }" $path.Link.HAMatch $path.Link.Key) }}
 {{- end }}
 {{- end }}
 {{- end }}
@@ -1613,13 +1612,11 @@ frontend {{ $frontend.Name }}
 {{- define "httpFilters" }}
 {{- $match := .p1 }}
 {{- $varbe := .p2 }}
-{{- $needIfStmt := .p3 }}
-{{- if $varbe }}
-{{- if $needIfStmt }}
-    {{- if or (not $match.First) $match.Headers }} if{{ end }}
-{{- end }}
-    {{- if not $match.First }} !{ var({{ $varbe }}) -m found }{{ end }}
+{{- $missingIfStmt := .p3 }}
+{{- if $missingIfStmt }}
+    {{- if or $match.Headers (and $varbe (not $match.First)) }} if{{ end }}
 {{- end }}
+{{- if and $varbe (not $match.First) }} !{ var({{ $varbe }}) -m found }{{ end }}
 {{- if $match.Headers }}
     {{- range $header := $match.Headers }} { hdr({{ $header.Name }}){{ if $header.Regex }} -m reg{{ end }} -- {{ $header.Value | haquote }} }{{ end }}
 {{- end }}
@@ -1628,19 +1625,16 @@ frontend {{ $frontend.Name }}
 {{- /*------------------------------------*/}}
 {{- /*------------------------------------*/}}
 {{- define "defaultbackend" }}
-{{- $hosts := .p1 }}
-{{- $defaultbackend := .p2 }}
-{{- $defaultHost := $hosts.DefaultHost }}
+{{- $frontend := .p1 }}
+{{- $backends := .p2 }}
+{{- $defaultHost := $frontend.DefaultHost }}
+{{- $defaultBackend := $backends.DefaultBackend }}
 {{- if $defaultHost }}
 {{- if not $defaultHost.SSLPassthrough }}
     use_backend %[var(req.defaultbackend)]
 {{- end }}
 {{- end }}
-{{- if $defaultbackend }}
-    default_backend {{ $defaultbackend.ID }}
-{{- else }}
-    default_backend _error404
-{{- end }}
+    default_backend {{ $defaultBackend.ID }}
 {{- end }}
 
 {{- define "frontend-support" }}



Test patch:
diff --git a/pkg/converters/gateway/gateway_test.go b/pkg/converters/gateway/gateway_test.go
index 8bc3f49f..e4fc34c3 100644
--- a/pkg/converters/gateway/gateway_test.go
+++ b/pkg/converters/gateway/gateway_test.go
@@ -37,6 +37,7 @@ import (
 	"github.com/jcmoraisjr/haproxy-ingress/pkg/converters/tracker"
 	convtypes "github.com/jcmoraisjr/haproxy-ingress/pkg/converters/types"
 	"github.com/jcmoraisjr/haproxy-ingress/pkg/haproxy"
+	hatypes "github.com/jcmoraisjr/haproxy-ingress/pkg/haproxy/types"
 	types_helper "github.com/jcmoraisjr/haproxy-ingress/pkg/types/helper_test"
 )
 
@@ -78,6 +79,24 @@ paths:
     weight: 128
 `,
 		},
+		{
+			id: "missing-proto",
+			resConfig: []string{`
+apiVersion: gateway.networking.k8s.io/v1
+kind: Gateway
+metadata:
+  name: web
+  namespace: default
+spec:
+  gatewayClassName: haproxy
+  listeners:
+  - name: h1
+`},
+			config: func(c *testConfig) {
+				c.createHTTPRoute1("default/web", "default/web", "echoserver:8080")
+			},
+			expLogging: `WARN missing protocol on Gateway 'default/web' listener 'h1' for HTTPRoute 'default/web'`,
+		},
 		{
 			id: "cross-namespace-1",
 			config: func(c *testConfig) {
@@ -226,10 +245,12 @@ spec:
   gatewayClassName: haproxy
   listeners:
   - name: h1
+    protocol: HTTP
     allowedRoutes:
       namespaces:
         from: Same
   - name: h2
+    protocol: HTTP
     allowedRoutes:
       namespaces:
         from: Same
@@ -538,6 +559,14 @@ paths:
 }
 
 func TestSyncTCPRouteCore(t *testing.T) {
+	defaultBackend := `
+- id: default_pg__tcprule0
+  endpoints:
+  - ip: 172.17.0.11
+    port: 15432
+    weight: 128
+  modetcp: true
+`
 	runTestSync(t, []testCaseSync{
 		{
 			id: "minimum",
@@ -553,14 +582,35 @@ func TestSyncTCPRouteCore(t *testing.T) {
   proxyprot: false
   tls: []
 `,
-			expBackends: `
-- id: default_pg__tcprule0
-  endpoints:
-  - ip: 172.17.0.11
-    port: 15432
-    weight: 128
-  modetcp: true
-`,
+			expBackends: defaultBackend,
+		},
+		{
+			id: "missing-proto",
+			resConfig: []string{`
+apiVersion: gateway.networking.k8s.io/v1
+kind: Gateway
+metadata:
+  name: web
+  namespace: default
+spec:
+  gatewayClassName: haproxy
+  listeners:
+  - name: h1
+`},
+			config: func(c *testConfig) {
+				c.createTCPRoute1("default/web", "default/web", "echoserver:8080")
+			},
+			expLogging: `WARN missing protocol on Gateway 'default/web' listener 'h1' for TCPRoute 'default/web'`,
+		},
+		{
+			id: "missing-secret-1",
+			config: func(c *testConfig) {
+				c.createGateway2("default/pg", "l1:5432", "crt")
+				c.createTCPRoute1("default/pg", "pg", "postgres:15432")
+				c.createService1("default/postgres", "15432", "172.17.0.11")
+			},
+			expBackends: defaultBackend,
+			expLogging:  `WARN skipping certificate reference on Gateway 'default/pg' listener 'l1': secret not found: 'default/crt'`,
 		},
 	})
 }
@@ -588,13 +638,6 @@ func TestSyncGatewayTLS(t *testing.T) {
   - ip: 172.17.0.11
     port: 8080
     weight: 128
-`
-	defaultHTTPHost := `
-hostname: <default>
-paths:
-- path: /
-  match: prefix
-  backend: default_web__rule0
 `
 	defaultHTTPSHost := `
 hostname: <default>
@@ -616,8 +659,7 @@ tls:
 			expLogging: `
 WARN skipping certificate reference on Gateway 'default/web' listener 'l1': secret not found: 'default/crt'
 `,
-			expDefaultHost: defaultHTTPHost,
-			expBackends:    defaultBackend,
+			expBackends: defaultBackend,
 		},
 		{
 			id: "tls-listener-1",
@@ -654,8 +696,7 @@ WARN skipping certificate reference on Gateway 'default/web' listener 'l1': secr
 			expLogging: `
 WARN skipping certificate reference on Gateway 'default/web' listener 'l1': listener has no certificate reference
 `,
-			expDefaultHost: defaultHTTPHost,
-			expBackends:    defaultBackend,
+			expBackends: defaultBackend,
 		},
 		{
 			id: "tls-listener-reassign-crt-1",
@@ -704,8 +745,8 @@ func TestSyncGatewayTLSPassthrough(t *testing.T) {
 		{
 			id: "passthrough-1",
 			config: func(c *testConfig) {
-				g := c.createGateway1("default/web", "l1")
-				r := c.createHTTPRoute1("default/web", "web", "echoserver:8443")
+				g := c.createGateway1("default/web", "l1:8443")
+				r := c.createTLSRoute1("default/web", "web", "echoserver:8443")
 				c.createService1("default/echoserver", "8443", "172.17.0.11")
 				g.Spec.Listeners[0].TLS = &gatewayv1.GatewayTLSConfig{Mode: &passthrough}
 				r.Spec.Hostnames = append(r.Spec.Hostnames, "domain.local")
@@ -715,230 +756,74 @@ func TestSyncGatewayTLSPassthrough(t *testing.T) {
   paths:
   - path: /
     match: prefix
-    backend: default_web__rule0
-  passthrough: true
-`,
-			expBackends: `
-- id: default_web__rule0
-  endpoints:
-  - ip: 172.17.0.11
-    port: 8443
-    weight: 128
-  modetcp: true
-`,
-		},
-		{
-			id: "passthrough-and-http-first-1",
-			config: func(c *testConfig) {
-				g := c.createGateway1("default/web", "l1,l2")
-				r1 := c.createHTTPRoute2("default/web1", "web:l1", "echoserver1:8080", "/")
-				r2 := c.createHTTPRoute1("default/web2", "web:l2", "echoserver2:8443")
-				c.createService1("default/echoserver1", "8080", "172.17.0.11")
-				c.createService1("default/echoserver2", "8443", "172.17.0.12")
-				g.Spec.Listeners[1].TLS = &gatewayv1.GatewayTLSConfig{Mode: &passthrough}
-				r1.Spec.Hostnames = []gatewayv1.Hostname{"domain.local"}
-				r2.Spec.Hostnames = []gatewayv1.Hostname{"domain.local"}
-			},
-			expHosts: `
-- hostname: domain.local
-  paths:
-  - path: /
-    match: prefix
-    backend: default_web2__rule0
+    backend: default_web__tlsrule0
   passthrough: true
-  httppassback: default_web1__rule0
 `,
 			expBackends: `
-- id: default_web1__rule0
+- id: default_web__tlsrule0
   endpoints:
   - ip: 172.17.0.11
-    port: 8080
-    weight: 128
-- id: default_web2__rule0
-  endpoints:
-  - ip: 172.17.0.12
     port: 8443
     weight: 128
   modetcp: true
 `,
 		},
 		{
-			id: "passthrough-and-http-last-1",
+			id: "passthrough-dup-1",
 			config: func(c *testConfig) {
-				g := c.createGateway1("default/web", "l1,l2")
-				r1 := c.createHTTPRoute1("default/web1", "web:l1", "echoserver1:8443")
-				r2 := c.createHTTPRoute2("default/web2", "web:l2", "echoserver2:8080", "/")
-				c.createService1("default/echoserver1", "8443", "172.17.0.11")
-				c.createService1("default/echoserver2", "8080", "172.17.0.12")
-				g.Spec.Listeners[0].TLS = &gatewayv1.GatewayTLSConfig{Mode: &passthrough}
-				r1.Spec.Hostnames = []gatewayv1.Hostname{"domain.local"}
-				r2.Spec.Hostnames = []gatewayv1.Hostname{"domain.local"}
-			},
-			expHosts: `
-- hostname: domain.local
-  paths:
-  - path: /
-    match: prefix
-    backend: default_web1__rule0
-  passthrough: true
-  httppassback: default_web2__rule0
-`,
-			expBackends: `
-- id: default_web1__rule0
-  endpoints:
-  - ip: 172.17.0.11
-    port: 8443
-    weight: 128
-  modetcp: true
-- id: default_web2__rule0
-  endpoints:
-  - ip: 172.17.0.12
-    port: 8080
-    weight: 128
-`,
-		},
-		{
-			id: "passthrough-and-http-dup-passthrough-1",
-			config: func(c *testConfig) {
-				g := c.createGateway1("default/web", "l1,l2,l3")
-				r1 := c.createHTTPRoute1("default/web1", "web:l1", "echoserver1:8443")
-				r2 := c.createHTTPRoute1("default/web2", "web:l2", "echoserver2:8443")
-				r3 := c.createHTTPRoute2("default/web3", "web:l3", "echoserver3:8080", "/")
+				g := c.createGateway1("default/web", "l1:8443,l2:8443")
+				r1 := c.createTLSRoute1("default/web1", "web:l1", "echoserver1:8443")
+				r2 := c.createTLSRoute1("default/web2", "web:l2", "echoserver2:8443")
 				c.createService1("default/echoserver1", "8443", "172.17.0.11")
 				c.createService1("default/echoserver2", "8443", "172.17.0.12")
-				c.createService1("default/echoserver3", "8080", "172.17.0.13")
 				g.Spec.Listeners[0].TLS = &gatewayv1.GatewayTLSConfig{Mode: &passthrough}
 				g.Spec.Listeners[1].TLS = &gatewayv1.GatewayTLSConfig{Mode: &passthrough}
 				r1.Spec.Hostnames = []gatewayv1.Hostname{"domain.local"}
 				r2.Spec.Hostnames = []gatewayv1.Hostname{"domain.local"}
-				r3.Spec.Hostnames = []gatewayv1.Hostname{"domain.local"}
 			},
 			expLogging: `
-WARN skipping redeclared ssl-passthrough root path on HTTPRoute 'default/web2'
+WARN skipping redeclared ssl-passthrough hostname 'domain.local' on TLSRoute 'default/web2'
 `,
 			expHosts: `
 - hostname: domain.local
   paths:
   - path: /
     match: prefix
-    backend: default_web1__rule0
+    backend: default_web1__tlsrule0
   passthrough: true
-  httppassback: default_web3__rule0
 `,
 			expBackends: `
-- id: default_web1__rule0
+- id: default_web1__tlsrule0
   endpoints:
   - ip: 172.17.0.11
     port: 8443
     weight: 128
   modetcp: true
-- id: default_web2__rule0
+- id: default_web2__tlsrule0
   endpoints:
   - ip: 172.17.0.12
     port: 8443
     weight: 128
   modetcp: true
-- id: default_web3__rule0
-  endpoints:
-  - ip: 172.17.0.13
-    port: 8080
-    weight: 128
 `,
 		},
 		{
-			id: "passthrough-and-http-dup-http-1",
+			// HTTPRoute is always processed first, so we have test on one single direction
+			id: "passthrough-having-matching-http-1",
 			config: func(c *testConfig) {
-				g := c.createGateway1("default/web", "l1,l2,l3")
-				r1 := c.createHTTPRoute1("default/web1", "web:l1", "echoserver1:8443")
-				r2 := c.createHTTPRoute2("default/web2", "web:l2", "echoserver2:8080", "/")
-				r3 := c.createHTTPRoute2("default/web3", "web:l3", "echoserver3:8080", "/")
-				c.createService1("default/echoserver1", "8443", "172.17.0.11")
-				c.createService1("default/echoserver2", "8080", "172.17.0.12")
-				c.createService1("default/echoserver3", "8080", "172.17.0.13")
-				g.Spec.Listeners[0].TLS = &gatewayv1.GatewayTLSConfig{Mode: &passthrough}
-				r1.Spec.Hostnames = []gatewayv1.Hostname{"domain.local"}
-				r2.Spec.Hostnames = []gatewayv1.Hostname{"domain.local"}
-				r3.Spec.Hostnames = []gatewayv1.Hostname{"domain.local"}
-			},
-			expLogging: `
-WARN skipping redeclared http root path on HTTPRoute 'default/web3'
-`,
-			expHosts: `
-- hostname: domain.local
-  paths:
-  - path: /
-    match: prefix
-    backend: default_web1__rule0
-  passthrough: true
-  httppassback: default_web2__rule0
-`,
-			expBackends: `
-- id: default_web1__rule0
-  endpoints:
-  - ip: 172.17.0.11
-    port: 8443
-    weight: 128
-  modetcp: true
-- id: default_web2__rule0
-  endpoints:
-  - ip: 172.17.0.12
-    port: 8080
-    weight: 128
-- id: default_web3__rule0
-  endpoints:
-  - ip: 172.17.0.13
-    port: 8080
-    weight: 128
-`,
-		},
-		{
-			id: "passthrough-and-http-path-1",
-			config: func(c *testConfig) {
-				g := c.createGateway1("default/web", "l1,l2")
-				r1 := c.createHTTPRoute2("default/web1", "web:l1", "echoserver1:8080", "/app")
-				r2 := c.createHTTPRoute1("default/web2", "web:l2", "echoserver2:8443")
+				c.createSecret1("default/crt")
+				g := c.createGateway2("default/web", "l0:8443,l1:8443", "crt")
+				r1 := c.createHTTPRoute1("default/web", "web:l0", "echoserver1:8080")
+				r2 := c.createTLSRoute1("default/web", "web:l1", "echoserver2:8443")
 				c.createService1("default/echoserver1", "8080", "172.17.0.11")
 				c.createService1("default/echoserver2", "8443", "172.17.0.12")
+				g.Spec.Listeners[0].Protocol = gatewayv1.HTTPSProtocolType
 				g.Spec.Listeners[1].TLS = &gatewayv1.GatewayTLSConfig{Mode: &passthrough}
 				r1.Spec.Hostnames = []gatewayv1.Hostname{"domain.local"}
 				r2.Spec.Hostnames = []gatewayv1.Hostname{"domain.local"}
 			},
-			expHosts: `
-- hostname: domain.local
-  paths:
-  - path: /app
-    match: prefix
-    backend: default_web1__rule0
-  - path: /
-    match: prefix
-    backend: default_web2__rule0
-  passthrough: true
-`,
-			expBackends: `
-- id: default_web1__rule0
-  endpoints:
-  - ip: 172.17.0.11
-    port: 8080
-    weight: 128
-- id: default_web2__rule0
-  endpoints:
-  - ip: 172.17.0.12
-    port: 8443
-    weight: 128
-  modetcp: true
-`,
-		},
-		{
-			id: "passthrough-with-match-1",
-			config: func(c *testConfig) {
-				g := c.createGateway1("default/web", "l1")
-				r1 := c.createHTTPRoute2("default/web", "web", "echoserver:8443", "/app")
-				c.createService1("default/echoserver", "8443", "172.17.0.11")
-				g.Spec.Listeners[0].TLS = &gatewayv1.GatewayTLSConfig{Mode: &passthrough}
-				r1.Spec.Hostnames = []gatewayv1.Hostname{"domain.local"}
-			},
 			expLogging: `
-WARN ignoring match from HTTPRoute 'default/web': backend is TCP or SSL Passthrough
+WARN skipping hostname 'domain.local' on TLSRoute 'default/web': hostname already declared as HTTP
 `,
 			expHosts: `
 - hostname: domain.local
@@ -946,12 +831,18 @@ WARN ignoring match from HTTPRoute 'default/web': backend is TCP or SSL Passthro
   - path: /
     match: prefix
     backend: default_web__rule0
-  passthrough: true
+  tls:
+    tlsfilename: /tls/default/crt.pem
 `,
 			expBackends: `
 - id: default_web__rule0
   endpoints:
   - ip: 172.17.0.11
+    port: 8080
+    weight: 128
+- id: default_web__tlsrule0
+  endpoints:
+  - ip: 172.17.0.12
     port: 8443
     weight: 128
   modetcp: true
@@ -1034,6 +925,7 @@ func (c *testConfig) createConverter() gateway.Config {
 			Cache:         c.cache,
 			Logger:        c.logger,
 			Tracker:       c.tracker,
+			HasTLSRouteA2: true,
 			HasTCPRouteA2: true,
 		},
 		c.hconfig,
@@ -1110,6 +1002,15 @@ spec:
 		}
 		l.Name = gatewayv1.SectionName(lname)
 		l.Port = lport
+		// TODO hardcoded for now, use integration test's framework instead in the future
+		switch lport {
+		case 5432:
+			l.Protocol = gatewayv1.TCPProtocolType
+		case 8443:
+			l.Protocol = gatewayv1.TLSProtocolType
+		default:
+			l.Protocol = gatewayv1.HTTPProtocolType
+		}
 		from := gatewayv1.NamespacesFromSame
 		var selector *v1.LabelSelector
 		if lselector != "" {
@@ -1213,6 +1114,12 @@ func (c *testConfig) createHTTPRoute2(name, parent, service, paths string) *gate
 	return r
 }
 
+func (c *testConfig) createTLSRoute1(name, parent, services string) *gatewayv1alpha2.TLSRoute {
+	r := CreateObject(c.createRoute("TLSRoute", "v1alpha2", name, parent, services)).(*gatewayv1alpha2.TLSRoute)
+	c.cache.TLSRouteList = append(c.cache.TLSRouteList, r)
+	return r
+}
+
 func (c *testConfig) createTCPRoute1(name, parent, services string) *gatewayv1alpha2.TCPRoute {
 	r := CreateObject(c.createRoute("TCPRoute", "v1alpha2", name, parent, services)).(*gatewayv1alpha2.TCPRoute)
 	c.cache.TCPRouteList = append(c.cache.TCPRouteList, r)
@@ -1253,7 +1160,10 @@ func (c *testConfig) compareText(id string, actual, expected string) {
 }
 
 func (c *testConfig) compareConfigDefaultHost(id string, expected string) {
-	host := c.hconfig.Hosts().DefaultHost()
+	var host *hatypes.Host
+	if f := c.hconfig.Frontends().Items(); len(f) > 0 {
+		host = f[0].DefaultHost()
+	}
 	if host != nil {
 		c.compareText(id, conv_helper.MarshalHost(host), expected)
 	} else {
@@ -1262,7 +1172,11 @@ func (c *testConfig) compareConfigDefaultHost(id string, expected string) {
 }
 
 func (c *testConfig) compareConfigHosts(id string, expected string) {
-	c.compareText(id, conv_helper.MarshalHosts(c.hconfig.Hosts().BuildSortedItems()...), expected)
+	var hosts []*hatypes.Host
+	if f := c.hconfig.Frontends().Items(); len(f) > 0 {
+		hosts = f[0].BuildSortedHosts()
+	}
+	c.compareText(id, conv_helper.MarshalHosts(hosts...), expected)
 }
 
 func (c *testConfig) compareConfigTCPServices(id string, expected string) {
diff --git a/pkg/converters/helper_test/cachemock.go b/pkg/converters/helper_test/cachemock.go
index 1a0f2c8c..341a0fcf 100644
--- a/pkg/converters/helper_test/cachemock.go
+++ b/pkg/converters/helper_test/cachemock.go
@@ -49,6 +49,7 @@ type CacheMock struct {
 	SvcList      []*api.Service
 	//
 	HTTPRouteList    []*gatewayv1.HTTPRoute
+	TLSRouteList     []*gatewayv1alpha2.TLSRoute
 	TCPRouteList     []*gatewayv1alpha2.TCPRoute
 	GatewayList      []*gatewayv1.Gateway
 	GatewayClassList []*gatewayv1.GatewayClass
@@ -142,6 +143,10 @@ func (c *CacheMock) GetTCPRouteList() ([]*gatewayv1alpha2.TCPRoute, error) {
 	return c.TCPRouteList, nil
 }
 
+func (c *CacheMock) GetTLSRouteList() ([]*gatewayv1alpha2.TLSRoute, error) {
+	return c.TLSRouteList, nil
+}
+
 // GetGatewayA2 ...
 func (c *CacheMock) GetGatewayA2(namespace, name string) (*gatewayv1alpha2.Gateway, error) {
 	return nil, fmt.Errorf("missing implementation")
diff --git a/pkg/converters/helper_test/marshal.go b/pkg/converters/helper_test/marshal.go
index 12393865..ee279489 100644
--- a/pkg/converters/helper_test/marshal.go
+++ b/pkg/converters/helper_test/marshal.go
@@ -53,7 +53,6 @@ type (
 		RootRedirect string  `yaml:",omitempty"`
 		TLS          tlsMock `yaml:",omitempty"`
 		Passthrough  bool    `yaml:",omitempty"`
-		HTTPPassBack string  `yaml:",omitempty"`
 	}
 	pathMock struct {
 		Path      string
@@ -154,8 +153,7 @@ func marshalHosts(hafronts ...*hatypes.Host) []hostMock {
 			Paths:        paths,
 			RootRedirect: f.RootRedirect,
 			TLS:          tlsMock{TLSFilename: f.TLS.TLSFilename},
-			Passthrough:  f.SSLPassthrough(),
-			HTTPPassBack: f.HTTPPassthroughBackend,
+			Passthrough:  f.SSLPassthrough,
 		})
 	}
 	return hosts
diff --git a/pkg/converters/ingress/annotations/backend_test.go b/pkg/converters/ingress/annotations/backend_test.go
index 051d71ac..326f3b22 100644
--- a/pkg/converters/ingress/annotations/backend_test.go
+++ b/pkg/converters/ingress/annotations/backend_test.go
@@ -525,8 +525,9 @@ func TestAuthExternal(t *testing.T) {
 	for i, test := range testCase {
 		c := setup(t)
 		u := c.createUpdater()
-		c.haproxy.Frontend().AuthProxy.RangeStart = 4001
-		c.haproxy.Frontend().AuthProxy.RangeEnd = 4009
+		f := c.haproxy.Frontends()
+		f.AuthProxy.RangeStart = 4001
+		f.AuthProxy.RangeEnd = 4009
 		if test.isExternal {
 			c.haproxy.Global().External.IsExternal = true
 		}
@@ -565,9 +566,9 @@ func TestAuthExternal(t *testing.T) {
 		}
 		d := c.createBackendMappingData("default/app", source, defaults, ann, []string{"/"})
 		u.buildBackendAuthExternal(d)
-		back := d.backend.Paths[0].AuthExternal
+		back := d.backend.Paths[0].AuthExtBack
 		var iplist []string
-		bindList := c.haproxy.Frontend().AuthProxy.BindList
+		bindList := f.AuthProxy.BindList
 		if len(bindList) > 0 {
 			auth := c.haproxy.Backends().FindBackendID(bindList[0].Backend)
 			for _, ep := range auth.Endpoints {
@@ -2053,7 +2054,10 @@ WARN oauth2_proxy on ingress 'default/ing1' needs Lua json module, install lua-j
 		Name:      "ing1",
 		Type:      "ingress",
 	}
-	annDefault := map[string]string{ingtypes.BackOAuthHeaders: "X-Auth-Request-Email"}
+	annDefault := map[string]string{
+		ingtypes.FrontHTTPPort:    "8080",
+		ingtypes.BackOAuthHeaders: "X-Auth-Request-Email",
+	}
 	for i, test := range testCases {
 		c := setup(t)
 		d := c.createBackendMappingData("default/app", source, annDefault, test.ann, []string{})
@@ -2064,12 +2068,12 @@ WARN oauth2_proxy on ingress 'default/ing1' needs Lua json module, install lua-j
 		if test.backend != "" {
 			b := strings.Split(test.backend, ":")
 			backend := c.haproxy.Backends().AcquireBackend(b[0], b[1], "8080")
-			c.haproxy.Hosts().AcquireHost("app.local").AddPath(backend, b[2], hatypes.MatchBegin)
+			c.haproxy.Frontends().AcquireFrontend(8080, false).AcquireHost("app.local").AddPath(backend, b[2], hatypes.MatchBegin)
 		}
 		c.createUpdater().buildBackendOAuth(d)
 		actual := map[string]hatypes.AuthExternal{}
 		for _, path := range d.backend.Paths {
-			actual[path.Path()] = path.AuthExternal
+			actual[path.Path()] = path.AuthExtBack
 		}
 		for k, v := range test.authExp {
 			if v.AuthBackendName != "" {
@@ -2123,8 +2127,8 @@ func TestRewriteURL(t *testing.T) {
 			ann = map[string]string{ingtypes.BackRewriteTarget: test.input}
 		}
 		d := c.createBackendData("default/app", &test.source, map[string]string{}, map[string]string{})
-		d.backend.AddBackendPath(hatypes.CreateHostPathLink("d1.local", "/", hatypes.MatchBegin))
-		d.mapper.AddAnnotations(&test.source, hatypes.CreateHostPathLink("d1.local", "/", hatypes.MatchBegin), ann)
+		d.backend.AddPath(&hatypes.Path{Link: hatypes.CreatePathLink("/", hatypes.MatchBegin)})
+		d.mapper.AddAnnotations(&test.source, hatypes.CreatePathLink("/", hatypes.MatchBegin), ann)
 		c.createUpdater().buildBackendRewriteURL(d)
 		actual := d.backend.Paths[0].RewriteURL
 		c.compareObjects("rewrite", i, actual, test.expected)
diff --git a/pkg/converters/ingress/annotations/global_test.go b/pkg/converters/ingress/annotations/global_test.go
index 9e094767..76a3174d 100644
--- a/pkg/converters/ingress/annotations/global_test.go
+++ b/pkg/converters/ingress/annotations/global_test.go
@@ -79,81 +79,12 @@ func TestAuthProxy(t *testing.T) {
 			ingtypes.GlobalAuthProxy: test.input,
 		})
 		c.createUpdater().buildGlobalAuthProxy(d)
-		c.compareObjects("bind", i, c.haproxy.Frontend().AuthProxy, test.expected)
+		c.compareObjects("bind", i, c.haproxy.Frontends().AuthProxy, test.expected)
 		c.logger.CompareLogging(test.logging)
 		c.teardown()
 	}
 }
 
-func TestBind(t *testing.T) {
-	testCases := []struct {
-		ann      map[string]string
-		expected hatypes.GlobalBindConfig
-	}{
-		// 0
-		{
-			ann: map[string]string{},
-			expected: hatypes.GlobalBindConfig{
-				HTTPBind:  "*:80",
-				HTTPSBind: "*:443",
-			},
-		},
-		// 1
-		{
-			ann: map[string]string{
-				ingtypes.GlobalBindHTTP: ":80,:8080",
-			},
-			expected: hatypes.GlobalBindConfig{
-				HTTPBind:  ":80,:8080",
-				HTTPSBind: "*:443",
-			},
-		},
-		// 2
-		{
-			ann: map[string]string{
-				ingtypes.GlobalBindHTTPS: ":443,:8443",
-			},
-			expected: hatypes.GlobalBindConfig{
-				HTTPBind:  "*:80",
-				HTTPSBind: ":443,:8443",
-			},
-		},
-		// 3
-		{
-			ann: map[string]string{
-				ingtypes.GlobalBindIPAddrHTTP: "127.0.0.1",
-			},
-			expected: hatypes.GlobalBindConfig{
-				HTTPBind:  "127.0.0.1:80",
-				HTTPSBind: "127.0.0.1:443",
-			},
-		},
-		// 4
-		{
-			ann: map[string]string{
-				ingtypes.GlobalHTTPPort:  "8080",
-				ingtypes.GlobalHTTPSPort: "8443",
-			},
-			expected: hatypes.GlobalBindConfig{
-				HTTPBind:  "*:8080",
-				HTTPSBind: "*:8443",
-			},
-		},
-	}
-	for i, test := range testCases {
-		c := setup(t)
-		d := c.createGlobalData(map[string]string{
-			ingtypes.GlobalHTTPPort:       "80",
-			ingtypes.GlobalHTTPSPort:      "443",
-			ingtypes.GlobalBindIPAddrHTTP: "*",
-		})
-		d.mapper.AddAnnotations(nil, hatypes.CreateHostPathLink("-", "-", hatypes.MatchBegin), test.ann)
-		c.createUpdater().buildGlobalBind(d)
-		c.compareObjects("bind", i, d.global.Bind, test.expected)
-		c.teardown()
-	}
-}
-
 func TestCloseSessions(t *testing.T) {
 	testCases := []struct {
 		annDuration string
@@ -701,61 +632,6 @@ func TestForwardFor(t *testing.T) {
 	}
 }
 
-func TestFrontingProxy(t *testing.T) {
-	testCases := []struct {
-		ann      map[string]string
-		expected hatypes.GlobalBindConfig
-	}{
-		// 0
-		{
-			ann: map[string]string{
-				ingtypes.GlobalHTTPStoHTTPPort: "8000",
-			},
-			expected: hatypes.GlobalBindConfig{
-				FrontingBind: ":8000",
-			},
-		},
-		// 1
-		{
-			ann: map[string]string{
-				ingtypes.GlobalFrontingProxyPort: "9000",
-			},
-			expected: hatypes.GlobalBindConfig{
-				FrontingBind: ":9000",
-			},
-		},
-		// 2
-		{
-			ann: map[string]string{
-				ingtypes.GlobalHTTPStoHTTPPort:   "9000",
-				ingtypes.GlobalBindFrontingProxy: ":7000",
-			},
-			expected: hatypes.GlobalBindConfig{
-				FrontingBind: ":7000",
-			},
-		},
-		// 3
-		{
-			ann: map[string]string{
-				ingtypes.GlobalFrontingProxyPort: "8000",
-				ingtypes.GlobalBindFrontingProxy: "127.0.0.1:7000",
-			},
-			expected: hatypes.GlobalBindConfig{
-				FrontingBind: "127.0.0.1:7000",
-			},
-		},
-	}
-	frontingSockID := 10011
-	for i, test := range testCases {
-		c := setup(t)
-		d := c.createGlobalData(test.ann)
-		c.createUpdater().buildGlobalHTTPStoHTTP(d)
-		test.expected.FrontingSockID = frontingSockID
-		c.compareObjects("fronting proxy", i, d.global.Bind, test.expected)
-		c.teardown()
-	}
-}
-
 func TestDisableCpuMap(t *testing.T) {
 	testCases := []struct {
 		ann      map[string]string
@@ -930,7 +806,7 @@ func TestSecurity(t *testing.T) {
 	for i, test := range testCases {
 		c := setup(t)
 		d := c.createGlobalData(test.ann)
-		c.createUpdater().buildSecurity(d)
+		c.createUpdater().buildGlobalSecurity(d)
 		c.compareObjects("fronting proxy", i, d.global.Security, test.expected)
 		c.logger.CompareLogging(test.logging)
 		c.teardown()
diff --git a/pkg/converters/ingress/annotations/host_test.go b/pkg/converters/ingress/annotations/host_test.go
index 63fd0f2e..b2650552 100644
--- a/pkg/converters/ingress/annotations/host_test.go
+++ b/pkg/converters/ingress/annotations/host_test.go
@@ -21,16 +21,133 @@ import (
 
 	ingtypes "github.com/jcmoraisjr/haproxy-ingress/pkg/converters/ingress/types"
 	hatypes "github.com/jcmoraisjr/haproxy-ingress/pkg/haproxy/types"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 )
 
+func TestFrontingProxy(t *testing.T) {
+	testCases := map[string]struct {
+		ann      map[string]string
+		expected hatypes.Frontend
+	}{
+		"test01": {
+			ann: map[string]string{
+				ingtypes.FrontHTTPStoHTTPPort: "8000",
+			},
+			expected: hatypes.Frontend{
+				IsFrontingProxy: true,
+				Bind:            ":8000",
+			},
+		},
+		"test02": {
+			ann: map[string]string{
+				ingtypes.FrontFrontingProxyPort: "9000",
+			},
+			expected: hatypes.Frontend{
+				IsFrontingProxy: true,
+				Bind:            ":9000",
+			},
+		},
+		"test03": {
+			ann: map[string]string{
+				ingtypes.FrontHTTPStoHTTPPort:   "9000",
+				ingtypes.FrontBindFrontingProxy: ":7000",
+			},
+			expected: hatypes.Frontend{
+				IsFrontingProxy: true,
+				Bind:            ":7000",
+			},
+		},
+		"test04": {
+			ann: map[string]string{
+				ingtypes.FrontFrontingProxyPort: "8000",
+				ingtypes.FrontBindFrontingProxy: "127.0.0.1:7000",
+			},
+			expected: hatypes.Frontend{
+				IsFrontingProxy: true,
+				Bind:            "127.0.0.1:7000",
+			},
+		},
+	}
+	source := &Source{Namespace: "default", Name: "ing1", Type: "ingress"}
+	for name, test := range testCases {
+		t.Run(name, func(t *testing.T) {
+			c := setup(t)
+			defer c.teardown()
+			d := c.createFrontData(source, test.ann, map[string]string{})
+			c.createUpdater().buildHTTPFrontFrontingProxy(d)
+			require.Equal(t, &test.expected, d.front)
+		})
+	}
+}
+
+func TestFrontendBind(t *testing.T) {
+	testCases := map[string]struct {
+		ann      map[string]string
+		expHTTP  hatypes.Frontend
+		expHTTPS hatypes.Frontend
+	}{
+		"01": {
+			ann:      map[string]string{},
+			expHTTP:  hatypes.Frontend{Bind: "*:80"},
+			expHTTPS: hatypes.Frontend{Bind: "*:443"},
+		},
+		"02": {
+			ann: map[string]string{
+				ingtypes.FrontBindHTTP: ":80,:8080",
+			},
+			expHTTP:  hatypes.Frontend{Bind: ":80,:8080"},
+			expHTTPS: hatypes.Frontend{Bind: "*:443"},
+		},
+		"03": {
+			ann: map[string]string{
+				ingtypes.FrontBindHTTPS: ":443,:8443",
+			},
+			expHTTP:  hatypes.Frontend{Bind: "*:80"},
+			expHTTPS: hatypes.Frontend{Bind: ":443,:8443"},
+		},
+		"04": {
+			ann: map[string]string{
+				ingtypes.FrontBindIPAddrHTTP: "127.0.0.1",
+			},
+			expHTTP:  hatypes.Frontend{Bind: "127.0.0.1:80"},
+			expHTTPS: hatypes.Frontend{Bind: "127.0.0.1:443"},
+		},
+		"05": {
+			ann: map[string]string{
+				ingtypes.FrontHTTPPort:  "8080",
+				ingtypes.FrontHTTPSPort: "8443",
+			},
+			expHTTP:  hatypes.Frontend{Bind: "*:8080"},
+			expHTTPS: hatypes.Frontend{Bind: "*:8443"},
+		},
+	}
+	source := &Source{Namespace: "default", Name: "ing1", Type: "ingress"}
+	annDefault := map[string]string{
+		ingtypes.FrontHTTPPort:       "80",
+		ingtypes.FrontHTTPSPort:      "443",
+		ingtypes.FrontBindIPAddrHTTP: "*",
+	}
+	for name, test := range testCases {
+		t.Run(name, func(t *testing.T) {
+			c := setup(t)
+			defer c.teardown()
+			d := c.createFrontData(source, test.ann, annDefault)
+			c.createUpdater().buildHTTPFrontBind(d)
+			assert.Equal(t, &test.expHTTP, d.front, "HTTPbind")
+			c.createUpdater().buildHTTPSFrontBind(d)
+			assert.Equal(t, &test.expHTTPS, d.front, "HTTPSbind")
+		})
+	}
+}
+
 func TestBuildHostRedirect(t *testing.T) {
 	testCases := []struct {
-		annPrev    map[string]string
-		ann        map[string]string
-		annDefault map[string]string
-		nopath     bool
-		expected   hatypes.HostRedirectConfig
-		logging    string
+		annPrev  map[string]string
+		ann      map[string]string
+		nopath   bool
+		expected hatypes.HostRedirectConfig
+		logging  string
 	}{
 		// 0
 		{
@@ -54,7 +171,7 @@ func TestBuildHostRedirect(t *testing.T) {
 			ann: map[string]string{
 				ingtypes.HostRedirectFrom: "www.d.local",
 			},
-			logging: `WARN ignoring redirect from 'www.d.local' on ingress 'default/ing1', it's already targeting to 'dprev.local'`,
+			logging: `WARN ignoring redirect from 'www.d.local' port 8080 on ingress 'default/ing1', it's already targeting to 'dprev.local'`,
 		},
 		// 3
 		{
@@ -74,7 +191,7 @@ func TestBuildHostRedirect(t *testing.T) {
 			ann: map[string]string{
 				ingtypes.HostRedirectFromRegex: "[a-z]+\\.d\\.local",
 			},
-			logging: `WARN ignoring regex redirect from '[a-z]+\.d\.local' on ingress 'default/ing1', it's already targeting to 'dprev.local'`,
+			logging: `WARN ignoring regex redirect from '[a-z]+\.d\.local' port 8080 on ingress 'default/ing1', it's already targeting to 'dprev.local'`,
 		},
 		// 5
 		{
@@ -92,7 +209,7 @@ func TestBuildHostRedirect(t *testing.T) {
 			ann: map[string]string{
 				ingtypes.HostRedirectFrom: "*.d.local",
 			},
-			logging: `WARN ignoring redirect from '*.d.local' on ingress 'default/ing1', it's already targeting to 'dprev.local'`,
+			logging: `WARN ignoring redirect from '*.d.local' port 8080 on ingress 'default/ing1', it's already targeting to 'dprev.local'`,
 		},
 		// 7
 		{
@@ -107,16 +224,18 @@ func TestBuildHostRedirect(t *testing.T) {
 	}
 	sprev := &Source{Namespace: "prev", Name: "ingprev", Type: "ingress"}
 	source := &Source{Namespace: "default", Name: "ing1", Type: "ingress"}
+	annDefault := map[string]string{
+		ingtypes.FrontHTTPPort:  "8080",
+		ingtypes.FrontHTTPSPort: "8443",
+	}
 	for i, test := range testCases {
 		c := setup(t)
-		if test.annDefault == nil {
-			test.annDefault = map[string]string{}
-		}
 		b := c.haproxy.Backends().AcquireBackend("default", "d", "8080")
-		dprev := c.createHostData(sprev, test.annPrev, test.annDefault)
-		d := c.createHostData(source, test.ann, test.annDefault)
-		dprev.host = c.haproxy.Hosts().AcquireHost("dprev.local")
-		d.host = c.haproxy.Hosts().AcquireHost("d.local")
+		dprev := c.createHostData(sprev, test.annPrev, annDefault)
+		d := c.createHostData(source, test.ann, annDefault)
+		f := c.haproxy.Frontends().AcquireFrontend(8080, false)
+		dprev.host = f.AcquireHost("dprev.local")
+		d.host = f.AcquireHost("d.local")
 		if !test.nopath {
 			dprev.host.AddPath(b, "/", hatypes.MatchPrefix)
 			d.host.AddPath(b, "/", hatypes.MatchPrefix)
diff --git a/pkg/converters/ingress/annotations/mapper_test.go b/pkg/converters/ingress/annotations/mapper_test.go
index 53261977..3d0f847e 100644
--- a/pkg/converters/ingress/annotations/mapper_test.go
+++ b/pkg/converters/ingress/annotations/mapper_test.go
@@ -55,10 +55,10 @@ var (
 )
 
 func TestAddAnnotation(t *testing.T) {
-	pathRoot := hatypes.CreateHostPathLink("domain.local", "/", hatypes.MatchBegin)
-	pathApp := hatypes.CreateHostPathLink("domain.local", "/app", hatypes.MatchBegin)
-	pathPath := hatypes.CreateHostPathLink("domain.local", "/path", hatypes.MatchBegin)
-	pathURL := hatypes.CreateHostPathLink("domain.local", "/url", hatypes.MatchBegin)
+	pathRoot := hatypes.CreatePathLink("/", hatypes.MatchBegin)
+	pathApp := hatypes.CreatePathLink("/app", hatypes.MatchBegin)
+	pathPath := hatypes.CreatePathLink("/path", hatypes.MatchBegin)
+	pathURL := hatypes.CreatePathLink("/url", hatypes.MatchBegin)
 	testCases := []struct {
 		ann     []ann
 		getKey  string
@@ -150,8 +150,8 @@ func TestAddAnnotation(t *testing.T) {
 }
 
 func TestGetAnnotation(t *testing.T) {
-	pathRoot := hatypes.CreateHostPathLink("domain.local", "/", hatypes.MatchBegin)
-	pathURL := hatypes.CreateHostPathLink("domain.local", "/url", hatypes.MatchBegin)
+	pathRoot := hatypes.CreatePathLink("/", hatypes.MatchBegin)
+	pathURL := hatypes.CreatePathLink("/url", hatypes.MatchBegin)
 	testCases := []struct {
 		ann       []ann
 		getKey    string
@@ -282,7 +282,7 @@ func TestGetDefault(t *testing.T) {
 			},
 		},
 	}
-	pathRoot := hatypes.CreateHostPathLink("domain.local", "/", hatypes.MatchBegin)
+	pathRoot := hatypes.CreatePathLink("/", hatypes.MatchBegin)
 	for i, test := range testCases {
 		c := setup(t)
 		mapper := NewMapBuilder(c.logger, test.annDefaults).NewMapper()
diff --git a/pkg/converters/ingress/annotations/updater_test.go b/pkg/converters/ingress/annotations/updater_test.go
index 60f883ce..66cc7ca4 100644
--- a/pkg/converters/ingress/annotations/updater_test.go
+++ b/pkg/converters/ingress/annotations/updater_test.go
@@ -155,7 +155,7 @@ func (c *testConfig) createUpdater() *updater {
 
 func (c *testConfig) createBackendData(svcFullName string, source *Source, ann, annDefault map[string]string) *backData {
 	mapper := NewMapBuilder(c.logger, annDefault).NewMapper()
-	mapper.AddAnnotations(source, hatypes.CreateHostPathLink("domain.local", "/", hatypes.MatchBegin), ann)
+	mapper.AddAnnotations(source, hatypes.CreatePathLink("/", hatypes.MatchBegin), ann)
 	svcName := strings.Split(svcFullName, "/")
 	namespace := svcName[0]
 	name := svcName[1]
@@ -172,14 +172,6 @@ func (c *testConfig) createBackendData(svcFullName string, source *Source, ann,
 	}
 }
 
-const testingHostname = "host.local"
-
-type hostResolver struct{}
-
-func (h *hostResolver) UseTLS() bool {
-	return true
-}
-
 func (c *testConfig) createBackendMappingData(
 	svcFullName string,
 	source *Source,
@@ -195,22 +187,30 @@ func (c *testConfig) createBackendMappingData(
 	for _, path := range addPaths {
 		paths[path] = struct{}{}
 	}
-	for path := range paths {
-		b := d.backend.AddBackendPath(hatypes.CreateHostPathLink(testingHostname, path, hatypes.MatchBegin))
-		// ignoring ID which isn't the focus of the test
-		// removing on createBackendPaths() as well
-		b.ID = ""
-		b.Host = &hostResolver{}
+	for p := range paths {
+		d.backend.AddPath(&hatypes.Path{
+			Link:     hatypes.CreatePathLink(p, hatypes.MatchBegin),
+			HasHTTPS: true,
+		})
 	}
 	for uri, ann := range urlAnnValue {
-		d.mapper.AddAnnotations(source, hatypes.CreateHostPathLink(testingHostname, uri, hatypes.MatchBegin), ann)
+		d.mapper.AddAnnotations(source, hatypes.CreatePathLink(uri, hatypes.MatchBegin), ann)
 	}
 	return d
 }
 
+func (c *testConfig) createFrontData(source *Source, ann, annDefault map[string]string) *frontData {
+	mapper := NewMapBuilder(c.logger, annDefault).NewMapper()
+	mapper.AddAnnotations(source, hatypes.CreatePathLink("/", hatypes.MatchBegin), ann)
+	return &frontData{
+		front:  &hatypes.Frontend{},
+		mapper: mapper,
+	}
+}
+
 func (c *testConfig) createHostData(source *Source, ann, annDefault map[string]string) *hostData {
 	mapper := NewMapBuilder(c.logger, annDefault).NewMapper()
-	mapper.AddAnnotations(source, hatypes.CreateHostPathLink("domain.local", "/", hatypes.MatchBegin), ann)
+	mapper.AddAnnotations(source, hatypes.CreatePathLink("/", hatypes.MatchBegin), ann)
 	return &hostData{
 		host:   &hatypes.Host{},
 		mapper: mapper,
diff --git a/pkg/converters/ingress/ingress_test.go b/pkg/converters/ingress/ingress_test.go
index ed118165..7c8e8a0e 100644
--- a/pkg/converters/ingress/ingress_test.go
+++ b/pkg/converters/ingress/ingress_test.go
@@ -46,6 +46,11 @@ import (
 	types_helper "github.com/jcmoraisjr/haproxy-ingress/pkg/types/helper_test"
 )
 
+const (
+	TestPortHTTP  = 28080
+	TestPortHTTPS = 28443
+)
+
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  *
  *  CORE INGRESS
@@ -896,7 +901,7 @@ func TestPathType(t *testing.T) {
 		ing := c.createIng1Ann("default/echo", "echo.localdomain", "/", "echo:8080", ann)
 		ing.Spec.Rules[0].HTTP.Paths[0].PathType = test.pathType
 		c.Sync(ing)
-		match := c.hconfig.Hosts().AcquireHost("echo.localdomain").FindPath("/", test.expected)
+		match := c.hconfig.Frontends().AcquireFrontend(TestPortHTTP, false).AcquireHost("echo.localdomain").FindPath("/", test.expected)
 		if len(match) == 0 {
 			c.t.Errorf("path type does not match in %d: expected '%s', but wasn't found", i, test.expected)
 		}
@@ -912,7 +917,7 @@ func TestSyncBackendDefault(t *testing.T) {
 	c.createSvc1Auto()
 	c.Sync(c.createIng2("default/echo", "echo:8080"))
 
-	c.compareConfigDefaultFront(`
+	c.compareConfigDefaultHost(`
 hostname: <default>
 paths:
 - path: /
@@ -951,7 +956,7 @@ func TestSyncBackendReuseDefaultSvc(t *testing.T) {
   - path: /app
     backend: system_default_8080`)
 
-	c.compareConfigDefaultFront(`[]`)
+	c.compareConfigDefaultHost(`[]`)
 	c.compareConfigBack(defaultBackendConfig)
 }
 
@@ -966,7 +971,7 @@ func TestSyncDefaultBackendReusedPath1(t *testing.T) {
 		c.createIng2("default/echo2", "echo2:8080"),
 	)
 
-	c.compareConfigDefaultFront(defaultDefaultFrontendConfig)
+	c.compareConfigDefaultHost(defaultDefaultFrontendConfig)
 
 	c.compareConfigBack(`
 - id: default_echo1_8080
@@ -989,7 +994,7 @@ func TestSyncDefaultBackendReusedPath2(t *testing.T) {
 		c.createIng1("default/echo2", hatypes.DefaultHost, "/", "echo2:8080"),
 	)
 
-	c.compareConfigDefaultFront(defaultDefaultFrontendConfig)
+	c.compareConfigDefaultHost(defaultDefaultFrontendConfig)
 
 	c.compareConfigBack(`
 - id: default_echo1_8080
@@ -1016,7 +1021,7 @@ func TestSyncEmptyHost(t *testing.T) {
 	c.createSvc1Auto()
 	c.Sync(c.createIng1("default/echo", "", "/", "echo:8080"))
 
-	c.compareConfigDefaultFront(`
+	c.compareConfigDefaultHost(`
 hostname: <default>
 paths:
 - path: /
@@ -1567,7 +1572,7 @@ WARN using default certificate due to an error reading secret 'default/tls1' on
 		if test.expDefaultFront == "" {
 			test.expDefaultFront = "[]"
 		}
-		c.compareConfigDefaultFront(test.expDefaultFront)
+		c.compareConfigDefaultHost(test.expDefaultFront)
 		c.compareConfigBack(test.expBack)
 		c.logger.CompareLogging(test.logging)
 
@@ -2422,26 +2427,39 @@ func TestSyncAnnPassthrough(t *testing.T) {
 			}),
 	)
 
-	c.compareConfigFront(`
+	c.compareConfigFrontPort(TestPortHTTP, `
 - hostname: echo1.example.com
   paths:
   - path: /app
     backend: default_echo_8080
+  - path: /
+    backend: default_echo_8080
+- hostname: echo2.example.com
+  paths:
+  - path: /
+    backend: _redirect_https
+`)
+	c.compareConfigFrontPort(TestPortHTTPS, `
+- hostname: echo1.example.com
+  paths:
   - path: /
     backend: default_echo_8443
+  passthrough: true
 - hostname: echo2.example.com
   paths:
   - path: /
     backend: default_echo_8443
+  passthrough: true
 `)
 
-	c.compareConfigDefaultFront(`
+	c.compareConfigDefaultHostPort(TestPortHTTP, `
 hostname: <default>
 paths:
 - path: /
   backend: default_echo_8443
 rootredirect: /login
 `)
+	c.compareConfigDefaultHostPort(TestPortHTTPS, `[]`)
 
 	c.compareConfigBack(`
 - id: default_echo_8080
@@ -2452,6 +2470,7 @@ rootredirect: /login
   endpoints:
   - ip: 172.17.1.101
     port: 8443
+  modetcp: true
 - id: system_default_8080
   endpoints:
   - ip: 172.17.0.99
@@ -2537,6 +2556,8 @@ func (c *testConfig) createConverter() *converter {
 	defaultConfig := func() map[string]string {
 		return map[string]string{
 			ingtypes.BackInitialWeight: "100",
+			ingtypes.FrontHTTPPort:     strconv.Itoa(TestPortHTTP),
+			ingtypes.FrontHTTPSPort:    strconv.Itoa(TestPortHTTPS),
 		}
 	}
 	return NewIngressConverter(
@@ -2773,6 +2794,9 @@ func (u *updaterMock) UpdateTCPHostConfig(tcpPort *hatypes.TCPServicePort, tcpHo
 	}
 }
 
+func (u *updaterMock) UpdateFrontConfig(front *hatypes.Frontend, mapper *annotations.Mapper) {
+}
+
 func (u *updaterMock) UpdateHostConfig(host *hatypes.Host, mapper *annotations.Mapper) {
 	host.RootRedirect = mapper.Get(ingtypes.HostAppRoot).Value
 }
@@ -2790,17 +2814,47 @@ func (c *testConfig) compareConfigTCPService(expected string) {
 	c.compareText(conv_helper.MarshalTCPServices(c.hconfig.TCPServices().BuildSortedItems()...), expected)
 }
 
+func (c *testConfig) findFrontend() *hatypes.Frontend {
+	if f := c.hconfig.Frontends().FindFrontend(TestPortHTTPS); f != nil {
+		return f
+	}
+	return c.hconfig.Frontends().FindFrontend(TestPortHTTP)
+}
+
 func (c *testConfig) compareConfigFront(expected string) {
-	c.compareText(conv_helper.MarshalHosts(c.hconfig.Hosts().BuildSortedItems()...), expected)
+	var hosts []*hatypes.Host
+	if f := c.findFrontend(); f != nil {
+		hosts = f.BuildSortedHosts()
+	}
+	c.compareText(conv_helper.MarshalHosts(hosts...), expected)
 }
 
-func (c *testConfig) compareConfigDefaultFront(expected string) {
-	host := c.hconfig.Hosts().DefaultHost()
-	if host != nil {
-		c.compareText(conv_helper.MarshalHost(host), expected)
-	} else {
-		c.compareText("[]", expected)
+func (c *testConfig) compareConfigFrontPort(port int32, expected string) {
+	var hosts []*hatypes.Host
+	if f := c.hconfig.Frontends().FindFrontend(port); f != nil {
+		hosts = f.BuildSortedHosts()
+	}
+	c.compareText(conv_helper.MarshalHosts(hosts...), expected)
+}
+
+func (c *testConfig) compareConfigDefaultHost(expected string) {
+	hoststr := "[]"
+	if f := c.findFrontend(); f != nil {
+		if h := f.DefaultHost(); h != nil {
+			hoststr = conv_helper.MarshalHost(h)
+		}
+	}
+	c.compareText(hoststr, expected)
+}
+
+func (c *testConfig) compareConfigDefaultHostPort(port int32, expected string) {
+	hoststr := "[]"
+	if f := c.hconfig.Frontends().FindFrontend(port); f != nil {
+		if h := f.DefaultHost(); h != nil {
+			hoststr = conv_helper.MarshalHost(h)
+		}
 	}
+	c.compareText(hoststr, expected)
 }
 
 func (c *testConfig) compareConfigBack(expected string) {
diff --git a/pkg/haproxy/config_test.go b/pkg/haproxy/config_test.go
index 46ae25a6..85f79707 100644
--- a/pkg/haproxy/config_test.go
+++ b/pkg/haproxy/config_test.go
@@ -20,41 +20,25 @@ import (
 	"testing"
 )
 
-func TestEmptyFrontend(t *testing.T) {
-	c := createConfig(options{})
-	if err := c.WriteFrontendMaps(); err != nil {
-		t.Errorf("error creating frontends: %v", err)
-	}
-	if maps := c.frontend.Maps; maps == nil {
-		t.Error("expected frontend.Maps != nil")
-	}
-	c.hosts.AcquireHost("empty")
-	if err := c.WriteFrontendMaps(); err != nil {
-		t.Errorf("error creating frontends: %v", err)
-	}
-	maps := c.frontend.Maps
-	if maps == nil {
-		t.Error("expected frontend.Maps != nil")
-	}
-}
-
 func TestAcquireHostDiff(t *testing.T) {
 	c := createConfig(options{})
-	f1 := c.hosts.AcquireHost("h1")
-	f2 := c.hosts.AcquireHost("h2")
-	if f1.Hostname != "h1" {
-		t.Errorf("expected %v but was %v", "h1", f1.Hostname)
+	f := c.frontends.AcquireFrontend(8000, false)
+	h1 := f.AcquireHost("h1")
+	h2 := f.AcquireHost("h2")
+	if h1.Hostname != "h1" {
+		t.Errorf("expected %v but was %v", "h1", h1.Hostname)
 	}
-	if f2.Hostname != "h2" {
-		t.Errorf("expected %v but was %v", "h2", f2.Hostname)
+	if h2.Hostname != "h2" {
+		t.Errorf("expected %v but was %v", "h2", h2.Hostname)
 	}
 }
 
 func TestAcquireHostSame(t *testing.T) {
 	c := createConfig(options{})
-	f1 := c.hosts.AcquireHost("h1")
-	f2 := c.hosts.AcquireHost("h1")
-	if f1 != f2 {
+	f := c.frontends.AcquireFrontend(8000, false)
+	h1 := f.AcquireHost("h1")
+	h2 := f.AcquireHost("h1")
+	if h1 != h2 {
 		t.Errorf("expected same host but was different")
 	}
 }
@@ -63,12 +47,13 @@ func TestClear(t *testing.T) {
 	c := createConfig(options{
 		mapsDir: "/tmp/maps",
 	})
-	c.Hosts().AcquireHost("app.local")
+	f := c.frontends.AcquireFrontend(8000, false)
+	_ = f.AcquireHost("app.local")
 	c.Backends().AcquireBackend("default", "app", "8080")
 	if c.options.mapsDir != "/tmp/maps" {
 		t.Error("expected mapsDir == /tmp/maps")
 	}
-	if len(c.Hosts().Items()) != 1 {
+	if len(f.Hosts()) != 1 {
 		t.Error("expected len(hosts) == 1")
 	}
 	if len(c.Backends().Items()) != 1 {
@@ -78,8 +63,8 @@ func TestClear(t *testing.T) {
 	if c.options.mapsDir != "/tmp/maps" {
 		t.Error("expected mapsDir == /tmp/maps")
 	}
-	if len(c.Hosts().Items()) != 0 {
-		t.Error("expected len(hosts) == 0")
+	if len(c.Frontends().Items()) != 0 {
+		t.Error("expected len(Frontends) == 0")
 	}
 	if len(c.Backends().Items()) != 0 {
 		t.Error("expected len(backends) == 0")
diff --git a/pkg/haproxy/dynupdate_test.go b/pkg/haproxy/dynupdate_test.go
index 97378905..50b03f19 100644
--- a/pkg/haproxy/dynupdate_test.go
+++ b/pkg/haproxy/dynupdate_test.go
@@ -18,16 +18,15 @@ package haproxy
 
 import (
 	"fmt"
-	"reflect"
 	"strings"
 	"testing"
 	"time"
 
-	"github.com/kylelemons/godebug/diff"
+	"github.com/stretchr/testify/assert"
 )
 
 func TestDynUpdate(t *testing.T) {
-	testCases := []struct {
+	testCases := map[string]struct {
 		doconfig1 func(c *testConfig)
 		doconfig2 func(c *testConfig)
 		expected  []string
@@ -36,20 +35,17 @@ func TestDynUpdate(t *testing.T) {
 		cmdOutput []string
 		logging   string
 	}{
-		// 0
-		{
+		"test01": {
 			dynamic: true,
 		},
-		// 1
-		{
+		"test02": {
 			doconfig2: func(c *testConfig) {
 				c.config.Global().MaxConn = 1
 			},
 			dynamic: false,
 			logging: `INFO-V(2) need to reload due to config changes: [global]`,
 		},
-		// 2
-		{
+		"test03": {
 			doconfig1: func(c *testConfig) {
 				b := c.config.Backends().AcquireBackend("default", "app", "8080")
 				b.AcquireEndpoint("172.17.0.2", 8080, "")
@@ -72,8 +68,7 @@ set server default_app_8080/srv002 state ready
 set server default_app_8080/srv002 weight 1`,
 			logging: `INFO-V(2) updated endpoint '172.17.0.4:8080' weight '1' state 'ready' on backend/server 'default_app_8080/srv002'`,
 		},
-		// 3
-		{
+		"test04": {
 			doconfig1: func(c *testConfig) {
 				b := c.config.Backends().AcquireBackend("default", "app", "8080")
 				b.AcquireEndpoint("172.17.0.2", 8080, "")
@@ -98,8 +93,7 @@ set server default_app_8080/srv002 state ready
 set server default_app_8080/srv002 weight 1`,
 			logging: `INFO-V(2) updated endpoint '172.17.0.4:8080' weight '1' state 'ready' on backend/server 'default_app_8080/srv002'`,
 		},
-		// 4
-		{
+		"test05": {
 			doconfig1: func(c *testConfig) {
 				b := c.config.Backends().AcquireBackend("default", "app", "8080")
 				b.AcquireEndpoint("172.17.0.2", 8080, "")
@@ -122,8 +116,7 @@ set server default_app_8080/srv001 state ready
 set server default_app_8080/srv001 weight 1`,
 			logging: `INFO-V(2) updated endpoint '172.17.0.4:8080' weight '1' state 'ready' on backend/server 'default_app_8080/srv001'`,
 		},
-		// 5
-		{
+		"test06": {
 			doconfig1: func(c *testConfig) {
 				b := c.config.Backends().AcquireBackend("default", "app", "8080")
 				b.AcquireEndpoint("172.17.0.3", 8080, "")
@@ -150,8 +143,7 @@ set server default_app_8080/srv001 weight 1`,
 INFO-V(2) added endpoints on backend 'default_app_8080'
 INFO-V(2) need to reload due to config changes: [backends]`,
 		},
-		// 6
-		{
+		"test07": {
 			doconfig1: func(c *testConfig) {
 				b := c.config.Backends().AcquireBackend("default", "app", "8080")
 				b.AcquireEndpoint("172.17.0.2", 8080, "")
@@ -174,8 +166,7 @@ set server default_app_8080/srv001 weight 0
 `,
 			logging: `INFO-V(2) disabled endpoint '172.17.0.2:8080' on backend/server 'default_app_8080/srv001'`,
 		},
-		// 7
-		{
+		"test08": {
 			doconfig1: func(c *testConfig) {
 				b := c.config.Backends().AcquireBackend("default", "app", "8080")
 				b.AcquireEndpoint("172.17.0.2", 8080, "")
@@ -197,8 +188,7 @@ set server default_app_8080/srv001 weight 2
 `,
 			logging: `INFO-V(2) updated endpoint '172.17.0.2:8080' weight '2' state 'ready' on backend/server 'default_app_8080/srv001'`,
 		},
-		// 8
-		{
+		"test09": {
 			doconfig1: func(c *testConfig) {
 				b := c.config.Backends().AcquireBackend("default", "app", "8080")
 				b.AddEmptyEndpoint()
@@ -222,8 +212,7 @@ set server default_app_8080/srv001 weight 1
 `,
 			logging: `INFO-V(2) added endpoint '172.17.0.2:8080' weight '1' state 'ready' on backend/server 'default_app_8080/srv001'`,
 		},
-		// 9
-		{
+		"test10": {
 			doconfig1: func(c *testConfig) {
 				b := c.config.Backends().AcquireBackend("default", "app", "8080")
 				b.AddEmptyEndpoint()
@@ -247,8 +236,7 @@ set server default_app_8080/srv001 weight 1
 `,
 			logging: `INFO-V(2) added endpoint '172.17.0.3:8080' weight '1' state 'ready' on backend/server 'default_app_8080/srv001'`,
 		},
-		// 10
-		{
+		"test11": {
 			doconfig1: func(c *testConfig) {
 				b := c.config.Backends().AcquireBackend("default", "app", "8080")
 				b.AcquireEndpoint("172.17.0.2", 8080, "")
@@ -306,8 +294,7 @@ INFO-V(2) disabled endpoint '172.17.0.8:8080' on backend/server 'default_app_808
 INFO-V(2) disabled endpoint '172.17.0.9:8080' on backend/server 'default_app_8080/srv008'
 `,
 		},
-		// 11
-		{
+		"test12": {
 			doconfig1: func(c *testConfig) {
 				b := c.config.Backends().AcquireBackend("default", "app", "8080")
 				b.AcquireEndpoint("172.17.0.2", 8080, "")
@@ -340,8 +327,7 @@ INFO-V(2) disabled endpoint '172.17.0.3:8080' on backend/server 'default_app_808
 INFO-V(2) disabled endpoint '172.17.0.4:8080' on backend/server 'default_app_8080/srv003'
 `,
 		},
-		// 12
-		{
+		"test13": {
 			doconfig1: func(c *testConfig) {
 				b := c.config.Backends().AcquireBackend("default", "app", "8080")
 				b.AcquireEndpoint("172.17.0.2", 8080, "")
@@ -369,8 +355,7 @@ INFO-V(2) disabled endpoint '172.17.0.4:8080' on backend/server 'default_app_808
 INFO-V(2) added endpoints on backend 'default_app_8080'
 INFO-V(2) need to reload due to config changes: [backends]`,
 		},
-		// 13
-		{
+		"test14": {
 			doconfig1: func(c *testConfig) {
 				b := c.config.Backends().AcquireBackend("default", "app", "8080")
 				b.AcquireEndpoint("172.17.0.2", 8080, "")
@@ -397,8 +382,7 @@ set server default_app_8080/srv002 weight 1
 `,
 			logging: `INFO-V(2) added endpoint '172.17.0.3:8080' weight '1' state 'ready' on backend/server 'default_app_8080/srv002'`,
 		},
-		// 14
-		{
+		"test15": {
 			doconfig1: func(c *testConfig) {
 				b1 := c.config.Backends().AcquireBackend("default", "default_backend", "8080")
 				c.config.Backends().DefaultBackend = b1
@@ -425,8 +409,7 @@ set server default_app_8080/srv002 weight 1
 INFO-V(2) added endpoints on backend 'default_app_8080'
 INFO-V(2) need to reload due to config changes: [backends]`,
 		},
-		// 15
-		{
+		"test16": {
 			doconfig2: func(c *testConfig) {
 				c.config.Backends().AcquireBackend("default", "app", "8080").Dynamic.DynUpdate = true
 			},
@@ -439,8 +422,7 @@ INFO-V(2) need to reload due to config changes: [backends]`,
 INFO-V(2) added backend 'default_app_8080'
 INFO-V(2) need to reload due to config changes: [backends]`,
 		},
-		// 16
-		{
+		"test17": {
 			doconfig2: func(c *testConfig) {
 				b := c.config.Backends().AcquireBackend("default", "app", "8080")
 				b.Dynamic.DynUpdate = true
@@ -458,8 +440,7 @@ INFO-V(2) need to reload due to config changes: [backends]`,
 INFO-V(2) added backend 'default_app_8080'
 INFO-V(2) need to reload due to config changes: [backends]`,
 		},
-		// 17
-		{
+		"test18": {
 			doconfig1: func(c *testConfig) {
 				b := c.config.Backends().AcquireBackend("default", "app", "8080")
 				b.AcquireEndpoint("172.17.0.2", 8080, "").Label = "green"
@@ -482,8 +463,7 @@ set server default_app_8080/srv002 state ready
 set server default_app_8080/srv002 weight 1`,
 			logging: `INFO-V(2) added endpoint '172.17.0.3:8080' weight '1' state 'ready' on backend/server 'default_app_8080/srv002'`,
 		},
-		// 18
-		{
+		"test19": {
 			doconfig1: func(c *testConfig) {
 				b := c.config.Backends().AcquireBackend("default", "app", "8080")
 				b.AcquireEndpoint("172.17.0.2", 8080, "").Label = "green"
@@ -508,8 +488,7 @@ set server default_app_8080/srv002 weight 1`,
 INFO-V(2) added endpoint '172.17.0.4:8080' weight '1' state 'ready' on backend/server 'default_app_8080/srv002'
 INFO-V(2) need to reload due to config changes: [backends]`,
 		},
-		// 19
-		{
+		"test20": {
 			doconfig1: func(c *testConfig) {
 				b := c.config.Backends().AcquireBackend("default", "app", "8080")
 				b.AcquireEndpoint("172.17.0.2", 8080, "").Label = "green"
@@ -533,8 +512,7 @@ set server default_app_8080/srv002 weight 0`,
 INFO-V(2) disabled endpoint '172.17.0.3:8080' on backend/server 'default_app_8080/srv002'
 INFO-V(2) need to reload due to config changes: [backends]`,
 		},
-		// 20
-		{
+		"test21": {
 			doconfig1: func(c *testConfig) {
 				b := c.config.Backends().AcquireBackend("default", "app", "8080")
 				b.AcquireEndpoint("172.17.0.2", 8080, "").Label = "green"
@@ -565,8 +543,7 @@ INFO-V(2) response from server: IP changed from '172.17.0.3' to '172.17.0.4', no
 INFO-V(2) updated endpoint '172.17.0.4:8080' weight '1' state 'ready' on backend/server 'default_app_8080/srv002'
 INFO-V(2) need to reload due to config changes: [backends]`,
 		},
-		// 21
-		{
+		"test22": {
 			doconfig1: func(c *testConfig) {
 				b := c.config.Backends().AcquireBackend("default", "app", "8080")
 				b.AcquireEndpoint("172.17.0.2", 8080, "")
@@ -586,8 +563,7 @@ INFO-V(2) need to reload due to config changes: [backends]`,
 INFO-V(2) backend 'default_app_8080' changed and its dynamic-scaling is 'false'
 INFO-V(2) need to reload due to config changes: [backends]`,
 		},
-		// 22
-		{
+		"test23": {
 			doconfig1: func(c *testConfig) {
 				b := c.config.Backends().AcquireBackend("default", "app", "8080")
 				b.AcquireEndpoint("172.17.0.2", 8080, "")
@@ -605,10 +581,10 @@ INFO-V(2) need to reload due to config changes: [backends]`,
 			},
 			dynamic: true,
 		},
-		// 23 - test that we're able to update when a cookie value of acquired
+		// test that we're able to update when a cookie value of acquired
 		// existing endpoint exactly matches and cookie affinity is enabled
 		// even with "preserve" cookie mode
-		{
+		"test24": {
 			doconfig1: func(c *testConfig) {
 				b1 := c.config.Backends().AcquireBackend("default", "default_backend", "8080")
 				c.config.Backends().DefaultBackend = b1
@@ -655,9 +631,9 @@ set server default_app_8080/srv002 weight 1
 `,
 			logging: `INFO-V(2) updated endpoint '172.17.0.4:8080' weight '1' state 'ready' on backend/server 'default_app_8080/srv002'`,
 		},
-		// 24 - test that we're unable to update when a cookie value of acquired
+		// test that we're unable to update when a cookie value of acquired
 		// existing endpoint doesn't match and "preserve" cookie mode is enabled
-		{
+		"test25": {
 			doconfig1: func(c *testConfig) {
 				b1 := c.config.Backends().AcquireBackend("default", "default_backend", "8080")
 				c.config.Backends().DefaultBackend = b1
@@ -700,10 +676,10 @@ set server default_app_8080/srv002 weight 1
 			cmd:     ``,
 			logging: `INFO-V(2) need to reload due to config changes: [backends]`,
 		},
-		// 25 - test that we're able to update when a cookie value of acquired
+		// test that we're able to update when a cookie value of acquired
 		// existing endpoint doesn't match and "preserve" cookie mode is not enabled
 		// (eg. it doesn't care to preserve the cookie value, so still updates)
-		{
+		"test26": {
 			doconfig1: func(c *testConfig) {
 				b1 := c.config.Backends().AcquireBackend("default", "default_backend", "8080")
 				c.config.Backends().DefaultBackend = b1
@@ -749,8 +725,7 @@ set server default_app_8080/srv002 state ready
 set server default_app_8080/srv002 weight 1`,
 			logging: `INFO-V(2) updated endpoint '172.17.0.4:8080' weight '1' state 'ready' on backend/server 'default_app_8080/srv002'`,
 		},
-		// 26
-		{
+		"test27": {
 			doconfig1: func(c *testConfig) {
 				b := c.config.Backends().AcquireBackend("default", "app", "8080")
 				c.config.backends.DefaultBackend = b
@@ -772,8 +747,7 @@ set server default_app_8080/srv001 state ready
 set server default_app_8080/srv001 weight 1`,
 			logging: `INFO-V(2) updated endpoint '172.17.0.3:8080' weight '1' state 'ready' on backend/server 'default_app_8080/srv001'`,
 		},
-		// 27
-		{
+		"test28": {
 			doconfig1: func(c *testConfig) {
 				b := c.config.Backends().AcquireBackend("default", "app", "8080")
 				b.Resolver = "k8s"
@@ -792,8 +766,7 @@ set server default_app_8080/srv001 weight 1`,
 			},
 			dynamic: true,
 		},
-		// 28
-		{
+		"test29": {
 			doconfig1: func(c *testConfig) {
 				b := c.config.Backends().AcquireBackend("default", "app", "8080")
 				b.AcquireEndpoint("172.17.0.2", 8080, "").Name = "srv002"
@@ -828,8 +801,7 @@ WARN unrecognized response adding/updating endpoint default_app_8080/srv003: No
 INFO-V(2) need to reload due to config changes: [backends]
 `,
 		},
-		// 29
-		{
+		"test30": {
 			doconfig1: func(c *testConfig) {
 				b := c.config.Backends().AcquireBackend("default", "app", "8080")
 				b.AcquireEndpoint("172.17.0.2", 8080, "").Name = "srv002"
@@ -862,19 +834,20 @@ WARN unrecognized response disabling endpoint default_app_8080/srv003: No such s
 INFO-V(2) need to reload due to config changes: [backends]
 `,
 		},
-		// 28
-		{
+		"test31": {
 			doconfig1: func(c *testConfig) {
-				h1 := c.config.Hosts().AcquireHost("domain1.local")
-				h2 := c.config.Hosts().AcquireHost("domain2.local")
+				f := c.httpFrontend(80)
+				h1 := f.AcquireHost("domain1.local")
+				h2 := f.AcquireHost("domain2.local")
 				h1.TLS.TLSFilename = "/tmp/domain1.pem"
 				h2.TLS.TLSFilename = "/tmp/domain2.pem"
 				h1.TLS.TLSHash = "1"
 				h2.TLS.TLSHash = "2"
 			},
 			doconfig2: func(c *testConfig) {
-				h1 := c.config.Hosts().AcquireHost("domain1.local")
-				h2 := c.config.Hosts().AcquireHost("domain2.local")
+				f := c.httpFrontend(80)
+				h1 := f.AcquireHost("domain1.local")
+				h2 := f.AcquireHost("domain2.local")
 				h1.TLS.TLSFilename = "/tmp/domain1.pem"
 				h2.TLS.TLSFilename = "/tmp/domain2.pem"
 				h1.TLS.TLSHash = "1"
@@ -897,37 +870,39 @@ INFO-V(2) response from server: Committing /tmp/domain2.pem. \\ Success!
 INFO certificate updated for domain2.local
 `,
 		},
-		// 29
-		{
+		"test32": {
 			doconfig1: func(c *testConfig) {
-				h1 := c.config.Hosts().AcquireHost("domain1.local")
+				f := c.httpFrontend(80)
+				h1 := f.AcquireHost("domain1.local")
 				h1.TLS.TLSFilename = "/tmp/domain1.pem"
 				h1.TLS.TLSHash = "1"
 			},
 			doconfig2: func(c *testConfig) {
-				h1 := c.config.Hosts().AcquireHost("domain1.local")
+				f := c.httpFrontend(80)
+				h1 := f.AcquireHost("domain1.local")
 				h1.TLS.TLSFilename = "/tmp/domain2.pem"
 				h1.TLS.TLSHash = "2"
 			},
 			dynamic: false,
 			logging: `
 INFO-V(2) diff outside server certificate of host 'domain1.local'
-INFO-V(2) need to reload due to config changes: [hosts]
+INFO-V(2) need to reload due to config changes: [hosts (_front_http)]
 `,
 		},
-		// 30
-		{
+		"test33": {
 			doconfig1: func(c *testConfig) {
-				h1 := c.config.Hosts().AcquireHost("domain1.local")
-				h2 := c.config.Hosts().AcquireHost("domain2.local")
+				f := c.httpFrontend(80)
+				h1 := f.AcquireHost("domain1.local")
+				h2 := f.AcquireHost("domain2.local")
 				h1.TLS.TLSFilename = "/tmp/domain1.pem"
 				h2.TLS.TLSFilename = ""
 				h1.TLS.TLSHash = "1"
 				h2.TLS.TLSHash = "2"
 			},
 			doconfig2: func(c *testConfig) {
-				h1 := c.config.Hosts().AcquireHost("domain1.local")
-				h2 := c.config.Hosts().AcquireHost("domain2.local")
+				f := c.httpFrontend(80)
+				h1 := f.AcquireHost("domain1.local")
+				h2 := f.AcquireHost("domain2.local")
 				h1.TLS.TLSFilename = "/tmp/domain1.pem"
 				h2.TLS.TLSFilename = ""
 				h1.TLS.TLSHash = "3"
@@ -950,15 +925,16 @@ INFO-V(2) response from server: Committing /tmp/domain1.pem. \\ Success!
 INFO certificate updated for domain1.local
 `,
 		},
-		// 31
-		{
+		"test34": {
 			doconfig1: func(c *testConfig) {
-				h1 := c.config.Hosts().AcquireHost("domain1.local")
+				f := c.httpFrontend(80)
+				h1 := f.AcquireHost("domain1.local")
 				h1.TLS.TLSFilename = "/tmp/domain1.pem"
 				h1.TLS.TLSHash = "1"
 			},
 			doconfig2: func(c *testConfig) {
-				h1 := c.config.Hosts().AcquireHost("domain1.local")
+				f := c.httpFrontend(80)
+				h1 := f.AcquireHost("domain1.local")
 				h1.TLS.TLSFilename = "/tmp/domain1.pem"
 				h1.TLS.TLSHash = "2"
 			},
@@ -977,75 +953,72 @@ commit ssl cert /tmp/domain1.pem
 INFO-V(2) response from server: Can't replace a certificate which is not referenced by the configuration! \\ Can't update /tmp/domain1.pem!
 INFO-V(2) response from server: No ongoing transaction! ! \\ Can't commit /tmp/domain1.pem!
 WARN cannot update certificate for domain1.local
-INFO-V(2) need to reload due to config changes: [hosts]
+INFO-V(2) need to reload due to config changes: [hosts (_front_http)]
 `,
 		},
-		// 32
-		{
+		"test35": {
 			doconfig1: func(c *testConfig) {
-				h1 := c.config.Hosts().AcquireHost("domain1.local")
+				f := c.httpFrontend(80)
+				h1 := f.AcquireHost("domain1.local")
 				h1.TLS.TLSFilename = "/tmp/domain1.pem"
 				h1.TLS.TLSHash = "1"
-				c.config.Hosts().AcquireHost("domain2.local")
+				f.AcquireHost("domain2.local")
 			},
 			doconfig2: func(c *testConfig) {
-				h1 := c.config.Hosts().AcquireHost("domain1.local")
+				f := c.httpFrontend(80)
+				h1 := f.AcquireHost("domain1.local")
 				h1.TLS.TLSFilename = "/tmp/domain1.pem"
 				h1.TLS.TLSHash = "1"
 			},
 			dynamic: false,
 			logging: `
 INFO-V(2) removed host 'domain2.local'
-INFO-V(2) need to reload due to config changes: [hosts]
+INFO-V(2) need to reload due to config changes: [hosts (_front_http)]
 `,
 		},
 	}
 	readFile = func(_ string) ([]byte, error) {
 		return []byte("<content>"), nil
 	}
-	for i, test := range testCases {
-		c := setup(t)
-		if test.doconfig1 != nil {
-			test.doconfig1(c)
-		}
-		c.instance.config.Commit()
-		hostnames := []string{}
-		for hostname := range c.config.hosts.Items() {
-			hostnames = append(hostnames, hostname)
-		}
-		c.config.Hosts().RemoveAll(hostnames)
-		backendIDs := []string{}
-		for _, backend := range c.config.Backends().Items() {
-			backendIDs = append(backendIDs, backend.ID)
-		}
-		c.config.Backends().RemoveAll(backendIDs)
-		if test.doconfig2 != nil {
-			test.doconfig2(c)
-		}
-		clientMock := &clientMock{
-			cmdOutput: test.cmdOutput,
-		}
-		dynUpdater := c.instance.newDynUpdater()
-		dynUpdater.socket = clientMock
-		dynamic := dynUpdater.update()
-		var actual []string
-		for _, ep := range c.config.Backends().AcquireBackend("default", "app", "8080").Endpoints {
-			actual = append(actual, fmt.Sprintf("%s:%s:%d:%d", ep.Name, ep.IP, ep.Port, ep.Weight))
-		}
-		if !reflect.DeepEqual(actual, test.expected) {
-			t.Errorf("endpoints expected and actual differs on %d -- expected: %v -- actual: %v",
-				i, test.expected, actual)
-		}
-		if dynamic != test.dynamic {
-			t.Errorf("dynamic expected as '%t' on %d, but was '%t'", test.dynamic, i, dynamic)
-		}
-		cmd := strings.TrimSpace(clientMock.cmd)
-		test.cmd = strings.TrimSpace(test.cmd)
-		if cmd != test.cmd {
-			t.Errorf("cmd differs on %d:\n%s", i, diff.Diff(test.cmd, cmd))
-		}
-		c.logger.CompareLogging(test.logging)
-		c.teardown()
+	for name, test := range testCases {
+		t.Run(name, func(t *testing.T) {
+			c := setup(t)
+			f := c.httpFrontend(80)
+			if test.doconfig1 != nil {
+				test.doconfig1(c)
+			}
+			c.instance.config.Commit()
+			hostnames := []string{}
+			for hostname := range f.Hosts() {
+				hostnames = append(hostnames, hostname)
+			}
+			f.RemoveAllHosts(hostnames)
+			backendIDs := []string{}
+			for _, backend := range c.config.Backends().Items() {
+				backendIDs = append(backendIDs, backend.ID)
+			}
+			c.config.Backends().RemoveAll(backendIDs)
+			if test.doconfig2 != nil {
+				test.doconfig2(c)
+			}
+			clientMock := &clientMock{
+				cmdOutput: test.cmdOutput,
+			}
+			dynUpdater := c.instance.newDynUpdater()
+			dynUpdater.socket = clientMock
+			dynamic := dynUpdater.update()
+			var actual []string
+			for _, ep := range c.config.Backends().AcquireBackend("default", "app", "8080").Endpoints {
+				actual = append(actual, fmt.Sprintf("%s:%s:%d:%d", ep.Name, ep.IP, ep.Port, ep.Weight))
+			}
+			assert.Equal(t, test.expected, actual, "endpoints differ")
+			assert.Equal(t, test.dynamic, dynamic, "dynamic differ")
+			cmd := strings.TrimSpace(clientMock.cmd)
+			test.cmd = strings.TrimSpace(test.cmd)
+			assert.Equal(t, test.cmd, cmd, "cmd differ")
+			c.logger.CompareLogging(test.logging)
+			c.teardown()
+		})
 	}
 }
 
diff --git a/pkg/haproxy/instance_test.go b/pkg/haproxy/instance_test.go
index c30a68bc..e051d3b0 100644
--- a/pkg/haproxy/instance_test.go
+++ b/pkg/haproxy/instance_test.go
@@ -40,8 +40,8 @@ import (
  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
 func TestBackends(t *testing.T) {
-	testCases := []struct {
-		doconfig  func(c *config, h *hatypes.Host, b *hatypes.Backend)
+	testCases := map[string]struct {
+		doconfig  func(c *testConfig, h *hatypes.Host, b *hatypes.Backend)
 		path      []string
 		match     []hatypes.MatchType
 		skipSrv   bool
@@ -50,8 +50,8 @@ func TestBackends(t *testing.T) {
 		expFronts string
 		expCheck  map[string]string
 	}{
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test01": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				b.Cookie.Name = "ingress-controller"
 				b.Cookie.Strategy = "insert"
 				b.Cookie.Keywords = "indirect nocache httponly"
@@ -63,8 +63,8 @@ func TestBackends(t *testing.T) {
 			expected: `
     cookie ingress-controller insert indirect nocache httponly`,
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test02": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				b.Cookie.Name = "Ingress"
 				b.Cookie.Strategy = "prefix"
 				b.Cookie.Dynamic = true
@@ -73,8 +73,8 @@ func TestBackends(t *testing.T) {
     cookie Ingress prefix dynamic
     dynamic-cookie-key "Ingress"`,
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test03": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				b.Cookie.Name = "Ingress"
 				b.Cookie.Strategy = "insert"
 				b.Cookie.Dynamic = true
@@ -86,8 +86,8 @@ func TestBackends(t *testing.T) {
     cookie Ingress insert indirect nocache httponly domain d1.local dynamic
     dynamic-cookie-key "Ingress"`,
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test04": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				b.Cookie.Name = "ingress-controller"
 				b.Cookie.Domain = "example.com"
 				b.Cookie.Strategy = "insert"
@@ -96,8 +96,8 @@ func TestBackends(t *testing.T) {
 			expected: `
     cookie ingress-controller insert indirect nocache httponly domain example.com`,
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test05": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				b.Cookie.Name = "Ingress"
 				b.Cookie.Strategy = "insert"
 				b.Cookie.Keywords = "indirect nocache httponly"
@@ -106,8 +106,8 @@ func TestBackends(t *testing.T) {
 			expected: `
     cookie Ingress insert attr SameSite=None secure indirect nocache httponly`,
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test06": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				config1 := hatypes.Cors{
 					Enabled:      true,
 					AllowOrigin:  []string{"http://d11.local", "https://d12.local"},
@@ -120,10 +120,10 @@ func TestBackends(t *testing.T) {
 				config2.AllowOriginRegex = []string{`^http://d10\.local$`, `^https?://d1[23]\.local`, `https://([a-z]*\.){0,3}d13\.local$`, `^https?://d1[45]\.local`, `^https?://d1[67]\.local`, `^https?://d1[89]\.local`}
 				config3 := config1
 				config3.AllowOrigin = []string{"https://d31.local"}
-				b.FindBackendPath(h.FindPath("/")[0].Link).Cors = config1
-				b.FindBackendPath(h.FindPath("/api")[0].Link).Cors = config1
-				b.FindBackendPath(h.FindPath("/app")[0].Link).Cors = config2
-				b.FindBackendPath(h.FindPath("/sub")[0].Link).Cors = config3
+				h.FindPath("/")[0].Cors = config1
+				h.FindPath("/api")[0].Cors = config1
+				h.FindPath("/app")[0].Cors = config2
+				h.FindPath("/sub")[0].Cors = config3
 			},
 			path: []string{"/", "/api", "/app", "/sub"},
 			expected: `
@@ -131,7 +131,7 @@ func TestBackends(t *testing.T) {
     # path02 = d1.local/api
     # path03 = d1.local/app
     # path04 = d1.local/sub
-    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_idpath__begin.map)
+    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_front_http_req__begin.map)
     http-request set-var(txn.hdr_origin0) req.hdr(Origin)
     http-request set-var(txn.cors_max_age) str(86400) if METH_OPTIONS { var(txn.pathID) -m str path01 path02 }
     http-request use-service lua.send-cors-preflight if METH_OPTIONS { var(txn.pathID) -m str path01 path02 }
@@ -161,8 +161,8 @@ func TestBackends(t *testing.T) {
     http-response set-header Access-Control-Allow-Methods "GET, PUT, POST, DELETE, PATCH, OPTIONS" if { var(txn.pathID) -m str path04 }
     http-response set-header Access-Control-Allow-Headers "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization" if { var(txn.pathID) -m str path04 }`,
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test07": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				config := hatypes.Cors{
 					Enabled:      true,
 					AllowOrigin:  []string{"https://d11.local", "https://d12.local"},
@@ -170,7 +170,7 @@ func TestBackends(t *testing.T) {
 					AllowMethods: "GET, PUT, POST, DELETE, PATCH, OPTIONS",
 					MaxAge:       86400,
 				}
-				b.FindBackendPath(h.FindPath("/")[0].Link).Cors = config
+				h.FindPath("/")[0].Cors = config
 			},
 			path: []string{"/"},
 			expected: `
@@ -185,8 +185,8 @@ func TestBackends(t *testing.T) {
     http-response set-header Vary %[res.fhdr(Vary)],Origin if { res.hdr(Vary) -m found }
     http-response set-header Vary Origin if ! { res.hdr(Vary) -m found }`,
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test08": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				config := hatypes.Cors{
 					Enabled:          true,
 					AllowOrigin:      []string{"https://d11.local", "https://d12.local"},
@@ -195,7 +195,7 @@ func TestBackends(t *testing.T) {
 					AllowMethods:     "GET, PUT, POST, DELETE, PATCH, OPTIONS",
 					MaxAge:           86400,
 				}
-				b.FindBackendPath(h.FindPath("/")[0].Link).Cors = config
+				h.FindPath("/")[0].Cors = config
 			},
 			path: []string{"/"},
 			expected: `
@@ -211,8 +211,8 @@ func TestBackends(t *testing.T) {
     http-response set-header Vary %[res.fhdr(Vary)],Origin if { res.hdr(Vary) -m found }
     http-response set-header Vary Origin if ! { res.hdr(Vary) -m found }`,
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test09": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				config := hatypes.Cors{
 					Enabled:          true,
 					AllowOrigin:      []string{"https://d11.local"},
@@ -221,7 +221,7 @@ func TestBackends(t *testing.T) {
 					AllowMethods:     "GET, PUT, POST, DELETE, PATCH, OPTIONS",
 					MaxAge:           86400,
 				}
-				b.FindBackendPath(h.FindPath("/")[0].Link).Cors = config
+				h.FindPath("/")[0].Cors = config
 			},
 			path: []string{"/"},
 			expected: `
@@ -237,8 +237,8 @@ func TestBackends(t *testing.T) {
     http-response set-header Vary %[res.fhdr(Vary)],Origin if { res.hdr(Vary) -m found }
     http-response set-header Vary Origin if ! { res.hdr(Vary) -m found }`,
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test10": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				config := hatypes.Cors{
 					Enabled:          true,
 					AllowOrigin:      []string{"https://d11.local"},
@@ -247,7 +247,7 @@ func TestBackends(t *testing.T) {
 					AllowMethods:     "GET, PUT, POST, DELETE, PATCH, OPTIONS",
 					MaxAge:           86400,
 				}
-				b.FindBackendPath(h.FindPath("/")[0].Link).Cors = config
+				h.FindPath("/")[0].Cors = config
 			},
 			path: []string{"/"},
 			expected: `
@@ -263,8 +263,8 @@ func TestBackends(t *testing.T) {
     http-response set-header Vary %[res.fhdr(Vary)],Origin if { res.hdr(Vary) -m found }
     http-response set-header Vary Origin if ! { res.hdr(Vary) -m found }`,
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test11": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				config := hatypes.Cors{
 					Enabled:      true,
 					AllowOrigin:  []string{"https://d11.local"},
@@ -272,7 +272,7 @@ func TestBackends(t *testing.T) {
 					AllowMethods: "GET, PUT, POST, DELETE, PATCH, OPTIONS",
 					MaxAge:       86400,
 				}
-				b.FindBackendPath(h.FindPath("/")[0].Link).Cors = config
+				h.FindPath("/")[0].Cors = config
 			},
 			path: []string{"/"},
 			expected: `
@@ -283,8 +283,8 @@ func TestBackends(t *testing.T) {
     http-response set-header Access-Control-Allow-Headers "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization"`,
 		},
 		// ignore AllowOriginRegex if we don't have at least one AllowOrigin
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test12": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				config := hatypes.Cors{
 					Enabled:          true,
 					AllowOrigin:      []string{},
@@ -293,13 +293,13 @@ func TestBackends(t *testing.T) {
 					AllowMethods:     "GET, PUT, POST, DELETE, PATCH, OPTIONS",
 					MaxAge:           86400,
 				}
-				b.FindBackendPath(h.FindPath("/")[0].Link).Cors = config
+				h.FindPath("/")[0].Cors = config
 			},
 			path:     []string{"/"},
 			expected: ``,
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test13": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				config := hatypes.Cors{
 					Enabled:      true,
 					AllowOrigin:  []string{},
@@ -307,13 +307,13 @@ func TestBackends(t *testing.T) {
 					AllowMethods: "GET, PUT, POST, DELETE, PATCH, OPTIONS",
 					MaxAge:       86400,
 				}
-				b.FindBackendPath(h.FindPath("/")[0].Link).Cors = config
+				h.FindPath("/")[0].Cors = config
 			},
 			path:     []string{"/"},
 			expected: ``,
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test14": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				config := hatypes.Cors{
 					Enabled:      true,
 					AllowOrigin:  []string{"*"},
@@ -321,8 +321,8 @@ func TestBackends(t *testing.T) {
 					AllowMethods: "GET, PUT, POST, DELETE, PATCH, OPTIONS",
 					MaxAge:       86400,
 				}
-				b.FindBackendPath(h.FindPath("/")[0].Link).Cors = config
-				b.FindBackendPath(h.FindPath("/sub")[0].Link).Cors = config
+				h.FindPath("/")[0].Cors = config
+				h.FindPath("/sub")[0].Cors = config
 			},
 			path: []string{"/", "/sub"},
 			expected: `
@@ -332,8 +332,8 @@ func TestBackends(t *testing.T) {
     http-response set-header Access-Control-Allow-Methods "GET, PUT, POST, DELETE, PATCH, OPTIONS"
     http-response set-header Access-Control-Allow-Headers "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization"`,
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test15": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				config := hatypes.Cors{
 					Enabled:          true,
 					AllowOrigin:      []string{"*"},
@@ -342,13 +342,13 @@ func TestBackends(t *testing.T) {
 					MaxAge:           86400,
 					AllowCredentials: true,
 				}
-				b.FindBackendPath(h.FindPath("/")[0].Link).Cors = config
+				h.FindPath("/")[0].Cors = config
 			},
 			path: []string{"/", "/sub"},
 			expected: `
     # path01 = d1.local/
     # path02 = d1.local/sub
-    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_idpath__begin.map)
+    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_front_http_req__begin.map)
     http-request set-var(txn.cors_max_age) str(86400) if METH_OPTIONS { var(txn.pathID) -m str path01 }
     http-request use-service lua.send-cors-preflight if METH_OPTIONS { var(txn.pathID) -m str path01 }
     http-response set-header Access-Control-Allow-Origin  "*" if { var(txn.pathID) -m str path01 }
@@ -356,26 +356,26 @@ func TestBackends(t *testing.T) {
     http-response set-header Access-Control-Allow-Headers "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization" if { var(txn.pathID) -m str path01 }
     http-response set-header Access-Control-Allow-Credentials "true" if { var(txn.pathID) -m str path01 }`,
 			expCheck: map[string]string{
-				"_back_d1_app_8080_idpath__begin.map": `
+				"_back_d1_app_8080_front_http_req__begin.map": `
 d1.local#/sub path02
 d1.local#/ path01`,
 			},
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
-				b.FindBackendPath(h.FindPath("/")[0].Link).HSTS = hatypes.HSTS{
+		"test16": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
+				h.FindPath("/")[0].HSTS = hatypes.HSTS{
 					Enabled:    true,
 					MaxAge:     15768000,
 					Preload:    true,
 					Subdomains: true,
 				}
-				b.FindBackendPath(h.FindPath("/path")[0].Link).HSTS = hatypes.HSTS{
+				h.FindPath("/path")[0].HSTS = hatypes.HSTS{
 					Enabled:    true,
 					MaxAge:     15768000,
 					Preload:    false,
 					Subdomains: false,
 				}
-				b.FindBackendPath(h.FindPath("/uri")[0].Link).HSTS = hatypes.HSTS{
+				h.FindPath("/uri")[0].HSTS = hatypes.HSTS{
 					Enabled:    true,
 					MaxAge:     15768000,
 					Preload:    false,
@@ -388,102 +388,102 @@ d1.local#/ path01`,
     # path01 = d1.local/
     # path02 = d1.local/path
     # path03 = d1.local/uri
-    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_idpath__begin.map)
+    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_front_http_req__begin.map)
     http-response set-header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload" if https-request { var(txn.pathID) -m str path01 }
     http-response set-header Strict-Transport-Security "max-age=15768000" if https-request { var(txn.pathID) -m str path02 path03 }`,
 			expCheck: map[string]string{
-				"_back_d1_app_8080_idpath__begin.map": `
+				"_back_d1_app_8080_front_http_req__begin.map": `
 d1.local#/uri path03
 d1.local#/path path02
 d1.local#/ path01`,
 			},
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
-				b.FindBackendPath(h.FindPath("/app")[0].Link).RewriteURL = "/"
+		"test17": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
+				h.FindPath("/app")[0].RewriteURL = "/"
 			},
 			path: []string{"/app"},
 			expected: `
     http-request replace-path ^/app/?(.*)$     /\1`,
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
-				b.FindBackendPath(h.FindPath("/app")[0].Link).RewriteURL = "/other"
+		"test18": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
+				h.FindPath("/app")[0].RewriteURL = "/other"
 			},
 			path: []string{"/app"},
 			expected: `
     http-request replace-path ^/app(.*)$       /other\1`,
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
-				b.FindBackendPath(h.FindPath("/app")[0].Link).RewriteURL = "/other/"
-				b.FindBackendPath(h.FindPath("/app/sub")[0].Link).RewriteURL = "/other/"
+		"test19": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
+				h.FindPath("/app")[0].RewriteURL = "/other/"
+				h.FindPath("/app/sub")[0].RewriteURL = "/other/"
 			},
 			path: []string{"/app", "/app/sub"},
 			expected: `
     http-request replace-path ^/app(.*)$       /other/\1
     http-request replace-path ^/app/sub(.*)$       /other/\1`,
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
-				b.FindBackendPath(h.FindPath("/path1")[0].Link).RewriteURL = "/sub1"
-				b.FindBackendPath(h.FindPath("/path2")[0].Link).RewriteURL = "/sub2"
-				b.FindBackendPath(h.FindPath("/path3")[0].Link).RewriteURL = "/sub2"
+		"test20": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
+				h.FindPath("/path1")[0].RewriteURL = "/sub1"
+				h.FindPath("/path2")[0].RewriteURL = "/sub2"
+				h.FindPath("/path3")[0].RewriteURL = "/sub2"
 			},
 			path: []string{"/path1", "/path2", "/path3"},
 			expected: `
     # path01 = d1.local/path1
     # path02 = d1.local/path2
     # path03 = d1.local/path3
-    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_idpath__begin.map)
+    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_front_http_req__begin.map)
     http-request replace-path ^/path1(.*)$       /sub1\1     if { var(txn.pathID) -m str path01 }
     http-request replace-path ^/path2(.*)$       /sub2\1     if { var(txn.pathID) -m str path02 }
     http-request replace-path ^/path3(.*)$       /sub2\1     if { var(txn.pathID) -m str path03 }`,
 			expCheck: map[string]string{
-				"_back_d1_app_8080_idpath__begin.map": `
+				"_back_d1_app_8080_front_http_req__begin.map": `
 d1.local#/path3 path03
 d1.local#/path2 path02
 d1.local#/path1 path01`,
 			},
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
-				b.FindBackendPath(h.FindPath("/app")[0].Link).SSLRedirect = true
+		"test21": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
+				h.FindPath("/app")[0].SSLRedirect = true
 			},
 			path: []string{"/app", "/path"},
 			expected: `
     acl https-request ssl_fc
     # path01 = d1.local/app
     # path02 = d1.local/path
-    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_idpath__begin.map)
+    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_front_http_req__begin.map)
     http-request redirect scheme https if !https-request { var(txn.pathID) -m str path01 }`,
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
-				b.FindBackendPath(h.FindPath("/app")[0].Link).SSLRedirect = true
-				c.global.SSL.RedirectCode = 301
+		"test22": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
+				h.FindPath("/app")[0].SSLRedirect = true
+				c.config.global.SSL.RedirectCode = 301
 			},
 			path: []string{"/app", "/path"},
 			expected: `
     acl https-request ssl_fc
     # path01 = d1.local/app
     # path02 = d1.local/path
-    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_idpath__begin.map)
+    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_front_http_req__begin.map)
     http-request redirect scheme https code 301 if !https-request { var(txn.pathID) -m str path01 }`,
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
-				b.FindBackendPath(h.FindPath("/app")[0].Link).AllowedIPHTTP.Rule = []string{"10.0.0.0/8", "192.168.0.0/16"}
-				b.FindBackendPath(h.FindPath("/api")[0].Link).AllowedIPHTTP.Rule = []string{"10.0.0.0/8", "192.168.0.0/16"}
-				b.FindBackendPath(h.FindPath("/path")[0].Link).AllowedIPHTTP.Rule = []string{"192.168.95.0/24"}
-				b.FindBackendPath(h.FindPath("/path")[0].Link).AllowedIPHTTP.Exception = []string{"192.168.95.11"}
+		"test23": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
+				h.FindPath("/app")[0].AllowedIPHTTP.Rule = []string{"10.0.0.0/8", "192.168.0.0/16"}
+				h.FindPath("/api")[0].AllowedIPHTTP.Rule = []string{"10.0.0.0/8", "192.168.0.0/16"}
+				h.FindPath("/path")[0].AllowedIPHTTP.Rule = []string{"192.168.95.0/24"}
+				h.FindPath("/path")[0].AllowedIPHTTP.Exception = []string{"192.168.95.11"}
 			},
 			path: []string{"/app", "/api", "/path"},
 			expected: `
     # path02 = d1.local/api
     # path01 = d1.local/app
     # path03 = d1.local/path
-    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_idpath__begin.map)
+    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_front_http_req__begin.map)
     acl allow_rule_src0 src 10.0.0.0/8 192.168.0.0/16
     http-request deny if { var(txn.pathID) -m str path01 path02 } !allow_rule_src0
     acl allow_rule_src1 src 192.168.95.0/24
@@ -491,19 +491,19 @@ d1.local#/path1 path01`,
     http-request deny if { var(txn.pathID) -m str path03 } allow_exception_src1
     http-request deny if { var(txn.pathID) -m str path03 } !allow_rule_src1`,
 			expCheck: map[string]string{
-				"_back_d1_app_8080_idpath__begin.map": `
+				"_back_d1_app_8080_front_http_req__begin.map": `
 d1.local#/path path03
 d1.local#/app path01
 d1.local#/api path02`,
 			},
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
-				b.FindBackendPath(h.FindPath("/app")[0].Link).AllowedIPHTTP.Rule = []string{"10.0.0.0/8", "192.168.0.0/16"}
-				b.FindBackendPath(h.FindPath("/api")[0].Link).AllowedIPHTTP.Rule = []string{"10.0.0.0/8", "192.168.0.0/16"}
-				b.FindBackendPath(h.FindPath("/path")[0].Link).AllowedIPHTTP.Rule = []string{"10.0.0.0/8", "192.168.0.0/16"}
-				b.FindBackendPath(h.FindPath("/api/v[0-9]+/")[0].Link).AllowedIPHTTP.Rule = []string{"10.0.0.0/8", "192.168.0.0/16"}
-				b.FindBackendPath(h.FindPath("/")[0].Link).AllowedIPHTTP.Rule = []string{"172.17.0.0/16"}
+		"test24": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
+				h.FindPath("/app")[0].AllowedIPHTTP.Rule = []string{"10.0.0.0/8", "192.168.0.0/16"}
+				h.FindPath("/api")[0].AllowedIPHTTP.Rule = []string{"10.0.0.0/8", "192.168.0.0/16"}
+				h.FindPath("/path")[0].AllowedIPHTTP.Rule = []string{"10.0.0.0/8", "192.168.0.0/16"}
+				h.FindPath("/api/v[0-9]+/")[0].AllowedIPHTTP.Rule = []string{"10.0.0.0/8", "192.168.0.0/16"}
+				h.FindPath("/")[0].AllowedIPHTTP.Rule = []string{"172.17.0.0/16"}
 			},
 			path:  []string{"/", "/app", "/api", "/path", "/api/v[0-9]+/"},
 			match: []hatypes.MatchType{hatypes.MatchBegin, hatypes.MatchExact, hatypes.MatchBegin, hatypes.MatchPrefix, hatypes.MatchRegex},
@@ -513,40 +513,50 @@ d1.local#/api path02`,
     # path05 = d1.local/api/v[0-9]+/
     # path02 = d1.local/app
     # path04 = d1.local/path
-    http-request set-var(txn.pathID) var(req.base),map_str(/etc/haproxy/maps/_back_d1_app_8080_idpath__exact.map)
-    http-request set-var(txn.pathID) var(req.base),map_dir(/etc/haproxy/maps/_back_d1_app_8080_idpath__prefix_02.map) if !{ var(txn.pathID) -m found }
-    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_idpath__begin.map) if !{ var(txn.pathID) -m found }
-    http-request set-var(txn.pathID) var(req.base),map_reg(/etc/haproxy/maps/_back_d1_app_8080_idpath__regex.map) if !{ var(txn.pathID) -m found }
+    http-request set-var(txn.pathID) var(req.base),map_str(/etc/haproxy/maps/_back_d1_app_8080_front_http_req__exact.map)
+    http-request set-var(txn.pathID) var(req.base),map_dir(/etc/haproxy/maps/_back_d1_app_8080_front_http_req__prefix_02.map) if !{ var(txn.pathID) -m found }
+    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_front_http_req__begin.map) if !{ var(txn.pathID) -m found }
+    http-request set-var(txn.pathID) var(req.base),map_reg(/etc/haproxy/maps/_back_d1_app_8080_front_http_req__regex.map) if !{ var(txn.pathID) -m found }
     acl allow_rule_src0 src 172.17.0.0/16
     http-request deny if { var(txn.pathID) -m str path01 } !allow_rule_src0
     acl allow_rule_src1 src 10.0.0.0/8 192.168.0.0/16
     http-request deny if { var(txn.pathID) -m str path02 path03 path04 path05 } !allow_rule_src1`,
-			expFronts: "<<frontends-default-match-4>>",
+			expFronts: `frontend _front_http
+    mode http
+    bind :80
+    <<set-req-base>>
+    <<http-headers>>
+    http-request set-var(req.backend) var(req.base),map_str(/etc/haproxy/maps/_front_http_host__exact.map)
+    http-request set-var(req.backend) var(req.base),map_dir(/etc/haproxy/maps/_front_http_host__prefix_02.map) if !{ var(req.backend) -m found }
+    http-request set-var(req.backend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_host__begin.map) if !{ var(req.backend) -m found }
+    http-request set-var(req.backend) var(req.base),map_reg(/etc/haproxy/maps/_front_http_host__regex.map) if !{ var(req.backend) -m found }
+    use_backend %[var(req.backend)] if { var(req.backend) -m found }
+    default_backend _error404`,
 			expCheck: map[string]string{
-				"_back_d1_app_8080_idpath__exact.map": `
+				"_back_d1_app_8080_front_http_req__exact.map": `
 d1.local#/app path02`,
-				"_back_d1_app_8080_idpath__prefix_02.map": `
+				"_back_d1_app_8080_front_http_req__prefix_02.map": `
 d1.local#/path path04`,
-				"_back_d1_app_8080_idpath__begin.map": `
+				"_back_d1_app_8080_front_http_req__begin.map": `
 d1.local#/api path03
 d1.local#/ path01`,
-				"_back_d1_app_8080_idpath__regex.map": `
+				"_back_d1_app_8080_front_http_req__regex.map": `
 ^d1\.local#/api/v[0-9]+/ path05`,
 			},
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
-				b.FindBackendPath(h.FindPath("/app1")[0].Link).AllowedIPHTTP.Rule = []string{"10.0.0.0/8"}
-				b.FindBackendPath(h.FindPath("/app1")[0].Link).AllowedIPHTTP.Exception = []string{"10.0.110.0/24"}
-				b.FindBackendPath(h.FindPath("/app2")[0].Link).DeniedIPHTTP.Rule = []string{"192.168.95.0/24"}
-				b.FindBackendPath(h.FindPath("/app2")[0].Link).DeniedIPHTTP.Exception = []string{"192.168.95.128/28"}
+		"test25": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
+				h.FindPath("/app1")[0].AllowedIPHTTP.Rule = []string{"10.0.0.0/8"}
+				h.FindPath("/app1")[0].AllowedIPHTTP.Exception = []string{"10.0.110.0/24"}
+				h.FindPath("/app2")[0].DeniedIPHTTP.Rule = []string{"192.168.95.0/24"}
+				h.FindPath("/app2")[0].DeniedIPHTTP.Exception = []string{"192.168.95.128/28"}
 			},
 			path: []string{"/app1", "/app2", "/app3"},
 			expected: `
     # path01 = d1.local/app1
     # path02 = d1.local/app2
     # path03 = d1.local/app3
-    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_idpath__begin.map)
+    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_front_http_req__begin.map)
     acl allow_rule_src0 src 10.0.0.0/8
     acl allow_exception_src0 src 10.0.110.0/24
     http-request deny if { var(txn.pathID) -m str path01 } allow_exception_src0
@@ -555,18 +565,18 @@ d1.local#/ path01`,
     acl deny_exception_src1 src 192.168.95.128/28
     http-request deny if { var(txn.pathID) -m str path02 } deny_rule_src1 !deny_exception_src1`,
 			expCheck: map[string]string{
-				"_back_d1_app_8080_idpath__begin.map": `
+				"_back_d1_app_8080_front_http_req__begin.map": `
 d1.local#/app3 path03
 d1.local#/app2 path02
 d1.local#/app1 path01`,
 			},
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
-				b.FindBackendPath(h.FindPath("/app1")[0].Link).AllowedIPHTTP.Rule = []string{"10.0.0.0/8"}
-				b.FindBackendPath(h.FindPath("/app2")[0].Link).AllowedIPHTTP.Exception = []string{"10.0.110.0/24"}
-				b.FindBackendPath(h.FindPath("/app3")[0].Link).DeniedIPHTTP.Rule = []string{"192.168.95.0/24"}
-				b.FindBackendPath(h.FindPath("/app4")[0].Link).DeniedIPHTTP.Exception = []string{"192.168.95.128/28"}
+		"test26": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
+				h.FindPath("/app1")[0].AllowedIPHTTP.Rule = []string{"10.0.0.0/8"}
+				h.FindPath("/app2")[0].AllowedIPHTTP.Exception = []string{"10.0.110.0/24"}
+				h.FindPath("/app3")[0].DeniedIPHTTP.Rule = []string{"192.168.95.0/24"}
+				h.FindPath("/app4")[0].DeniedIPHTTP.Exception = []string{"192.168.95.128/28"}
 			},
 			path: []string{"/app1", "/app2", "/app3", "/app4"},
 			expected: `
@@ -574,7 +584,7 @@ d1.local#/app1 path01`,
     # path02 = d1.local/app2
     # path03 = d1.local/app3
     # path04 = d1.local/app4
-    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_idpath__begin.map)
+    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_front_http_req__begin.map)
     acl allow_rule_src0 src 10.0.0.0/8
     http-request deny if { var(txn.pathID) -m str path01 } !allow_rule_src0
     acl allow_exception_src1 src 10.0.110.0/24
@@ -584,16 +594,16 @@ d1.local#/app1 path01`,
     acl deny_exception_src2 src 192.168.95.128/28
     http-request deny if { var(txn.pathID) -m str path04 } !deny_exception_src2`,
 			expCheck: map[string]string{
-				"_back_d1_app_8080_idpath__begin.map": `
+				"_back_d1_app_8080_front_http_req__begin.map": `
 d1.local#/app4 path04
 d1.local#/app3 path03
 d1.local#/app2 path02
 d1.local#/app1 path01`,
 			},
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
-				b.FindBackendPath(h.FindPath("/path")[0].Link).AllowedIPHTTP.Rule = []string{
+		"test27": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
+				h.FindPath("/path")[0].AllowedIPHTTP.Rule = []string{
 					"1.1.1.1", "1.1.1.2", "1.1.1.3", "1.1.1.4", "1.1.1.5",
 					"1.1.1.6", "1.1.1.7", "1.1.1.8", "1.1.1.9", "1.1.1.10",
 					"1.1.1.11",
@@ -604,19 +614,19 @@ d1.local#/app1 path01`,
     # path02 = d1.local/api
     # path01 = d1.local/app
     # path03 = d1.local/path
-    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_idpath__begin.map)
+    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_front_http_req__begin.map)
     acl allow_rule_src1 src 1.1.1.1 1.1.1.2 1.1.1.3 1.1.1.4 1.1.1.5 1.1.1.6 1.1.1.7 1.1.1.8 1.1.1.9 1.1.1.10
     acl allow_rule_src1 src 1.1.1.11
     http-request deny if { var(txn.pathID) -m str path03 } !allow_rule_src1`,
 			expCheck: map[string]string{
-				"_back_d1_app_8080_idpath__begin.map": `
+				"_back_d1_app_8080_front_http_req__begin.map": `
 d1.local#/path path03
 d1.local#/app path01
 d1.local#/api path02`,
 			},
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test28": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				b.AllowedIPTCP.Rule = []string{"10.0.0.0/8", "192.168.0.0/16"}
 				b.AllowedIPTCP.Exception = []string{"192.168.95.0/24"}
 				b.ModeTCP = true
@@ -627,8 +637,8 @@ d1.local#/api path02`,
     tcp-request content reject if allow_exception_tcp
     tcp-request content reject if !allow_rule_tcp`,
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test29": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				b.DeniedIPTCP.Rule = []string{"10.0.0.0/8", "192.168.0.0/16"}
 				b.ModeTCP = true
 			},
@@ -636,8 +646,8 @@ d1.local#/api path02`,
     acl deny_rule_tcp src 10.0.0.0/8 192.168.0.0/16
     tcp-request content reject if deny_rule_tcp`,
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test30": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				b.DeniedIPTCP.Rule = []string{"10.0.0.0/8", "192.168.0.0/16"}
 				b.DeniedIPTCP.Exception = []string{"192.168.95.0/24"}
 				b.ModeTCP = true
@@ -647,33 +657,33 @@ d1.local#/api path02`,
     acl deny_exception_tcp src 192.168.95.0/24
     tcp-request content reject if deny_rule_tcp !deny_exception_tcp`,
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
-				b.FindBackendPath(h.FindPath("/")[0].Link).MaxBodySize = 1024
-				b.FindBackendPath(h.FindPath("/app")[0].Link).MaxBodySize = 1024
+		"test31": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
+				h.FindPath("/")[0].MaxBodySize = 1024
+				h.FindPath("/app")[0].MaxBodySize = 1024
 			},
 			path: []string{"/", "/app"},
 			expected: `
     http-request use-service lua.send-413 if { req.body_size,sub(1024) gt 0 }`,
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
-				b.FindBackendPath(h.FindPath("/app")[0].Link).MaxBodySize = 2048
+		"test32": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
+				h.FindPath("/app")[0].MaxBodySize = 2048
 			},
 			path: []string{"/", "/app"},
 			expected: `
     # path01 = d1.local/
     # path02 = d1.local/app
-    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_idpath__begin.map)
+    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_front_http_req__begin.map)
     http-request use-service lua.send-413 if { var(txn.pathID) -m str path02 } { req.body_size,sub(2048) gt 0 }`,
 			expCheck: map[string]string{
-				"_back_d1_app_8080_idpath__begin.map": `
+				"_back_d1_app_8080_front_http_req__begin.map": `
 d1.local#/app path02
 d1.local#/ path01`,
 			},
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test33": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				b.Headers = []*hatypes.BackendHeader{
 					{Name: "Name", Value: "Value"},
 				}
@@ -681,8 +691,8 @@ d1.local#/ path01`,
 			expected: `
     http-request set-header Name Value`,
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test34": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				b.Headers = []*hatypes.BackendHeader{
 					{Name: "X-ID", Value: "abc"},
 					{Name: "Host", Value: "app.domain"},
@@ -692,9 +702,9 @@ d1.local#/ path01`,
     http-request set-header X-ID abc
     http-request set-header Host app.domain`,
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
-				auth := &b.FindBackendPath(h.FindPath("/app1")[0].Link).AuthExternal
+		"test35": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
+				auth := &h.FindPath("/app1")[0].AuthExtBack
 				auth.AuthBackendName = "_auth_4001"
 				auth.AuthPath = "/oauth2/auth"
 				auth.HeadersRequest = []string{"*"}
@@ -706,13 +716,13 @@ d1.local#/ path01`,
 			expected: `
     # path01 = d1.local/app1
     # path02 = d1.local/app2
-    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_idpath__begin.map)
+    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_front_http_req__begin.map)
     http-request lua.auth-intercept _auth_4001 /oauth2/auth GET '*' '*' '*' if { var(txn.pathID) -m str path01 }
     http-request deny if !{ var(txn.auth_response_successful) -m bool } { var(txn.pathID) -m str path01 }`,
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
-				auth := &b.FindBackendPath(h.FindPath("/app1")[0].Link).AuthExternal
+		"test36": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
+				auth := &h.FindPath("/app1")[0].AuthExtBack
 				auth.AuthBackendName = "_auth_4001"
 				auth.AuthPath = "/oauth2/auth"
 				auth.HeadersRequest = []string{"X-UserID1", "X-Token1"}
@@ -725,13 +735,13 @@ d1.local#/ path01`,
 			expected: `
     # path01 = d1.local/app1
     # path02 = d1.local/app2
-    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_idpath__begin.map)
+    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_front_http_req__begin.map)
     http-request lua.auth-intercept _auth_4001 /oauth2/auth GET 'X-UserID1,X-Token1' 'X-UserID2,X-Token2' 'X-UserID3,X-Token3' if { var(txn.pathID) -m str path01 }
     http-request redirect location http://auth.local/auth1 if !{ var(txn.auth_response_successful) -m bool } { var(txn.pathID) -m str path01 }`,
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
-				auth := &b.FindBackendPath(h.FindPath("/app1")[0].Link).AuthExternal
+		"test37": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
+				auth := &h.FindPath("/app1")[0].AuthExtBack
 				auth.AuthBackendName = "_auth_4001"
 				auth.AuthPath = "/oauth2/auth"
 				auth.HeadersRequest = []string{"*"}
@@ -745,25 +755,25 @@ d1.local#/ path01`,
 			expected: `
     # path01 = d1.local/app1
     # path02 = d1.local/app2
-    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_idpath__begin.map)
+    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_front_http_req__begin.map)
     http-request lua.auth-intercept _auth_4001 /oauth2/auth HEAD '*' '-' '-' if { var(txn.pathID) -m str path01 }
     http-request redirect location http://auth.local/login if !{ var(txn.auth_response_successful) -m bool } { var(txn.pathID) -m str path01 }
     http-request set-header X-Auth-Request-Email %[var(req.auth_response_header.x_auth_request_email)] if { var(req.auth_response_header.x_auth_request_email) -m found } { var(txn.pathID) -m str path01 }`,
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
-				auth := &b.FindBackendPath(h.FindPath("/app1")[0].Link).AuthExternal
+		"test38": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
+				auth := &h.FindPath("/app1")[0].AuthExtBack
 				auth.AlwaysDeny = true
 			},
 			path: []string{"/app1", "/app2"},
 			expected: `
     # path01 = d1.local/app1
     # path02 = d1.local/app2
-    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_idpath__begin.map)
+    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_front_http_req__begin.map)
     http-request deny if { var(txn.pathID) -m str path01 }`,
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test39": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				b.SourceIPs = []net.IP{net.ParseIP("192.168.0.2"), net.ParseIP("192.168.0.3")}
 			},
 			// IP distribution starts based on the hash of the backend name.
@@ -771,14 +781,14 @@ d1.local#/ path01`,
 			// the number of IPs or the name of the backend change.
 			srvsuffix: "source 192.168.0.3",
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test40": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				b.HealthCheck.Interval = "2s"
 			},
 			srvsuffix: "check inter 2s",
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test41": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				b.HealthCheck.URI = "/check"
 				b.HealthCheck.Port = 4000
 			},
@@ -786,29 +796,29 @@ d1.local#/ path01`,
     option httpchk /check`,
 			srvsuffix: "check port 4000",
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test42": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				b.AgentCheck.Port = 8000
 				b.AgentCheck.Interval = "2s"
 			},
 			srvsuffix: "agent-check agent-port 8000 agent-inter 2s",
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test43": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				b.Server.Secure = true
 			},
 			srvsuffix: "ssl verify none",
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test44": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				b.Server.Secure = true
 				b.Server.Ciphers = "ECDHE-ECDSA-AES128-GCM-SHA256"
 				b.Server.CipherSuites = "TLS_AES_128_GCM_SHA256"
 			},
 			srvsuffix: "ssl ciphers ECDHE-ECDSA-AES128-GCM-SHA256 ciphersuites TLS_AES_128_GCM_SHA256 verify none",
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test45": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				b.Server.Secure = true
 				b.Server.CrtFilename = "/var/haproxy/ssl/client.pem"
 				b.Server.CipherSuites = "TLS_AES_128_GCM_SHA256"
@@ -816,74 +826,74 @@ d1.local#/ path01`,
 			},
 			srvsuffix: "ssl ciphersuites TLS_AES_128_GCM_SHA256 no-sslv3 no-tlsv10 no-tlsv11 no-tlsv12 no-tls-tickets crt /var/haproxy/ssl/client.pem verify none",
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test46": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				b.Server.Secure = true
 				b.Server.CrtFilename = "/var/haproxy/ssl/client.pem"
 			},
 			srvsuffix: "ssl crt /var/haproxy/ssl/client.pem verify none",
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test47": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				b.Server.Secure = true
 				b.Server.CAFilename = "/var/haproxy/ssl/ca.pem"
 			},
 			srvsuffix: "ssl verify required ca-file /var/haproxy/ssl/ca.pem",
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test48": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				b.Server.Secure = true
 				b.Server.CAFilename = "/var/haproxy/ssl/ca.pem"
 				b.Server.CRLFilename = "/var/haproxy/ssl/crl.pem"
 			},
 			srvsuffix: "ssl verify required ca-file /var/haproxy/ssl/ca.pem crl-file /var/haproxy/ssl/crl.pem",
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test49": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				b.Server.Secure = true
 				b.Server.SNI = "var(req.host)"
 			},
 			srvsuffix: "ssl sni var(req.host) verify none",
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test50": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				b.Server.Secure = true
 				b.Server.CAFilename = "/var/haproxy/ssl/ca.pem"
 				b.Server.SNI = "ssl_fc_sni"
 			},
 			srvsuffix: "ssl sni ssl_fc_sni verify required ca-file /var/haproxy/ssl/ca.pem",
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test51": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				b.Server.Secure = true
 				b.Server.CAFilename = "/var/haproxy/ssl/ca.pem"
 				b.Server.VerifyHost = "domain.tld"
 			},
 			srvsuffix: "ssl verify required ca-file /var/haproxy/ssl/ca.pem verifyhost domain.tld",
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test52": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				b.Server.Protocol = "h2"
 			},
 			srvsuffix: "proto h2",
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test53": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				b.Server.Protocol = "h2"
 				b.Server.Secure = true
 			},
 			srvsuffix: "proto h2 alpn h2 ssl verify none",
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test54": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				b.Server.Protocol = "h2"
 				b.Server.Secure = true
 				b.Server.CAFilename = "/var/haproxy/ssl/ca.pem"
 			},
 			srvsuffix: "proto h2 alpn h2 ssl verify required ca-file /var/haproxy/ssl/ca.pem",
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test55": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				b.Limit.Connections = 200
 				b.Limit.RPS = 20
 				b.Limit.Whitelist = []string{"192.168.0.0/16", "10.1.1.101"}
@@ -895,8 +905,8 @@ d1.local#/ path01`,
     http-request deny deny_status 429 if !wlist_conn { sc1_conn_cur gt 200 }
     http-request deny deny_status 429 if !wlist_conn { sc1_conn_rate gt 20 }`,
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test56": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				b.Limit.RPS = 20
 			},
 			expected: `
@@ -904,8 +914,8 @@ d1.local#/ path01`,
     http-request track-sc1 src
     http-request deny deny_status 429 if { sc1_conn_rate gt 20 }`,
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test57": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				b.Limit.Connections = 200
 			},
 			expected: `
@@ -913,8 +923,8 @@ d1.local#/ path01`,
     http-request track-sc1 src
     http-request deny deny_status 429 if { sc1_conn_cur gt 200 }`,
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test58": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				b.ModeTCP = true
 				b.Limit.Connections = 200
 				b.Limit.RPS = 20
@@ -927,14 +937,14 @@ d1.local#/ path01`,
     tcp-request content reject if !wlist_conn { sc1_conn_cur gt 200 }
     tcp-request content reject if !wlist_conn { sc1_conn_rate gt 20 }`,
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test59": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				b.Server.SendProxy = "send-proxy-v2"
 			},
 			srvsuffix: "send-proxy-v2",
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test60": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				b.BlueGreen.CookieName = "ServerName"
 				e1, e2, e3 := *endpointS31, *endpointS32, *endpointS33
 				b.Endpoints = []*hatypes.Endpoint{&e1, &e2, &e3}
@@ -947,8 +957,8 @@ d1.local#/ path01`,
     server s32 172.17.0.132:8080 weight 100
     server s33 172.17.0.133:8080 weight 100`,
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test61": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				b.BlueGreen.HeaderName = "X-Svc"
 				e1, e2, e3 := *endpointS31, *endpointS32, *endpointS33
 				b.Endpoints = []*hatypes.Endpoint{&e1, &e2, &e3}
@@ -961,8 +971,8 @@ d1.local#/ path01`,
     server s32 172.17.0.132:8080 weight 100
     server s33 172.17.0.133:8080 weight 100`,
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test62": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				b.BlueGreen.CookieName = "ServerName"
 				b.BlueGreen.HeaderName = "X-Svc"
 				e1, e2, e3 := *endpointS31, *endpointS32, *endpointS33
@@ -981,8 +991,8 @@ d1.local#/ path01`,
     server s33 172.17.0.133:8080 weight 100`,
 		},
 		// simulates a config where the cookie value is a pod id
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test63": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				b.Cookie.Name = "serverId"
 				b.Cookie.Strategy = "insert"
 				b.Cookie.Keywords = "nocache"
@@ -996,8 +1006,8 @@ d1.local#/ path01`,
     cookie serverId insert nocache`,
 		},
 		// simulates a config where the cookie "preserve" option is used
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test64": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				b.Cookie.Name = "serverId"
 				b.Cookie.Strategy = "insert"
 				b.Cookie.Preserve = true
@@ -1010,14 +1020,14 @@ d1.local#/ path01`,
 			expected: `
     cookie serverId insert preserve nocache`,
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test65": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				b.Endpoints[0].PUID = 1234567
 			},
 			srvsuffix: "id 1234567",
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test66": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				b.Server.FastCGIApp = "app1"
 				b.Server.Protocol = "fcgi"
 			},
@@ -1026,19 +1036,20 @@ d1.local#/ path01`,
     filter fcgi-app app1
     use-fcgi-app app1`,
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test67": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
+				f := c.httpFrontend(80)
 				link1 := hatypes.CreatePathLink("/app1", hatypes.MatchPrefix).
 					WithHeadersMatch(hatypes.HTTPHeaderMatch{{Name: "x-user", Value: "myusr1"}})
 				link2 := hatypes.CreatePathLink("/app2", hatypes.MatchPrefix).
 					WithHeadersMatch(hatypes.HTTPHeaderMatch{{Name: "x-user", Value: "myusr2"}})
 
-				hdef := c.hosts.AcquireHost(hatypes.DefaultHost)
-				b.FindBackendPath(hdef.AddLink(b, link1)).MaxBodySize = 1048576
-				b.FindBackendPath(hdef.AddLink(b, link2)).MaxBodySize = 2097152
+				hdef := f.AcquireHost(hatypes.DefaultHost)
+				hdef.AddLink(b, link1).MaxBodySize = 1048576
+				hdef.AddLink(b, link2).MaxBodySize = 2097152
 
-				b.FindBackendPath(h.AddLink(b, link1)).MaxBodySize = 1048576
-				b.FindBackendPath(h.AddLink(b, link2)).MaxBodySize = 2097152
+				h.AddLink(b, link1).MaxBodySize = 1048576
+				h.AddLink(b, link2).MaxBodySize = 2097152
 			},
 			expected: `
     # path02 = <default>/app1
@@ -1046,11 +1057,11 @@ d1.local#/ path01`,
     # path01 = d1.local/
     # path04 = d1.local/app1
     # path05 = d1.local/app2
-    http-request set-var(txn.pathID) var(req.base),map_dir(/etc/haproxy/maps/_back_d1_app_8080_idpath__prefix_01.map) if { hdr(x-user) -- 'myusr2' }
-    http-request set-var(txn.pathID) var(req.base),map_dir(/etc/haproxy/maps/_back_d1_app_8080_idpath__prefix_02.map) if !{ var(txn.pathID) -m found } { hdr(x-user) -- 'myusr1' }
-    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_idpath__begin.map) if !{ var(txn.pathID) -m found }
-    http-request set-var(txn.pathID) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_back_d1_app_8080_idpathdef__prefix_01.map) if !{ var(txn.pathID) -m found } { hdr(x-user) -- 'myusr2' }
-    http-request set-var(txn.pathID) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_back_d1_app_8080_idpathdef__prefix_02.map) if !{ var(txn.pathID) -m found } { hdr(x-user) -- 'myusr1' }
+    http-request set-var(txn.pathID) var(req.base),map_dir(/etc/haproxy/maps/_back_d1_app_8080_front_http_req__prefix_01.map) if { hdr(x-user) -- 'myusr2' }
+    http-request set-var(txn.pathID) var(req.base),map_dir(/etc/haproxy/maps/_back_d1_app_8080_front_http_req__prefix_02.map) if !{ var(txn.pathID) -m found } { hdr(x-user) -- 'myusr1' }
+    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_front_http_req__begin.map) if !{ var(txn.pathID) -m found }
+    http-request set-var(txn.pathID) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_back_d1_app_8080_front_http_def__prefix_01.map) if !{ var(txn.pathID) -m found } { hdr(x-user) -- 'myusr2' }
+    http-request set-var(txn.pathID) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_back_d1_app_8080_front_http_def__prefix_02.map) if !{ var(txn.pathID) -m found } { hdr(x-user) -- 'myusr1' }
     http-request use-service lua.send-413 if { var(txn.pathID) -m str path02 path04 } { req.body_size,sub(1048576) gt 0 }
     http-request use-service lua.send-413 if { var(txn.pathID) -m str path03 path05 } { req.body_size,sub(2097152) gt 0 }`,
 			expFronts: `frontend _front_http
@@ -1061,34 +1072,21 @@ d1.local#/ path01`,
     http-request set-var(req.backend) var(req.base),map_dir(/etc/haproxy/maps/_front_http_host__prefix_01.map) if { hdr(x-user) -- 'myusr1' }
     http-request set-var(req.backend) var(req.base),map_dir(/etc/haproxy/maps/_front_http_host__prefix_02.map) if !{ var(req.backend) -m found } { hdr(x-user) -- 'myusr2' }
     http-request set-var(req.backend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_host__begin.map) if !{ var(req.backend) -m found }
-    http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_front_defaulthost__prefix_01.map) if !{ var(req.backend) -m found } { hdr(x-user) -- 'myusr1' }
-    http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_front_defaulthost__prefix_02.map) if !{ var(req.backend) -m found } !{ var(req.defaultbackend) -m found } { hdr(x-user) -- 'myusr2' }
+    http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_front_http_defaulthost__prefix_01.map) if !{ var(req.backend) -m found } { hdr(x-user) -- 'myusr1' }
+    http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_front_http_defaulthost__prefix_02.map) if !{ var(req.backend) -m found } !{ var(req.defaultbackend) -m found } { hdr(x-user) -- 'myusr2' }
     use_backend %[var(req.backend)] if { var(req.backend) -m found }
     use_backend %[var(req.defaultbackend)]
-    default_backend _error404
-frontend _front_https
-    mode http
-    bind :443 ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_bind_crt.list ca-ignore-err all crt-ignore-err all
-    <<set-req-base>>
-    http-request set-var(req.hostbackend) var(req.base),map_dir(/etc/haproxy/maps/_front_https_host__prefix_01.map) if { hdr(x-user) -- 'myusr1' }
-    http-request set-var(req.hostbackend) var(req.base),map_dir(/etc/haproxy/maps/_front_https_host__prefix_02.map) if !{ var(req.hostbackend) -m found } { hdr(x-user) -- 'myusr2' }
-    http-request set-var(req.hostbackend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_https_host__begin.map) if !{ var(req.hostbackend) -m found }
-    http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_front_defaulthost__prefix_01.map) if !{ var(req.hostbackend) -m found } { hdr(x-user) -- 'myusr1' }
-    http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_front_defaulthost__prefix_02.map) if !{ var(req.hostbackend) -m found } !{ var(req.defaultbackend) -m found } { hdr(x-user) -- 'myusr2' }
-    <<https-headers>>
-    use_backend %[var(req.hostbackend)] if { var(req.hostbackend) -m found }
-    use_backend %[var(req.defaultbackend)]
     default_backend _error404`,
 			expCheck: map[string]string{
-				"_back_d1_app_8080_idpath__prefix_01.map":    "d1.local#/app2 path05",
-				"_back_d1_app_8080_idpath__prefix_02.map":    "d1.local#/app1 path04",
-				"_back_d1_app_8080_idpath__begin.map":        "d1.local#/ path01",
-				"_back_d1_app_8080_idpathdef__prefix_01.map": "<default>#/app2 path03",
-				"_back_d1_app_8080_idpathdef__prefix_02.map": "<default>#/app1 path02",
+				"_back_d1_app_8080_front_http_req__prefix_01.map": "d1.local#/app2 path05",
+				"_back_d1_app_8080_front_http_req__prefix_02.map": "d1.local#/app1 path04",
+				"_back_d1_app_8080_front_http_req__begin.map":     "d1.local#/ path01",
+				"_back_d1_app_8080_front_http_def__prefix_01.map": "<default>#/app2 path03",
+				"_back_d1_app_8080_front_http_def__prefix_02.map": "<default>#/app1 path02",
 			},
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test68": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				b.CustomConfigEarly = []string{"## early custom for HTTP backend"}
 				b.CustomConfigLate = []string{"## late custom for HTTP backend"}
 			},
@@ -1096,8 +1094,8 @@ frontend _front_https
     ## early custom for HTTP backend
     ## late custom for HTTP backend`,
 		},
-		{
-			doconfig: func(c *config, h *hatypes.Host, b *hatypes.Backend) {
+		"test69": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
 				b.ModeTCP = true
 				b.CustomConfigEarly = []string{"## early custom for TCP backend"}
 				b.CustomConfigLate = []string{"## late custom for TCP backend"}
@@ -1106,49 +1104,89 @@ frontend _front_https
     ## early custom for TCP backend
     ## late custom for TCP backend`,
 		},
+		"test70 paths from distinct frontends": {
+			doconfig: func(c *testConfig, h *hatypes.Host, b *hatypes.Backend) {
+				f1 := c.httpFrontend(80)
+				f2 := c.httpFrontend(8001)
+				f3 := c.httpFrontend(8002)
+				f1.AcquireHost("d1.local").AddPath(b, "/app1", hatypes.MatchBegin).SSLRedirect = true
+				f2.AcquireHost("d1.local").AddPath(b, "/app1", hatypes.MatchBegin).HSTS.Enabled = true
+				f3.AcquireHost("d1.local").AddPath(b, "/app1", hatypes.MatchBegin).HSTS.Enabled = true
+			},
+			path: []string{},
+			expFronts: `<<frontend-http>>
+    default_backend _error404
+frontend _front_http_8001
+    mode http
+    bind :8001
+    <<set-req-base>>
+    <<http-headers>>
+    http-request set-var(req.backend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_8001_host__begin.map)
+    use_backend %[var(req.backend)] if { var(req.backend) -m found }
+    default_backend _error404
+frontend _front_http_8002
+    mode http
+    bind :8002
+    <<set-req-base>>
+    <<http-headers>>
+    http-request set-var(req.backend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_8002_host__begin.map)
+    use_backend %[var(req.backend)] if { var(req.backend) -m found }
+    default_backend _error404`,
+			expected: `
+    acl https-request ssl_fc
+    # path01 = fe:_front_http -- host/path:d1.local/app1
+    # path02 = fe:_front_http_8001/_front_http_8002 -- host/path:d1.local/app1
+    http-request set-var-fmt(req.fe) "%f"
+    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_front_http_req__begin.map) if { var(req.fe) -m str _front_http }
+    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_front_http_8001_req__begin.map) if { var(req.fe) -m str _front_http_8001 _front_http_8002 }
+    http-request redirect scheme https if !https-request { var(txn.pathID) -m str path01 }
+    http-response set-header Strict-Transport-Security "max-age=0" if https-request { var(txn.pathID) -m str path02 }`,
+		},
 	}
-	for _, test := range testCases {
-		c := setup(t)
+	for name, test := range testCases {
+		t.Run(name, func(t *testing.T) {
+			c := setup(t)
 
-		if len(test.path) == 0 {
-			test.path = []string{"/"}
-		}
-		if test.srvsuffix != "" {
-			test.srvsuffix = " " + test.srvsuffix
-		}
+			if test.path == nil {
+				test.path = []string{"/"}
+			}
+			if test.srvsuffix != "" {
+				test.srvsuffix = " " + test.srvsuffix
+			}
 
-		var h *hatypes.Host
-		var b = c.config.Backends().AcquireBackend("d1", "app", "8080")
-		ep := *endpointS1
-		b.Endpoints = []*hatypes.Endpoint{&ep}
-		h = c.config.Hosts().AcquireHost("d1.local")
-		for j, p := range test.path {
-			match := hatypes.MatchBegin
-			if test.match != nil {
-				match = test.match[j]
+			var h *hatypes.Host
+			var b = c.config.Backends().AcquireBackend("d1", "app", "8080")
+			ep := *endpointS1
+			b.Endpoints = []*hatypes.Endpoint{&ep}
+			h = c.httpFrontend(80).AcquireHost("d1.local")
+			for j, p := range test.path {
+				match := hatypes.MatchBegin
+				if test.match != nil {
+					match = test.match[j]
+				}
+				h.AddPath(b, p, match)
 			}
-			h.AddPath(b, p, match)
-		}
-		test.doconfig(c.config, h, b)
+			test.doconfig(c, h, b)
 
-		var mode string
-		if b.ModeTCP {
-			mode = "tcp"
-		} else {
-			mode = "http"
-		}
+			var mode string
+			if b.ModeTCP {
+				mode = "tcp"
+			} else {
+				mode = "http"
+			}
 
-		if test.expFronts == "" {
-			test.expFronts = "<<frontends-default>>"
-		}
+			if test.expFronts == "" {
+				test.expFronts = `<<frontend-http>>
+    default_backend _error404`
+			}
 
-		var srv string
-		if !test.skipSrv {
-			srv = `
+			var srv string
+			if !test.skipSrv {
+				srv = `
     server s1 172.17.0.11:8080 weight 100` + test.srvsuffix
-		}
-		c.Update()
-		c.checkConfig(`
+			}
+			c.Update()
+			c.checkConfig(`
 <<global>>
 <<defaults>>
 backend d1_app_8080
@@ -1158,12 +1196,13 @@ backend d1_app_8080
 <<support>>
 `)
 
-		for mapName, content := range test.expCheck {
-			c.checkMap(mapName, content)
-		}
+			for mapName, content := range test.expCheck {
+				c.checkMap(mapName, content)
+			}
 
-		c.logger.CompareLogging(defaultLogging)
-		c.teardown()
+			c.logger.CompareLogging(defaultLogging)
+			c.teardown()
+		})
 	}
 }
 
@@ -1182,10 +1221,6 @@ func TestInstanceClean(t *testing.T) {
 <<global>>
 <<defaults>>
 <<backends-default>>
-<<frontend-http-clean>>
-    default_backend _error404
-<<frontend-https-clean>>
-    default_backend _error404
 <<support>>
 `)
 	c.logger.CompareLogging(defaultLogging)
@@ -1198,7 +1233,7 @@ func TestInstanceBare(t *testing.T) {
 	var h *hatypes.Host
 	var b = c.config.Backends().AcquireBackend("d1", "app", "8080")
 	b.Endpoints = []*hatypes.Endpoint{endpointS1}
-	h = c.config.Hosts().AcquireHost("d1.local")
+	h = c.httpFrontend(80).AcquireHost("d1.local")
 	h.AddPath(b, "/", hatypes.MatchBegin)
 
 	c.Update()
@@ -1209,7 +1244,8 @@ backend d1_app_8080
     mode http
     server s1 172.17.0.11:8080 weight 100
 <<backends-default>>
-<<frontends-default>>
+<<frontend-http>>
+    default_backend _error404
 <<support>>
 `)
 	c.logger.CompareLogging(defaultLogging)
@@ -1219,12 +1255,14 @@ func TestInstanceBareHTTP(t *testing.T) {
 	c := setup(t)
 	defer c.teardown()
 
-	var h *hatypes.Host
-	var b = c.config.Backends().AcquireBackend("d1", "app", "8080")
+	b := c.config.Backends().AcquireBackend("d1", "app", "8080")
 	b.Endpoints = []*hatypes.Endpoint{endpointS1}
-	h = c.config.Hosts().AcquireHost("d1.local")
-	h.TLS.UseDefaultCrt = false
-	h.AddPath(b, "/", hatypes.MatchBegin)
+	f1 := c.httpFrontend(80)
+	f2 := c.httpsFrontend(443)
+	h1 := f1.AcquireHost("d1.local")
+	h2 := f2.AcquireHost("d1.local")
+	h1.AddPath(b, "/", hatypes.MatchBegin)
+	h2.AddPath(b, "/", hatypes.MatchBegin)
 
 	c.Update()
 	c.checkConfig(`
@@ -1234,58 +1272,289 @@ backend d1_app_8080
     mode http
     server s1 172.17.0.11:8080 weight 100
 <<backends-default>>
-<<frontend-http>>
+<<frontends-default>>
+<<support>>
+`)
+
+	c.checkMap("_front_http_host__begin.map", `
+d1.local#/ d1_app_8080`)
+	c.checkMap("_front_https_host__begin.map", `
+d1.local#/ d1_app_8080`)
+
+	c.logger.CompareLogging(defaultLogging)
+}
+
+func TestInstanceFrontendList(t *testing.T) {
+	c := setup(t)
+	defer c.teardown()
+
+	b1 := c.config.Backends().AcquireBackend("d1", "app", "8080")
+	b1.Endpoints = []*hatypes.Endpoint{endpointS1}
+	b2 := c.config.Backends().AcquireBackend("d2", "app", "8080")
+	b2.Endpoints = []*hatypes.Endpoint{endpointS21}
+	bssl := c.config.Backends().AcquireBackend("d3", "app", "8443")
+	bssl.ModeTCP = true
+	bssl.Endpoints = []*hatypes.Endpoint{endpointS41s}
+
+	frontends := []*hatypes.Frontend{
+		c.httpFrontend(8080),  // http, default http name
+		c.httpFrontend(8081),  // http
+		c.httpsFrontend(8443), // https d2.local passthrough, default https name
+		c.httpsFrontend(8444), // https d2.local passthrough
+		c.httpsFrontend(8543), // https
+		c.httpsFrontend(8544), // https
+	}
+	domains := []string{"d1.local", "d2.local"}
+	backhttp := []*hatypes.Backend{b1, b2}
+	backssl := []*hatypes.Backend{bssl}
+	paths := []string{"/", "/api", "/static"}
+	for _, f := range frontends {
+		for _, d := range domains {
+			h := f.AcquireHost(d)
+			sslpassthrough := d == "d2.local" && f.Port() > 8400 && f.Port() < 8500
+			if sslpassthrough {
+				h.SSLPassthrough = true
+				for _, b := range backssl {
+					h.AddPath(b, "/", hatypes.MatchBegin)
+				}
+			} else {
+				for _, b := range backhttp {
+					for _, p := range paths {
+						h.AddPath(b, p, hatypes.MatchBegin)
+					}
+				}
+			}
+		}
+	}
+
+	c.Update()
+	c.checkConfig(`
+<<global>>
+<<defaults>>
+backend d1_app_8080
+    mode http
+    server s1 172.17.0.11:8080 weight 100
+backend d2_app_8080
+    mode http
+    server s21 172.17.0.121:8080 weight 100
+backend d3_app_8443
+    mode tcp
+    server s41s 172.17.0.141:8443 weight 100
+<<backends-default>>
+frontend _front_http
+    mode http
+    bind :8080
+    <<set-req-base>>
+    <<http-headers>>
+    http-request set-var(req.backend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_host__begin.map)
+    use_backend %[var(req.backend)] if { var(req.backend) -m found }
+    default_backend _error404
+frontend _front_http_8081
+    mode http
+    bind :8081
+    <<set-req-base>>
+    <<http-headers>>
+    http-request set-var(req.backend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_8081_host__begin.map)
+    use_backend %[var(req.backend)] if { var(req.backend) -m found }
+    default_backend _error404
+listen _front__tls
+    mode tcp
+    bind :8443
+    tcp-request inspect-delay 5s
+    tcp-request content set-var(req.sslpassback) req.ssl_sni,lower,map_str(/etc/haproxy/maps/_front_https_sslpassthrough__exact.map)
+    tcp-request content accept if { req.ssl_hello_type 1 }
+    use_backend %[var(req.sslpassback)] if { var(req.sslpassback) -m found }
+    server _default_server_front_https_socket unix@/var/run/haproxy/_front_https_socket.sock send-proxy-v2
+frontend _front_https__local
+    mode http
+    bind unix@/var/run/haproxy/_front_https_socket.sock accept-proxy ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_https_bind_crt.list ca-ignore-err all crt-ignore-err all
+    <<set-req-base>>
+    http-request set-var(req.hostbackend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_https_host__begin.map)
+    <<https-headers>>
+    use_backend %[var(req.hostbackend)] if { var(req.hostbackend) -m found }
+    default_backend _error404
+listen _front__tls_8444
+    mode tcp
+    bind :8444
+    tcp-request inspect-delay 5s
+    tcp-request content set-var(req.sslpassback) req.ssl_sni,lower,map_str(/etc/haproxy/maps/_front_https_8444_sslpassthrough__exact.map)
+    tcp-request content accept if { req.ssl_hello_type 1 }
+    use_backend %[var(req.sslpassback)] if { var(req.sslpassback) -m found }
+    server _default_server_front_https_8444_socket unix@/var/run/haproxy/_front_https_8444_socket.sock send-proxy-v2
+frontend _front_https_8444__local
+    mode http
+    bind unix@/var/run/haproxy/_front_https_8444_socket.sock accept-proxy ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_https_8444_bind_crt.list ca-ignore-err all crt-ignore-err all
+    <<set-req-base>>
+    http-request set-var(req.hostbackend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_https_8444_host__begin.map)
+    <<https-headers>>
+    use_backend %[var(req.hostbackend)] if { var(req.hostbackend) -m found }
+    default_backend _error404
+frontend _front_https_8543
+    mode http
+    bind :8543 ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_https_8543_bind_crt.list ca-ignore-err all crt-ignore-err all
+    <<set-req-base>>
+    http-request set-var(req.hostbackend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_https_8543_host__begin.map)
+    <<https-headers>>
+    use_backend %[var(req.hostbackend)] if { var(req.hostbackend) -m found }
+    default_backend _error404
+frontend _front_https_8544
+    mode http
+    bind :8544 ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_https_8544_bind_crt.list ca-ignore-err all crt-ignore-err all
+    <<set-req-base>>
+    http-request set-var(req.hostbackend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_https_8544_host__begin.map)
+    <<https-headers>>
+    use_backend %[var(req.hostbackend)] if { var(req.hostbackend) -m found }
     default_backend _error404
-<<frontend-https-clean>>
+<<support>>
+`)
+
+	c.logger.CompareLogging(defaultLogging)
+}
+
+func TestInstanceFrontendScopedBackend(t *testing.T) {
+	c := setup(t)
+	defer c.teardown()
+
+	b := c.config.Backends().AcquireBackend("d1", "app", "8080")
+	b.Endpoints = []*hatypes.Endpoint{endpointS1}
+
+	f1 := c.httpFrontend(8080)
+	f1.Name = "_front_http_8080"
+	h11 := f1.AcquireHost("d1.local")
+	h12 := f1.AcquireHost("d2.local")
+
+	f2 := c.httpFrontend(8081)
+	f2.Name = "_front_http_8081"
+	h21 := f2.AcquireHost("d1.local")
+	h22 := f2.AcquireHost("d2.local")
+
+	h11.AddPath(b, "/", hatypes.MatchBegin).AllowedIPHTTP.Rule = []string{"10.0.0.11"}
+	h11.AddPath(b, "/static", hatypes.MatchBegin).Cors.Enabled = true
+	h12.AddPath(b, "/", hatypes.MatchBegin).AllowedIPHTTP.Rule = []string{"10.0.0.12"}
+	h12.AddPath(b, "/api", hatypes.MatchBegin).HSTS.Enabled = true
+	h21.AddPath(b, "/", hatypes.MatchBegin).AllowedIPHTTP.Rule = []string{"10.0.0.11"}
+	h21.AddPath(b, "/static", hatypes.MatchBegin).MaxBodySize = 1048576
+	h22.AddPath(b, "/", hatypes.MatchBegin).AllowedIPHTTP.Rule = []string{"10.0.0.12"}
+	h22.AddPath(b, "/api", hatypes.MatchBegin).RewriteURL = "/apiv1"
+
+	c.Update()
+	c.checkConfig(`
+<<global>>
+<<defaults>>
+backend d1_app_8080
+    mode http
+    acl https-request ssl_fc
+    # path01 = fe:_front_http_8080 -- host/path:d1.local/
+    # path02 = fe:_front_http_8080 -- host/path:d1.local/static
+    # path03 = fe:_front_http_8080 -- host/path:d2.local/
+    # path04 = fe:_front_http_8080 -- host/path:d2.local/api
+    # path05 = fe:_front_http_8081 -- host/path:d1.local/
+    # path06 = fe:_front_http_8081 -- host/path:d1.local/static
+    # path07 = fe:_front_http_8081 -- host/path:d2.local/
+    # path08 = fe:_front_http_8081 -- host/path:d2.local/api
+    http-request set-var-fmt(req.fe) "%f"
+    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_front_http_8080_req__begin.map) if { var(req.fe) -m str _front_http_8080 }
+    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_front_http_8081_req__begin.map) if { var(req.fe) -m str _front_http_8081 }
+    acl allow_rule_src0 src 10.0.0.11
+    http-request deny if { var(txn.pathID) -m str path01 path05 } !allow_rule_src0
+    acl allow_rule_src2 src 10.0.0.12
+    http-request deny if { var(txn.pathID) -m str path03 path07 } !allow_rule_src2
+    http-request use-service lua.send-413 if { var(txn.pathID) -m str path06 } { req.body_size,sub(1048576) gt 0 }
+    http-request replace-path ^/api(.*)$       /apiv1\1     if { var(txn.pathID) -m str path08 }
+    http-response set-header Strict-Transport-Security "max-age=0" if https-request { var(txn.pathID) -m str path04 }
+    server s1 172.17.0.11:8080 weight 100
+<<backends-default>>
+frontend _front_http_8080
+    mode http
+    bind :8080
+    <<set-req-base>>
+    <<http-headers>>
+    http-request set-var(req.backend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_8080_host__begin.map)
+    use_backend %[var(req.backend)] if { var(req.backend) -m found }
+    default_backend _error404
+frontend _front_http_8081
+    mode http
+    bind :8081
+    <<set-req-base>>
+    <<http-headers>>
+    http-request set-var(req.backend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_8081_host__begin.map)
+    use_backend %[var(req.backend)] if { var(req.backend) -m found }
     default_backend _error404
 <<support>>
 `)
+
+	c.checkMap("_back_d1_app_8080_front_http_8080_req__begin.map", `
+d1.local#/static path02
+d1.local#/ path01
+d2.local#/api path04
+d2.local#/ path03
+`)
+
+	c.checkMap("_back_d1_app_8080_front_http_8081_req__begin.map", `
+d1.local#/static path06
+d1.local#/ path05
+d2.local#/api path08
+d2.local#/ path07
+`)
+
+	c.checkMap("_front_http_8080_host__begin.map", `
+d1.local#/static d1_app_8080
+d1.local#/ d1_app_8080
+d2.local#/api d1_app_8080
+d2.local#/ d1_app_8080
+`)
+
+	c.checkMap("_front_http_8081_host__begin.map", `
+d1.local#/static d1_app_8080
+d1.local#/ d1_app_8080
+d2.local#/api d1_app_8080
+d2.local#/ d1_app_8080
+`)
+
 	c.logger.CompareLogging(defaultLogging)
 }
 
-func TestInstanceGlobalBind(t *testing.T) {
+func TestInstanceFrontendBind(t *testing.T) {
 	testCases := []struct {
-		bind          hatypes.GlobalBindConfig
+		httpBind      string
+		httpsBind     string
+		acceptProxy   bool
 		expectedHTTP  string
 		expectedHTTPS string
 	}{
-		// 0
-		{},
 		// 1
 		{
-			bind: hatypes.GlobalBindConfig{
-				HTTPBind:    ":80",
-				HTTPSBind:   ":443",
-				AcceptProxy: true,
-			},
+			httpBind:      ":80",
+			httpsBind:     ":443",
+			acceptProxy:   true,
 			expectedHTTP:  "bind :80 accept-proxy",
-			expectedHTTPS: "bind :443 accept-proxy ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_bind_crt.list ca-ignore-err all crt-ignore-err all",
+			expectedHTTPS: "bind :443 accept-proxy ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_https_bind_crt.list ca-ignore-err all crt-ignore-err all",
 		},
 		// 2
 		{
-			bind: hatypes.GlobalBindConfig{
-				HTTPBind:  "127.0.0.1:80",
-				HTTPSBind: "127.0.0.1:443",
-			},
+			httpBind:      "127.0.0.1:80",
+			httpsBind:     "127.0.0.1:443",
 			expectedHTTP:  "bind 127.0.0.1:80",
-			expectedHTTPS: "bind 127.0.0.1:443 ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_bind_crt.list ca-ignore-err all crt-ignore-err all",
+			expectedHTTPS: "bind 127.0.0.1:443 ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_https_bind_crt.list ca-ignore-err all crt-ignore-err all",
 		},
 	}
 	for _, test := range testCases {
 		c := setup(t)
-		var h *hatypes.Host
-		var b = c.config.Backends().AcquireBackend("d1", "app", "8080")
+		b := c.config.Backends().AcquireBackend("d1", "app", "8080")
 		b.Endpoints = []*hatypes.Endpoint{endpointS1}
-		h = c.config.Hosts().AcquireHost("d1.local")
-		h.AddPath(b, "/", hatypes.MatchBegin)
-
-		c.config.Global().Bind = test.bind
-		if test.expectedHTTP != "" {
-			test.expectedHTTP = "\n    " + test.expectedHTTP
-		}
-		if test.expectedHTTPS != "" {
-			test.expectedHTTPS = "\n    " + test.expectedHTTPS
-		}
+		f1 := c.httpFrontend(80)
+		f2 := c.httpsFrontend(443)
+		h1 := f1.AcquireHost("d1.local")
+		h1.AddPath(b, "/", hatypes.MatchBegin)
+		h2 := f2.AcquireHost("d1.local")
+		h2.AddPath(b, "/", hatypes.MatchBegin)
+
+		f1.Bind = test.httpBind
+		f1.AcceptProxy = test.acceptProxy
+		f2.Bind = test.httpsBind
+		f2.AcceptProxy = test.acceptProxy
+		test.expectedHTTP = "\n    " + test.expectedHTTP
+		test.expectedHTTPS = "\n    " + test.expectedHTTPS
 
 		c.Update()
 		c.checkConfig(`
@@ -1320,7 +1589,9 @@ func TestInstanceEmpty(t *testing.T) {
 	c := setup(t)
 	defer c.teardown()
 
-	c.config.Hosts().AcquireHost("empty").AddPath(c.config.Backends().AcquireBackend("default", "empty", "8080"), "/", hatypes.MatchBegin)
+	b := c.config.Backends().AcquireBackend("default", "empty", "8080")
+	c.httpFrontend(80).AcquireHost("empty").AddPath(b, "/", hatypes.MatchBegin)
+	c.httpsFrontend(443).AcquireHost("empty").AddPath(b, "/", hatypes.MatchBegin)
 	c.Update()
 
 	c.checkConfig(`
@@ -1358,9 +1629,7 @@ defaults
     timeout tunnel          1h
 backend default_empty_8080
     mode http
-backend _error404
-    mode http
-    http-request use-service lua.send-404
+<<backends-default>>
 <<frontends-default>>
 <<support>>
 `)
@@ -1378,7 +1647,8 @@ func TestDefaultBackendRedir(t *testing.T) {
 	defer c.teardown()
 
 	c.config.Global().DefaultBackendRedir = "https://example.tld"
-	c.config.Hosts().AcquireHost("empty").AddPath(c.config.Backends().AcquireBackend("default", "empty", "8080"), "/", hatypes.MatchBegin)
+	c.httpFrontend(80).AcquireHost("empty").AddPath(c.config.Backends().AcquireBackend("default", "empty", "8080"), "/", hatypes.MatchBegin)
+	_ = c.config.backends.AcquireNotFoundBackend()
 
 	c.Update()
 
@@ -1420,13 +1690,12 @@ backend default_empty_8080
 backend _error404
     mode http
     redirect location https://example.tld code 301
-<<frontends-default>>
+<<frontend-http>>
+    default_backend _error404
 <<support>>
 `)
 
 	c.checkMap("_front_http_host__begin.map", `
-empty#/ default_empty_8080`)
-	c.checkMap("_front_https_host__begin.map", `
 empty#/ default_empty_8080`)
 	c.logger.CompareLogging(defaultLogging)
 }
@@ -1443,7 +1712,7 @@ func TestInstanceEmptyExternal(t *testing.T) {
 	c.config.global.Security.Username = "external"
 	c.config.global.Security.Groupname = "external"
 
-	c.config.Hosts().AcquireHost("empty").AddPath(c.config.Backends().AcquireBackend("default", "empty", "8080"), "/", hatypes.MatchBegin)
+	c.httpFrontend(80).AcquireHost("empty").AddPath(c.config.Backends().AcquireBackend("default", "empty", "8080"), "/", hatypes.MatchBegin)
 	c.Update()
 
 	c.checkConfig(`
@@ -1467,10 +1736,9 @@ global
 <<defaults>>
 backend default_empty_8080
     mode http
-backend _error404
-    mode http
-    http-request use-service lua.send-404
-<<frontends-default>>
+<<backends-default>>
+<<frontend-http>>
+    default_backend _error404
 <<support>>
 `)
 	c.logger.CompareLogging(`
@@ -1486,10 +1754,10 @@ func TestPathIDsSplit(t *testing.T) {
 	b.Endpoints = []*hatypes.Endpoint{endpointS1}
 
 	max := 32
+	f := c.httpFrontend(80)
 	for i := 1; i <= max; i++ {
-		h := c.config.Hosts().AcquireHost(fmt.Sprintf("h%02d.local", i))
-		h.AddPath(b, "/", hatypes.MatchBegin)
-		path := b.FindBackendPath(h.FindPath("/")[0].Link)
+		h := f.AcquireHost(fmt.Sprintf("h%02d.local", i))
+		path := h.AddPath(b, "/", hatypes.MatchBegin)
 		path.SSLRedirect = true
 		path.AllowedIPHTTP.Rule = []string{"10.0.0.0/8"}
 		if i < max {
@@ -1542,7 +1810,7 @@ backend d1_app_8080
     # path30 = h30.local/
     # path31 = h31.local/
     # path32 = h32.local/
-    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_idpath__begin.map)
+    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_front_http_req__begin.map)
     http-request redirect scheme https if !https-request
     acl allow_rule_src0 src 10.0.0.0/8
     http-request deny if !allow_rule_src0
@@ -1560,7 +1828,8 @@ backend d1_app_8080
     http-response set-header Access-Control-Allow-Headers "DNT,X-CustomHeader,Keep-Alive,User-Agent" if { var(txn.pathID) -m str path31 }
     server s1 172.17.0.11:8080 weight 100
 <<backends-default>>
-<<frontends-default>>
+<<frontend-http>>
+    default_backend _error404
 <<support>>
 `)
 
@@ -1574,7 +1843,7 @@ func TestInstanceSecurity(t *testing.T) {
 	c.config.global.Security.Username = "haproxy"
 	c.config.global.Security.Groupname = "haproxy"
 
-	c.config.Hosts().AcquireHost("empty").AddPath(c.config.Backends().AcquireBackend("default", "empty", "8080"), "/", hatypes.MatchBegin)
+	c.httpFrontend(80).AcquireHost("empty").AddPath(c.config.Backends().AcquireBackend("default", "empty", "8080"), "/", hatypes.MatchBegin)
 	c.Update()
 
 	c.checkConfig(`
@@ -1599,10 +1868,9 @@ global
 <<defaults>>
 backend default_empty_8080
     mode http
-backend _error404
-    mode http
-    http-request use-service lua.send-404
-<<frontends-default>>
+<<backends-default>>
+<<frontend-http>>
+    default_backend _error404
 <<support>>
 `)
 	c.logger.CompareLogging(defaultLogging)
@@ -1614,9 +1882,15 @@ func TestInstanceMatch(t *testing.T) {
 
 	b := c.config.Backends().AcquireBackend("default", "d1", "8080")
 	b.Endpoints = []*hatypes.Endpoint{endpointS1}
-	h := c.config.Hosts().AcquireHost("d1.local")
-	h.AddPath(b, "/app", hatypes.MatchPrefix)
-	h.AddPath(b, "/api/v[0-9]+/", hatypes.MatchRegex)
+
+	h1 := c.httpFrontend(80).AcquireHost("d1.local")
+	h1.AddPath(b, "/app", hatypes.MatchPrefix)
+	h1.AddPath(b, "/api/v[0-9]+/", hatypes.MatchRegex)
+
+	h2 := c.httpsFrontend(443).AcquireHost("d1.local")
+	h2.AddPath(b, "/app", hatypes.MatchPrefix)
+	h2.AddPath(b, "/api/v[0-9]+/", hatypes.MatchRegex)
+
 	c.Update()
 
 	c.checkConfig(`
@@ -1637,7 +1911,7 @@ frontend _front_http
     default_backend _error404
 frontend _front_https
     mode http
-    bind :443 ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_bind_crt.list ca-ignore-err all crt-ignore-err all
+    bind :443 ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_https_bind_crt.list ca-ignore-err all crt-ignore-err all
     <<set-req-base>>
     http-request set-var(req.hostbackend) var(req.base),map_dir(/etc/haproxy/maps/_front_https_host__prefix.map)
     http-request set-var(req.hostbackend) var(req.base),map_reg(/etc/haproxy/maps/_front_https_host__regex.map) if !{ var(req.hostbackend) -m found }
@@ -1660,317 +1934,131 @@ d1.local#/app default_d1_8080`)
 }
 
 func TestInstanceFrontingProxy(t *testing.T) {
-	var (
-		frontUseProto = `
-    <<set-req-base>>
-    http-request set-var(txn.proto) hdr(X-Forwarded-Proto)
-    http-request set-header X-Forwarded-Proto http if !fronting-proxy
-    http-request del-header X-SSL-Client-CN if !fronting-proxy
-    http-request del-header X-SSL-Client-DN if !fronting-proxy
-    http-request del-header X-SSL-Client-SHA1 if !fronting-proxy
-    http-request del-header X-SSL-Client-SHA2 if !fronting-proxy
-    http-request del-header X-SSL-Client-Cert if !fronting-proxy
-    http-request set-var(req.backend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_host__begin.map)
-    use_backend %[var(req.backend)] if { var(req.backend) -m found }`
-		aclFrontExact = `
-    acl tls-has-crt ssl_c_used
-    acl tls-need-crt ssl_fc_sni -i -m str -f /etc/haproxy/maps/_front_tls_needcrt__exact.list
-    acl tls-host-need-crt var(req.host) -i -m str -f /etc/haproxy/maps/_front_tls_needcrt__exact.list
-    acl tls-has-invalid-crt ssl_c_verify gt 0
-    acl tls-check-crt ssl_fc_sni -i -m str -f /etc/haproxy/maps/_front_tls_auth__exact.list`
-		aclFrontRegex = `
-    acl tls-has-crt ssl_c_used
-    acl tls-need-crt ssl_fc_sni -i -m reg -f /etc/haproxy/maps/_front_tls_needcrt__regex.list
-    acl tls-host-need-crt var(req.host) -i -m reg -f /etc/haproxy/maps/_front_tls_needcrt__regex.list
-    acl tls-has-invalid-crt ssl_c_verify gt 0
-    acl tls-check-crt ssl_fc_sni -i -m reg -f /etc/haproxy/maps/_front_tls_auth__regex.list`
-		aclBackWithSockID = `
-    acl fronting-proxy so_id 11
-    acl https-request ssl_fc
-    acl https-request var(txn.proto) -m str https`
-		aclBackWithHdr = `
-    acl fronting-proxy var(txn.proto) -m found
-    acl https-request ssl_fc
-    acl https-request var(txn.proto) -m str https`
-		setHeaderWithACL = `
-    http-request set-header X-SSL-Client-CN   %{+Q}[ssl_c_s_dn(cn)]   if local-offload
-    http-request set-header X-SSL-Client-DN   %{+Q}[ssl_c_s_dn]       if local-offload
-    http-request set-header X-SSL-Client-SHA1 %{+Q}[ssl_c_sha1,hex]   if local-offload
-    http-response set-header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload" if https-request`
-		setHeaderSSLWithACL = `
-    http-request set-header X-SSL-Client-CN   %{+Q}[ssl_c_s_dn(cn)]   if local-offload
-    http-request set-header X-SSL-Client-DN   %{+Q}[ssl_c_s_dn]       if local-offload
-    http-request set-header X-SSL-Client-SHA1 %{+Q}[ssl_c_sha1,hex]   if local-offload
-    http-response set-header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload"`
-		setHeaderNoACL = `
-    http-request set-header X-SSL-Client-CN   %{+Q}[ssl_c_s_dn(cn)]
-    http-request set-header X-SSL-Client-DN   %{+Q}[ssl_c_s_dn]
-    http-request set-header X-SSL-Client-SHA1 %{+Q}[ssl_c_sha1,hex]
-    http-response set-header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload"`
-		setvarBegin = `
-    http-request set-var(req.hostbackend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_https_host__begin.map)`
-		setvarRegex = `
-    http-request set-var(req.hostbackend) var(req.base),map_reg(/etc/haproxy/maps/_front_https_host__regex.map)`
-	)
 	testCases := map[string]struct {
-		frontingBind      string
-		domain            string
-		useProto          bool
-		sslRedirect       bool
-		expectedACLBack   string
-		expectedSetHeader string
-		expectedFront     string
-		expectedMap       string
-		expectedRegexMap  string
-		expectedACLFront  string
-		expectedSetvar    string
+		f1UseFronting bool
+		f2UseFronting bool
+		f1UseProto    bool
+		f2UseProto    bool
+		expFront1     string
+		expFront2     string
+		expBack       string
 	}{
-		"test01": {
-			frontingBind:    ":8000",
-			domain:          "d1.local",
-			useProto:        true,
-			sslRedirect:     false,
-			expectedACLBack: aclBackWithSockID,
-			expectedSetHeader: `
-    http-request redirect scheme https if fronting-proxy !{ var(txn.proto) -m str https }` + setHeaderWithACL,
-			expectedFront: `
-    mode http
-    bind :80
-    bind :8000 id 11
-    acl fronting-proxy so_id 11` + frontUseProto,
-			expectedMap:      "d1.local#/ d1_app_8080",
-			expectedACLFront: aclFrontExact,
-			expectedSetvar:   setvarBegin,
-		},
-		"test02": {
-			frontingBind:    ":8000",
-			domain:          "*.d1.local",
-			useProto:        true,
-			sslRedirect:     false,
-			expectedACLBack: aclBackWithSockID,
-			expectedSetHeader: `
-    http-request redirect scheme https if fronting-proxy !{ var(txn.proto) -m str https }` + setHeaderWithACL,
-			expectedFront: `
-    mode http
-    bind :80
-    bind :8000 id 11
-    acl fronting-proxy so_id 11
+		"test01 fronting=none useproto=none": {
+			f1UseFronting: false, f2UseFronting: false, f1UseProto: false, f2UseProto: false,
+			expFront1: `
     <<set-req-base>>
-    http-request set-var(txn.proto) hdr(X-Forwarded-Proto)
-    http-request set-header X-Forwarded-Proto http if !fronting-proxy
-    http-request del-header X-SSL-Client-CN if !fronting-proxy
-    http-request del-header X-SSL-Client-DN if !fronting-proxy
-    http-request del-header X-SSL-Client-SHA1 if !fronting-proxy
-    http-request del-header X-SSL-Client-SHA2 if !fronting-proxy
-    http-request del-header X-SSL-Client-Cert if !fronting-proxy
-    http-request set-var(req.backend) var(req.base),map_reg(/etc/haproxy/maps/_front_http_host__regex.map)
-    use_backend %[var(req.backend)] if { var(req.backend) -m found }`,
-			expectedRegexMap: `^[^.]+\.d1\.local#/ d1_app_8080`,
-			expectedACLFront: aclFrontRegex,
-			expectedSetvar:   setvarRegex,
-		},
-		"test03": {
-			frontingBind:    ":80",
-			domain:          "d1.local",
-			useProto:        true,
-			sslRedirect:     false,
-			expectedACLBack: aclBackWithHdr,
-			expectedSetHeader: `
-    http-request redirect scheme https if fronting-proxy !{ var(txn.proto) -m str https }` + setHeaderWithACL,
-			expectedFront: `
-    mode http
-    bind :80
-    acl fronting-proxy var(txn.proto) -m found` + frontUseProto,
-			expectedMap:      "d1.local#/ d1_app_8080",
-			expectedACLFront: aclFrontExact,
-			expectedSetvar:   setvarBegin,
-		},
-		"test04": {
-			frontingBind:      ":8000",
-			domain:            "d1.local",
-			useProto:          false,
-			sslRedirect:       false,
-			expectedACLBack:   ``,
-			expectedSetHeader: setHeaderSSLWithACL,
-			expectedFront: `
-    mode http
-    bind :80
-    bind :8000 id 11
+    <<http-headers>>`,
+			expFront2: `
     <<set-req-base>>
-    http-request set-var(req.backend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_host__begin.map)
-    use_backend %[var(req.backend)] if { var(req.backend) -m found }`,
-			expectedMap:      "d1.local#/ d1_app_8080",
-			expectedACLFront: aclFrontExact,
-			expectedSetvar:   setvarBegin,
-		},
-		"test05": {
-			frontingBind:      ":8000",
-			domain:            "*.d1.local",
-			useProto:          false,
-			sslRedirect:       false,
-			expectedACLBack:   ``,
-			expectedSetHeader: setHeaderSSLWithACL,
-			expectedFront: `
-    mode http
-    bind :80
-    bind :8000 id 11
+    <<http-headers>>`,
+			expBack: ``,
+		},
+		"test02 fronting=some useproto=none": {
+			f1UseFronting: true, f2UseFronting: false, f1UseProto: false, f2UseProto: false,
+			expFront1: `
+    http-request set-var(req.fronting_proxy) str(1)
+    <<set-req-base>>`,
+			expFront2: `
     <<set-req-base>>
-    http-request set-var(req.backend) var(req.base),map_reg(/etc/haproxy/maps/_front_http_host__regex.map)
-    use_backend %[var(req.backend)] if { var(req.backend) -m found }`,
-			expectedRegexMap: `^[^.]+\.d1\.local#/ d1_app_8080`,
-			expectedACLFront: aclFrontRegex,
-			expectedSetvar:   setvarRegex,
-		},
-		"test06": {
-			frontingBind:      ":80",
-			domain:            "d1.local",
-			useProto:          false,
-			sslRedirect:       false,
-			expectedACLBack:   ``,
-			expectedSetHeader: setHeaderSSLWithACL,
-			expectedFront: `
-    mode http
-    bind :80
-    <<set-req-base>>
-    http-request set-var(req.backend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_host__begin.map)
-    use_backend %[var(req.backend)] if { var(req.backend) -m found }`,
-			expectedMap:      "d1.local#/ d1_app_8080",
-			expectedACLFront: aclFrontExact,
-			expectedSetvar:   setvarBegin,
-		},
-		"test07": {
-			frontingBind:    ":8000",
-			domain:          "d1.local",
-			useProto:        true,
-			sslRedirect:     true,
-			expectedACLBack: aclBackWithSockID,
-			expectedSetHeader: `
-    http-request redirect scheme https if fronting-proxy !{ var(txn.proto) -m str https }
-    http-request redirect scheme https if !fronting-proxy !https-request` + setHeaderNoACL,
-			expectedFront: `
-    mode http
-    bind :80
-    bind :8000 id 11
-    acl fronting-proxy so_id 11` + frontUseProto,
-			expectedMap:      "d1.local#/ d1_app_8080",
-			expectedACLFront: aclFrontExact,
-			expectedSetvar:   setvarBegin,
-		},
-		"test08": {
-			frontingBind:    ":80",
-			domain:          "d1.local",
-			useProto:        true,
-			sslRedirect:     true,
-			expectedACLBack: aclBackWithHdr,
-			expectedSetHeader: `
-    http-request redirect scheme https if fronting-proxy !{ var(txn.proto) -m str https }
-    http-request redirect scheme https if !fronting-proxy !https-request` + setHeaderNoACL,
-			expectedFront: `
-    mode http
-    bind :80
-    acl fronting-proxy var(txn.proto) -m found` + frontUseProto,
-			expectedMap:      "d1.local#/ d1_app_8080",
-			expectedACLFront: aclFrontExact,
-			expectedSetvar:   setvarBegin,
-		},
-		"test09": {
-			frontingBind:      ":8000",
-			domain:            "d1.local",
-			useProto:          false,
-			sslRedirect:       true,
-			expectedACLBack:   ``,
-			expectedSetHeader: setHeaderNoACL,
-			expectedFront: `
-    mode http
-    bind :80
-    bind :8000 id 11
+    <<http-headers>>`,
+			expBack: `
+    acl fronting-proxy var(req.fronting_proxy) -m found`,
+		},
+		"test03 fronting=some useproto=some": {
+			f1UseFronting: true, f2UseFronting: false, f1UseProto: true, f2UseProto: false,
+			expFront1: `
+    http-request set-var(req.fronting_proxy) str(1)
+    http-request set-var(req.fronting_proto) str(use)
+    <<set-req-base>>`,
+			expFront2: `
+    http-request set-var(req.fronting_proto) str(ignore)
     <<set-req-base>>
-    http-request set-var(req.backend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_host__begin.map)
-    use_backend %[var(req.backend)] if { var(req.backend) -m found }`,
-			expectedMap:      "d1.local#/ d1_app_8080",
-			expectedACLFront: aclFrontExact,
-			expectedSetvar:   setvarBegin,
-		},
-		"test10": {
-			frontingBind:      ":80",
-			domain:            "d1.local",
-			useProto:          false,
-			sslRedirect:       true,
-			expectedACLBack:   ``,
-			expectedSetHeader: setHeaderNoACL,
-			expectedFront: `
-    mode http
-    bind :80
-    <<set-req-base>>
-    http-request set-var(req.backend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_host__begin.map)
-    use_backend %[var(req.backend)] if { var(req.backend) -m found }`,
-			expectedMap:      "d1.local#/ d1_app_8080",
-			expectedACLFront: aclFrontExact,
-			expectedSetvar:   setvarBegin,
+    <<http-headers>>`,
+			expBack: `
+    acl fronting-proxy var(req.fronting_proxy) -m found
+    acl https-request var(req.fronting_proto) -m str ignore
+    acl https-request hdr(X-Forwarded-Proto) https
+    http-request redirect scheme https if fronting-proxy !https-request`,
+		},
+		"test04 fronting=all useproto=none": {
+			f1UseFronting: true, f2UseFronting: true, f1UseProto: false, f2UseProto: false,
+			expFront1: `
+    <<set-req-base>>`,
+			expFront2: `
+    <<set-req-base>>`,
+			expBack: ``,
+		},
+		"test05 fronting=all useproto=some": {
+			f1UseFronting: true, f2UseFronting: true, f1UseProto: true, f2UseProto: false,
+			expFront1: `
+    http-request set-var(req.fronting_proto) str(use)
+    <<set-req-base>>`,
+			expFront2: `
+    http-request set-var(req.fronting_proto) str(ignore)
+    <<set-req-base>>`,
+			expBack: `
+    acl https-request var(req.fronting_proto) -m str ignore
+    acl https-request hdr(X-Forwarded-Proto) https
+    http-request redirect scheme https if !https-request`,
+		},
+		"test06 fronting=all useproto=all": {
+			f1UseFronting: true, f2UseFronting: true, f1UseProto: true, f2UseProto: true,
+			expFront1: `
+    <<set-req-base>>`,
+			expFront2: `
+    <<set-req-base>>`,
+			expBack: `
+    acl https-request hdr(X-Forwarded-Proto) https
+    http-request redirect scheme https if !https-request`,
 		},
 	}
 	for name, test := range testCases {
 		t.Run(name, func(t *testing.T) {
 			c := setup(t)
+			defer c.teardown()
 
-			var h *hatypes.Host
-			var b = c.config.Backends().AcquireBackend("d1", "app", "8080")
-			h = c.config.Hosts().AcquireHost(test.domain)
-			h.AddPath(b, "/", hatypes.MatchBegin)
+			b := c.config.Backends().AcquireBackend("d1", "app", "8080")
 			b.Endpoints = []*hatypes.Endpoint{endpointS1}
-			b.FindBackendPath(h.FindPath("/")[0].Link).SSLRedirect = test.sslRedirect
-			b.FindBackendPath(h.FindPath("/")[0].Link).HSTS = hatypes.HSTS{
-				Enabled:    true,
-				MaxAge:     15768000,
-				Subdomains: true,
-				Preload:    true,
-			}
-			h.TLS.CAHash = "1"
-			h.TLS.CAFilename = "/var/haproxy/ssl/ca.pem"
-			c.config.Global().Bind.FrontingBind = test.frontingBind
-			c.config.Global().Bind.FrontingSockID = 11
-			c.config.Global().Bind.FrontingUseProto = test.useProto
+
+			f1 := c.httpFrontend(8081)
+			f1.Name = "_front_http_8081"
+			f1.Bind = ":8081"
+			f1.IsFrontingProxy = test.f1UseFronting
+			f1.IsFrontingUseProto = test.f1UseProto
+			h1 := f1.AcquireHost("d1.local")
+			h1.AddPath(b, "/", hatypes.MatchBegin)
+
+			f2 := c.httpFrontend(8082)
+			f2.Name = "_front_http_8082"
+			f2.Bind = ":8082"
+			f2.IsFrontingProxy = test.f2UseFronting
+			f2.IsFrontingUseProto = test.f2UseProto
+			h2 := f2.AcquireHost("d2.local")
+			h2.AddPath(b, "/", hatypes.MatchBegin)
 
 			c.Update()
 			c.checkConfig(`
 <<global>>
 <<defaults>>
 backend d1_app_8080
-    mode http` + test.expectedACLBack + `
-    acl local-offload ssl_fc` + test.expectedSetHeader + `
+    mode http` + test.expBack + `
     server s1 172.17.0.11:8080 weight 100
 <<backends-default>>
-frontend _front_http` + test.expectedFront + `
+frontend _front_http_8081
+    mode http
+    bind :8081` + test.expFront1 + `
+    http-request set-var(req.backend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_8081_host__begin.map)
+    use_backend %[var(req.backend)] if { var(req.backend) -m found }
     default_backend _error404
-frontend _front_https
+frontend _front_http_8082
     mode http
-    bind :443 ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_bind_crt.list ca-ignore-err all crt-ignore-err all
-    <<set-req-base>>` + test.expectedSetvar + `
-    http-request set-header X-Forwarded-Proto https
-    http-request del-header X-SSL-Client-CN
-    http-request del-header X-SSL-Client-DN
-    http-request del-header X-SSL-Client-SHA1
-    http-request del-header X-SSL-Client-SHA2
-    http-request del-header X-SSL-Client-Cert` + test.expectedACLFront + `
-    http-request set-var(req.tls_nocrt_redir) str(_internal) if !tls-has-crt tls-need-crt
-    http-request set-var(req.tls_invalidcrt_redir) str(_internal) if tls-has-invalid-crt tls-check-crt
-    http-request use-service lua.send-421 if tls-has-crt { ssl_fc_has_sni } !{ ssl_fc_sni,strcmp(req.host) eq 0 }
-    http-request use-service lua.send-496 if { var(req.tls_nocrt_redir) -m str _internal }
-    http-request use-service lua.send-421 if !tls-has-crt tls-host-need-crt
-    http-request use-service lua.send-495 if { var(req.tls_invalidcrt_redir) -m str _internal }
-    use_backend %[var(req.hostbackend)] if { var(req.hostbackend) -m found }
-    use_backend %[var(req.snibackend)] if { var(req.snibackend) -m found }
+    bind :8082` + test.expFront2 + `
+    http-request set-var(req.backend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_8082_host__begin.map)
+    use_backend %[var(req.backend)] if { var(req.backend) -m found }
     default_backend _error404
 <<support>>
 `)
-			if test.expectedMap != "" {
-				c.checkMap("_front_http_host__begin.map", test.expectedMap)
-			}
-			if test.expectedRegexMap != "" {
-				c.checkMap("_front_http_host__regex.map", test.expectedRegexMap)
-			}
 			c.logger.CompareLogging(defaultLogging)
-			c.teardown()
 		})
 	}
 }
@@ -1983,7 +2071,7 @@ func TestInstanceTCPServices(t *testing.T) {
 	var b = c.config.Backends().AcquireBackend("d1", "app", "8080")
 	b.ModeTCP = true
 	b.Endpoints = []*hatypes.Endpoint{endpointS1}
-	h = c.config.Hosts().AcquireHost("d1.local")
+	h = c.httpFrontend(80).AcquireHost("d1.local")
 	h.AddPath(b, "/", hatypes.MatchBegin)
 
 	b2 := c.config.Backends().AcquireBackend("d2", "app", "8080")
@@ -2242,7 +2330,8 @@ frontend _front_tcp_7015
     bind :7015 ssl crt-list /etc/haproxy/crtlist_tcp_7015.list ca-ignore-err all crt-ignore-err all
     mode tcp
     default_backend d1_app_8080
-<<frontends-default>>
+<<frontend-http>>
+    default_backend _error404
 <<support>>
 `)
 	c.checkMap("_tcp_sni_7011__exact.map", `
@@ -2342,7 +2431,7 @@ listen _tcp_pq_5432
 				b.ProxyProt.Decode = true
 				b.ProxyProt.EncodeVersion = "v1"
 				b.CheckInterval = "2s"
-				c.config.Global().Bind.TCPBindIP = "127.0.0.1"
+				c.config.Global().TCPBindIP = "127.0.0.1"
 			},
 			expected: `
 listen _tcp_pq_5432
@@ -2389,13 +2478,7 @@ listen _tcp_pq_5432
 		c.checkConfig(`
 <<global>>
 <<defaults>>` + test.expected + `
-backend _error404
-    mode http
-    http-request use-service lua.send-404
-<<frontend-http-clean>>
-    default_backend _error404
-<<frontend-https-clean>>
-    default_backend _error404
+<<backends-default>>
 <<support>>
 `)
 		logging := test.logging
@@ -2415,35 +2498,57 @@ func TestInstanceDefaultHost(t *testing.T) {
 	def.Endpoints = []*hatypes.Endpoint{endpointS0}
 	c.config.Backends().DefaultBackend = def
 
-	var h *hatypes.Host
+	fhttp := c.httpFrontend(80)
+	fhttps := c.httpsFrontend(443)
+
+	var hhttp, hhttps *hatypes.Host
 	var b *hatypes.Backend
-	hdef := c.config.Hosts().AcquireHost(hatypes.DefaultHost)
+	hdefhttp := fhttp.AcquireHost(hatypes.DefaultHost)
+	hdefhttps := fhttps.AcquireHost(hatypes.DefaultHost)
+
+	//
 
 	b = c.config.Backends().AcquireBackend("d1", "app", "8080")
 	b.Endpoints = []*hatypes.Endpoint{endpointS1}
-	h = c.config.Hosts().AcquireHost("d1.local")
-	h.TLS.TLSFilename = "/var/haproxy/ssl/certs/default.pem"
-	h.TLS.TLSHash = "0"
-	h.VarNamespace = true
-	hdef.AddPath(b, "/", hatypes.MatchBegin)
-	hdef.AddPath(b, "/app1", hatypes.MatchExact)
-	hdef.AddPath(b, "/app2", hatypes.MatchPrefix)
-	b.FindBackendPath(hdef.FindPath("/")[0].Link).SSLRedirect = true
-	b.FindBackendPath(hdef.FindPath("/app1")[0].Link).RewriteURL = "/"
-	b.FindBackendPath(hdef.FindPath("/app2")[0].Link).MaxBodySize = 32768
+
+	hhttp = fhttp.AcquireHost("d1.local")
+	hhttp.VarNamespace = true
+	p1 := hdefhttp.AddPath(b, "/", hatypes.MatchBegin)
+	p2 := hdefhttp.AddPath(b, "/app1", hatypes.MatchExact)
+	p3 := hdefhttp.AddPath(b, "/app2", hatypes.MatchPrefix)
+	p1.SSLRedirect = true
+	p2.RewriteURL = "/"
+	p3.MaxBodySize = 32768
+
+	hhttps = fhttps.AcquireHost("d1.local")
+	hhttps.TLS.TLSFilename = "/var/haproxy/ssl/certs/default.pem"
+	hhttps.TLS.TLSHash = "0"
+	hhttps.VarNamespace = true
+	hdefhttps.AddPath(b, "/", hatypes.MatchBegin).SSLRedirect = true
+	hdefhttps.AddPath(b, "/app1", hatypes.MatchExact).RewriteURL = "/"
+	hdefhttps.AddPath(b, "/app2", hatypes.MatchPrefix).MaxBodySize = 32768
+
+	//
 
 	b = c.config.Backends().AcquireBackend("d2", "app", "8080")
 	b.Endpoints = []*hatypes.Endpoint{endpointS1}
-	h = c.config.Hosts().AcquireHost("d2.local")
-	h.TLS.TLSFilename = "/var/haproxy/ssl/certs/default.pem"
-	h.TLS.TLSHash = "0"
-	h.VarNamespace = true
-	h.AddPath(b, "/app11", hatypes.MatchBegin)
-	hdef.AddPath(b, "/app12", hatypes.MatchExact)
-	hdef.AddPath(b, "/app13", hatypes.MatchPrefix)
-	b.FindBackendPath(h.FindPath("/app11")[0].Link).SSLRedirect = true
-	b.FindBackendPath(hdef.FindPath("/app12")[0].Link).RewriteURL = "/"
-	b.FindBackendPath(hdef.FindPath("/app13")[0].Link).MaxBodySize = 65536
+
+	hhttp = fhttp.AcquireHost("d2.local")
+	hhttp.VarNamespace = true
+	p1 = hhttp.AddPath(b, "/app11", hatypes.MatchBegin)
+	p2 = hdefhttp.AddPath(b, "/app12", hatypes.MatchExact)
+	p3 = hdefhttp.AddPath(b, "/app13", hatypes.MatchPrefix)
+	p1.SSLRedirect = true
+	p2.RewriteURL = "/"
+	p3.MaxBodySize = 65536
+
+	hhttps = fhttps.AcquireHost("d2.local")
+	hhttps.TLS.TLSFilename = "/var/haproxy/ssl/certs/default.pem"
+	hhttps.TLS.TLSHash = "0"
+	hhttps.VarNamespace = true
+	hhttps.AddPath(b, "/app11", hatypes.MatchBegin).SSLRedirect = true
+	hdefhttps.AddPath(b, "/app12", hatypes.MatchExact).RewriteURL = "/"
+	hdefhttps.AddPath(b, "/app13", hatypes.MatchPrefix).MaxBodySize = 65536
 
 	c.Update()
 	c.checkConfig(`
@@ -2455,9 +2560,9 @@ backend d1_app_8080
     # path01 = <default>/
     # path02 = <default>/app1
     # path03 = <default>/app2
-    http-request set-var(txn.pathID) str(<default>\#),concat(,req.path),map_str(/etc/haproxy/maps/_back_d1_app_8080_idpathdef__exact.map)
-    http-request set-var(txn.pathID) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_back_d1_app_8080_idpathdef__prefix_02.map) if !{ var(txn.pathID) -m found }
-    http-request set-var(txn.pathID) str(<default>\#),concat(,req.path),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_idpathdef__begin.map) if !{ var(txn.pathID) -m found }
+    http-request set-var(txn.pathID) str(<default>\#),concat(,req.path),map_str(/etc/haproxy/maps/_back_d1_app_8080_front_http_def__exact.map)
+    http-request set-var(txn.pathID) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_back_d1_app_8080_front_http_def__prefix_02.map) if !{ var(txn.pathID) -m found }
+    http-request set-var(txn.pathID) str(<default>\#),concat(,req.path),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_front_http_def__begin.map) if !{ var(txn.pathID) -m found }
     http-request redirect scheme https if !https-request { var(txn.pathID) -m str path01 }
     http-request use-service lua.send-413 if { var(txn.pathID) -m str path03 } { req.body_size,sub(32768) gt 0 }
     http-request replace-path ^/app1/?(.*)$     /\1     if { var(txn.pathID) -m str path02 }
@@ -2468,9 +2573,9 @@ backend d2_app_8080
     # path02 = <default>/app12
     # path03 = <default>/app13
     # path01 = d2.local/app11
-    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d2_app_8080_idpath__begin.map)
-    http-request set-var(txn.pathID) str(<default>\#),concat(,req.path),map_str(/etc/haproxy/maps/_back_d2_app_8080_idpathdef__exact.map) if !{ var(txn.pathID) -m found }
-    http-request set-var(txn.pathID) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_back_d2_app_8080_idpathdef__prefix.map) if !{ var(txn.pathID) -m found }
+    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d2_app_8080_front_http_req__begin.map)
+    http-request set-var(txn.pathID) str(<default>\#),concat(,req.path),map_str(/etc/haproxy/maps/_back_d2_app_8080_front_http_def__exact.map) if !{ var(txn.pathID) -m found }
+    http-request set-var(txn.pathID) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_back_d2_app_8080_front_http_def__prefix.map) if !{ var(txn.pathID) -m found }
     http-request redirect scheme https if !https-request { var(txn.pathID) -m str path01 }
     http-request use-service lua.send-413 if { var(txn.pathID) -m str path03 } { req.body_size,sub(65536) gt 0 }
     http-request replace-path ^/app12/?(.*)$     /\1     if { var(txn.pathID) -m str path02 }
@@ -2478,29 +2583,30 @@ backend d2_app_8080
 backend default_default-backend_8080
     mode http
     server s0 172.17.0.99:8080 weight 100
+<<backends-default>>
 frontend _front_http
     mode http
     bind :80
     <<set-req-base>>
-    http-request set-var(txn.namespace) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_namespace__begin.map)
+    http-request set-var(txn.namespace) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_namespace__begin.map)
     http-request set-var(txn.namespace) str(-) if !{ var(txn.namespace) -m found }
     <<http-headers>>
     http-request set-var(req.backend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_host__begin.map)
-    http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path),map_str(/etc/haproxy/maps/_front_defaulthost__exact.map) if !{ var(req.backend) -m found }
-    http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_front_defaulthost__prefix_02.map) if !{ var(req.backend) -m found } !{ var(req.defaultbackend) -m found }
-    http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path),lower,map_beg(/etc/haproxy/maps/_front_defaulthost__begin.map) if !{ var(req.backend) -m found } !{ var(req.defaultbackend) -m found }
+    http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path),map_str(/etc/haproxy/maps/_front_http_defaulthost__exact.map) if !{ var(req.backend) -m found }
+    http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_front_http_defaulthost__prefix_02.map) if !{ var(req.backend) -m found } !{ var(req.defaultbackend) -m found }
+    http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path),lower,map_beg(/etc/haproxy/maps/_front_http_defaulthost__begin.map) if !{ var(req.backend) -m found } !{ var(req.defaultbackend) -m found }
     use_backend %[var(req.backend)] if { var(req.backend) -m found }
     use_backend %[var(req.defaultbackend)]
     default_backend default_default-backend_8080
 frontend _front_https
     mode http
-    bind :443 ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_bind_crt.list ca-ignore-err all crt-ignore-err all
+    bind :443 ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_https_bind_crt.list ca-ignore-err all crt-ignore-err all
     <<set-req-base>>
     http-request set-var(req.hostbackend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_https_host__begin.map)
-    http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path),map_str(/etc/haproxy/maps/_front_defaulthost__exact.map) if !{ var(req.hostbackend) -m found }
-    http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_front_defaulthost__prefix_02.map) if !{ var(req.hostbackend) -m found } !{ var(req.defaultbackend) -m found }
-    http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path),lower,map_beg(/etc/haproxy/maps/_front_defaulthost__begin.map) if !{ var(req.hostbackend) -m found } !{ var(req.defaultbackend) -m found }
-    http-request set-var(txn.namespace) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_namespace__begin.map)
+    http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path),map_str(/etc/haproxy/maps/_front_https_defaulthost__exact.map) if !{ var(req.hostbackend) -m found }
+    http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_front_https_defaulthost__prefix_02.map) if !{ var(req.hostbackend) -m found } !{ var(req.defaultbackend) -m found }
+    http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path),lower,map_beg(/etc/haproxy/maps/_front_https_defaulthost__begin.map) if !{ var(req.hostbackend) -m found } !{ var(req.defaultbackend) -m found }
+    http-request set-var(txn.namespace) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_https_namespace__begin.map)
     http-request set-var(txn.namespace) str(-) if !{ var(txn.namespace) -m found }
     <<https-headers>>
     use_backend %[var(req.hostbackend)] if { var(req.hostbackend) -m found }
@@ -2509,84 +2615,271 @@ frontend _front_https
 <<support>>
 `)
 
+	c.checkMap("_front_https_bind_crt.list", `
+/var/haproxy/ssl/certs/default.pem !*
+`)
+
+	c.checkMap("_back_d1_app_8080_front_http_def__exact.map", `
+<default>#/app1 path02`)
+	c.checkMap("_back_d1_app_8080_front_http_def__prefix_02.map", `
+<default>#/app2 path03`)
+	c.checkMap("_back_d1_app_8080_front_http_def__begin.map", `
+<default>#/ path01`)
+	c.checkMap("_back_d2_app_8080_front_http_def__exact.map", `
+<default>#/app12 path02`)
+	c.checkMap("_back_d2_app_8080_front_http_def__prefix.map", `
+<default>#/app13 path03`)
+	c.checkMap("_back_d2_app_8080_front_http_req__begin.map", `
+d2.local#/app11 path01`)
+
 	c.checkMap("_front_http_host__begin.map", `
 d2.local#/app11 d2_app_8080
 `)
-	c.checkMap("_front_bind_crt.list", `
-/var/haproxy/ssl/certs/default.pem !*
+	c.checkMap("_front_http_namespace__begin.map", `
+d2.local#/app11 d2
 `)
-	c.checkMap("_front_namespace__begin.map", `
+	c.checkMap("_front_http_defaulthost__prefix_02.map", `
+<default>#/app2 d1_app_8080
+<default>#/app13 d2_app_8080`)
+	c.checkMap("_front_http_defaulthost__exact.map", `
+<default>#/app1 d1_app_8080
+<default>#/app12 d2_app_8080`)
+	c.checkMap("_front_http_defaulthost__begin.map", `
+<default>#/ d1_app_8080`)
+
+	c.checkMap("_front_https_namespace__begin.map", `
 d2.local#/app11 d2
 `)
 	c.checkMap("_front_https_host__begin.map", `
 d2.local#/app11 d2_app_8080
 `)
-	c.checkMap("_front_defaulthost__prefix_02.map", `
+	c.checkMap("_front_https_defaulthost__prefix_02.map", `
 <default>#/app2 d1_app_8080
 <default>#/app13 d2_app_8080`)
-	c.checkMap("_front_defaulthost__exact.map", `
+	c.checkMap("_front_https_defaulthost__exact.map", `
 <default>#/app1 d1_app_8080
 <default>#/app12 d2_app_8080`)
-	c.checkMap("_front_defaulthost__begin.map", `
+	c.checkMap("_front_https_defaulthost__begin.map", `
 <default>#/ d1_app_8080`)
-	c.checkMap("_back_d1_app_8080_idpathdef__exact.map", `
-<default>#/app1 path02`)
-	c.checkMap("_back_d1_app_8080_idpathdef__prefix_02.map", `
-<default>#/app2 path03`)
-	c.checkMap("_back_d1_app_8080_idpathdef__begin.map", `
-<default>#/ path01`)
-	c.checkMap("_back_d2_app_8080_idpathdef__exact.map", `
-<default>#/app12 path02`)
-	c.checkMap("_back_d2_app_8080_idpathdef__prefix.map", `
-<default>#/app13 path03`)
-	c.checkMap("_back_d2_app_8080_idpath__begin.map", `
-d2.local#/app11 path01`)
 
 	c.logger.CompareLogging(defaultLogging)
 }
 
-func TestInstanceUseDefaultCrt(t *testing.T) {
-	c := setup(t)
-	defer c.teardown()
-
-	var h *hatypes.Host
-	var b *hatypes.Backend
+func TestInstanceDefaultHostMultiFrontend(t *testing.T) {
+	// boilerplate to path ID comments
+	pathsNoreqNoacl := `
+    # path01 = <default>/app1
+    # path02 = <default>/app2`
+	pathsNoreqHasacl := `
+    # path01 = fe:_front_http_8001 -- host/path:<default>/app1
+    # path02 = fe:_front_http_8001 -- host/path:<default>/app2
+    # path03 = fe:_front_http_8002 -- host/path:<default>/app1
+    # path04 = fe:_front_http_8002 -- host/path:<default>/app2`
+	pathsHasreqNoacl := `
+    # path01 = <default>/app1
+    # path02 = <default>/app2
+    # path03 = d1.local/app1
+    # path04 = d1.local/app2`
+	pathsHasreqHasacl := `
+    # path01 = fe:_front_http_8001 -- host/path:<default>/app1
+    # path02 = fe:_front_http_8001 -- host/path:<default>/app2
+    # path03 = fe:_front_http_8001 -- host/path:d1.local/app1
+    # path04 = fe:_front_http_8001 -- host/path:d1.local/app2
+    # path05 = fe:_front_http_8002 -- host/path:<default>/app1
+    # path06 = fe:_front_http_8002 -- host/path:<default>/app2
+    # path07 = fe:_front_http_8002 -- host/path:d1.local/app1
+    # path08 = fe:_front_http_8002 -- host/path:d1.local/app2`
+
+	// boilerplate to backend request actions
+	has2paths := `
+    http-request use-service lua.send-413 if { var(txn.pathID) -m str path01 } { req.body_size,sub(1048576) gt 0 }
+    http-request use-service lua.send-413 if { var(txn.pathID) -m str path02 } { req.body_size,sub(2097152) gt 0 }`
+	has4pathsv1 := has2paths + `
+    http-request use-service lua.send-413 if { var(txn.pathID) -m str path03 } { req.body_size,sub(3145728) gt 0 }
+    http-request use-service lua.send-413 if { var(txn.pathID) -m str path04 } { req.body_size,sub(4194304) gt 0 }`
+	has4pathsv2 := has2paths + `
+    http-request use-service lua.send-413 if { var(txn.pathID) -m str path03 } { req.body_size,sub(5242880) gt 0 }
+    http-request use-service lua.send-413 if { var(txn.pathID) -m str path04 } { req.body_size,sub(6291456) gt 0 }`
+	has8paths := has4pathsv1 + `
+    http-request use-service lua.send-413 if { var(txn.pathID) -m str path05 } { req.body_size,sub(5242880) gt 0 }
+    http-request use-service lua.send-413 if { var(txn.pathID) -m str path06 } { req.body_size,sub(6291456) gt 0 }
+    http-request use-service lua.send-413 if { var(txn.pathID) -m str path07 } { req.body_size,sub(7340032) gt 0 }
+    http-request use-service lua.send-413 if { var(txn.pathID) -m str path08 } { req.body_size,sub(8388608) gt 0 }`
+
+	// frontend related checks
+	frontNofilterNoreq := `
+    http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_front_http_8001_defaulthost__prefix.map) if !{ var(req.backend) -m found }`
+	frontNofilterHasreq := `
+    http-request set-var(req.backend) var(req.base),map_dir(/etc/haproxy/maps/_front_http_8001_host__prefix.map)
+    http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_front_http_8001_defaulthost__prefix.map) if !{ var(req.backend) -m found }`
+	frontHasfilterNoreq := `
+    http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_front_http_8001_defaulthost__prefix_01.map) if !{ var(req.backend) -m found } { hdr(x-user) -- 'user1' }
+    http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_front_http_8001_defaulthost__prefix_02.map) if !{ var(req.backend) -m found } !{ var(req.defaultbackend) -m found } { hdr(x-user) -- 'user2' }`
+	frontHasfilterHasreq := `
+    http-request set-var(req.backend) var(req.base),map_dir(/etc/haproxy/maps/_front_http_8001_host__prefix_01.map) if { hdr(x-user) -- 'user1' }
+    http-request set-var(req.backend) var(req.base),map_dir(/etc/haproxy/maps/_front_http_8001_host__prefix_02.map) if !{ var(req.backend) -m found } { hdr(x-user) -- 'user2' }
+    http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_front_http_8001_defaulthost__prefix_01.map) if !{ var(req.backend) -m found } { hdr(x-user) -- 'user1' }
+    http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_front_http_8001_defaulthost__prefix_02.map) if !{ var(req.backend) -m found } !{ var(req.defaultbackend) -m found } { hdr(x-user) -- 'user2' }`
+
+	// backend related checks -- also binding everything together
+	testCases := map[string]struct {
+		hasHTTPFilter bool // ACLs matching HTTP headers
+		hasReqHost    bool // actions to configured (non default) hostnames, which adds additional ACLs
+		hasFrontACL   bool // two distinct frontends selecting a single backend, adding frontend check ACLs in the backend side
+		expFront      string
+		expBack       string
+	}{
+		"test01": {
+			hasHTTPFilter: false, hasReqHost: false, hasFrontACL: false,
+			expFront: frontNofilterNoreq,
+			expBack: pathsNoreqNoacl + `
+    http-request set-var(txn.pathID) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_back_default_app_8080_front_http_8001_def__prefix.map)` +
+				has2paths,
+		},
+		"test02": {
+			hasHTTPFilter: false, hasReqHost: false, hasFrontACL: true,
+			expFront: frontNofilterNoreq,
+			expBack: pathsNoreqHasacl + `
+    http-request set-var-fmt(req.fe) "%f"
+    http-request set-var(txn.pathID) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_back_default_app_8080_front_http_8001_def__prefix.map) if { var(req.fe) -m str _front_http_8001 }
+    http-request set-var(txn.pathID) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_back_default_app_8080_front_http_8002_def__prefix.map) if { var(req.fe) -m str _front_http_8002 }` +
+				has4pathsv2,
+		},
+		"test03": {
+			hasHTTPFilter: false, hasReqHost: true, hasFrontACL: false,
+			expFront: frontNofilterHasreq,
+			expBack: pathsHasreqNoacl + `
+    http-request set-var(txn.pathID) var(req.base),map_dir(/etc/haproxy/maps/_back_default_app_8080_front_http_8001_req__prefix.map)
+    http-request set-var(txn.pathID) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_back_default_app_8080_front_http_8001_def__prefix.map) if !{ var(txn.pathID) -m found }` +
+				has4pathsv1,
+		},
+		"test04": {
+			hasHTTPFilter: false, hasReqHost: true, hasFrontACL: true,
+			expFront: frontNofilterHasreq,
+			expBack: pathsHasreqHasacl + `
+    http-request set-var-fmt(req.fe) "%f"
+    http-request set-var(txn.pathID) var(req.base),map_dir(/etc/haproxy/maps/_back_default_app_8080_front_http_8001_req__prefix.map) if { var(req.fe) -m str _front_http_8001 }
+    http-request set-var(txn.pathID) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_back_default_app_8080_front_http_8001_def__prefix.map) if { var(req.fe) -m str _front_http_8001 } !{ var(txn.pathID) -m found }
+    http-request set-var(txn.pathID) var(req.base),map_dir(/etc/haproxy/maps/_back_default_app_8080_front_http_8002_req__prefix.map) if { var(req.fe) -m str _front_http_8002 }
+    http-request set-var(txn.pathID) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_back_default_app_8080_front_http_8002_def__prefix.map) if { var(req.fe) -m str _front_http_8002 } !{ var(txn.pathID) -m found }` +
+				has8paths,
+		},
+		"test05": {
+			hasHTTPFilter: true, hasReqHost: false, hasFrontACL: false,
+			expFront: frontHasfilterNoreq,
+			expBack: pathsNoreqNoacl + `
+    http-request set-var(txn.pathID) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_back_default_app_8080_front_http_8001_def__prefix_01.map) if { hdr(x-user) -- 'user2' }
+    http-request set-var(txn.pathID) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_back_default_app_8080_front_http_8001_def__prefix_02.map) if !{ var(txn.pathID) -m found } { hdr(x-user) -- 'user1' }` +
+				has2paths,
+		},
+		"test06": {
+			hasHTTPFilter: true, hasReqHost: false, hasFrontACL: true,
+			expFront: frontHasfilterNoreq,
+			expBack: pathsNoreqHasacl + `
+    http-request set-var-fmt(req.fe) "%f"
+    http-request set-var(txn.pathID) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_back_default_app_8080_front_http_8001_def__prefix_01.map) if { var(req.fe) -m str _front_http_8001 } { hdr(x-user) -- 'user2' }
+    http-request set-var(txn.pathID) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_back_default_app_8080_front_http_8001_def__prefix_02.map) if { var(req.fe) -m str _front_http_8001 } !{ var(txn.pathID) -m found } { hdr(x-user) -- 'user1' }
+    http-request set-var(txn.pathID) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_back_default_app_8080_front_http_8002_def__prefix_01.map) if { var(req.fe) -m str _front_http_8002 } { hdr(x-user) -- 'user2' }
+    http-request set-var(txn.pathID) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_back_default_app_8080_front_http_8002_def__prefix_02.map) if { var(req.fe) -m str _front_http_8002 } !{ var(txn.pathID) -m found } { hdr(x-user) -- 'user1' }` +
+				has4pathsv2,
+		},
+		"test07": {
+			hasHTTPFilter: true, hasReqHost: true, hasFrontACL: false,
+			expFront: frontHasfilterHasreq,
+			expBack: pathsHasreqNoacl + `
+    http-request set-var(txn.pathID) var(req.base),map_dir(/etc/haproxy/maps/_back_default_app_8080_front_http_8001_req__prefix_01.map) if { hdr(x-user) -- 'user2' }
+    http-request set-var(txn.pathID) var(req.base),map_dir(/etc/haproxy/maps/_back_default_app_8080_front_http_8001_req__prefix_02.map) if !{ var(txn.pathID) -m found } { hdr(x-user) -- 'user1' }
+    http-request set-var(txn.pathID) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_back_default_app_8080_front_http_8001_def__prefix_01.map) if { hdr(x-user) -- 'user2' }
+    http-request set-var(txn.pathID) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_back_default_app_8080_front_http_8001_def__prefix_02.map) if !{ var(txn.pathID) -m found } { hdr(x-user) -- 'user1' }` +
+				has4pathsv1,
+		},
+		"test08": {
+			hasHTTPFilter: true, hasReqHost: true, hasFrontACL: true,
+			expFront: frontHasfilterHasreq,
+			expBack: pathsHasreqHasacl + `
+    http-request set-var-fmt(req.fe) "%f"
+    http-request set-var(txn.pathID) var(req.base),map_dir(/etc/haproxy/maps/_back_default_app_8080_front_http_8001_req__prefix_01.map) if { var(req.fe) -m str _front_http_8001 } { hdr(x-user) -- 'user2' }
+    http-request set-var(txn.pathID) var(req.base),map_dir(/etc/haproxy/maps/_back_default_app_8080_front_http_8001_req__prefix_02.map) if { var(req.fe) -m str _front_http_8001 } !{ var(txn.pathID) -m found } { hdr(x-user) -- 'user1' }
+    http-request set-var(txn.pathID) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_back_default_app_8080_front_http_8001_def__prefix_01.map) if { var(req.fe) -m str _front_http_8001 } { hdr(x-user) -- 'user2' }
+    http-request set-var(txn.pathID) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_back_default_app_8080_front_http_8001_def__prefix_02.map) if { var(req.fe) -m str _front_http_8001 } !{ var(txn.pathID) -m found } { hdr(x-user) -- 'user1' }
+    http-request set-var(txn.pathID) var(req.base),map_dir(/etc/haproxy/maps/_back_default_app_8080_front_http_8002_req__prefix_01.map) if { var(req.fe) -m str _front_http_8002 } { hdr(x-user) -- 'user2' }
+    http-request set-var(txn.pathID) var(req.base),map_dir(/etc/haproxy/maps/_back_default_app_8080_front_http_8002_req__prefix_02.map) if { var(req.fe) -m str _front_http_8002 } !{ var(txn.pathID) -m found } { hdr(x-user) -- 'user1' }
+    http-request set-var(txn.pathID) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_back_default_app_8080_front_http_8002_def__prefix_01.map) if { var(req.fe) -m str _front_http_8002 } { hdr(x-user) -- 'user2' }
+    http-request set-var(txn.pathID) str(<default>\#),concat(,req.path),map_dir(/etc/haproxy/maps/_back_default_app_8080_front_http_8002_def__prefix_02.map) if { var(req.fe) -m str _front_http_8002 } !{ var(txn.pathID) -m found } { hdr(x-user) -- 'user1' }` +
+				has8paths,
+		},
+	}
+	for name, test := range testCases {
+		t.Run(name, func(t *testing.T) {
+			c := setup(t)
+			defer c.teardown()
 
-	b = c.config.Backends().AcquireBackend("d1", "app", "8080")
-	b.Endpoints = []*hatypes.Endpoint{endpointS1}
-	h = c.config.Hosts().AcquireHost("d1.local")
-	h.TLS.UseDefaultCrt = false
-	h.AddPath(b, "/", hatypes.MatchBegin)
+			b := c.config.backends.AcquireBackend("default", "app", "8080")
+			b.Endpoints = []*hatypes.Endpoint{endpointS1}
 
-	b = c.config.Backends().AcquireBackend("d2", "app", "8080")
-	b.Endpoints = []*hatypes.Endpoint{endpointS21}
-	h = c.config.Hosts().AcquireHost("d2.local")
-	h.TLS.UseDefaultCrt = true
-	h.AddPath(b, "/", hatypes.MatchBegin)
+			f1 := c.httpFrontend(8001)
+			f1.Name = "_front_http_8001"
+			f1h1 := f1.AcquireHost(hatypes.DefaultHost)
+			link1 := hatypes.CreatePathLink("/app1", hatypes.MatchPrefix)
+			link2 := hatypes.CreatePathLink("/app2", hatypes.MatchPrefix)
+			if test.hasHTTPFilter {
+				link1.WithHeadersMatch(hatypes.HTTPHeaderMatch{{Name: "x-user", Value: "user1"}})
+				link2.WithHeadersMatch(hatypes.HTTPHeaderMatch{{Name: "x-user", Value: "user2"}})
+			}
+			f1h1.AddLink(b, link1).MaxBodySize = 1048576
+			f1h1.AddLink(b, link2).MaxBodySize = 2097152
+			if test.hasReqHost {
+				f1h2 := f1.AcquireHost("d1.local")
+				f1h2.AddLink(b, link1).MaxBodySize = 3145728
+				f1h2.AddLink(b, link2).MaxBodySize = 4194304
+			}
+			var frontend2 string
+			if test.hasFrontACL {
+				f2 := c.httpFrontend(8002)
+				f2.Name = "_front_http_8002"
+				f2h1 := f2.AcquireHost(hatypes.DefaultHost)
+				f2h1.AddLink(b, link1).MaxBodySize = 5242880
+				f2h1.AddLink(b, link2).MaxBodySize = 6291456
+				if test.hasReqHost {
+					f2h2 := f2.AcquireHost("d1.local")
+					f2h2.AddLink(b, link1).MaxBodySize = 7340032
+					f2h2.AddLink(b, link2).MaxBodySize = 8388608
+				}
+				frontend2 = `
+frontend _front_http_8002
+    mode http
+    bind :8002
+    <<set-req-base>>
+    <<http-headers>>` + strings.ReplaceAll(test.expFront, "_front_http_8001_", "_front_http_8002_") + `
+    use_backend %[var(req.backend)] if { var(req.backend) -m found }
+    use_backend %[var(req.defaultbackend)]
+    default_backend _error404`
+			}
 
-	c.Update()
-	c.checkConfig(`
+			c.Update()
+			c.checkConfig(`
 <<global>>
 <<defaults>>
-backend d1_app_8080
-    mode http
+backend default_app_8080
+    mode http` + test.expBack + `
     server s1 172.17.0.11:8080 weight 100
-backend d2_app_8080
-    mode http
-    server s21 172.17.0.121:8080 weight 100
 <<backends-default>>
-<<frontends-default>>
+frontend _front_http_8001
+    mode http
+    bind :8001
+    <<set-req-base>>
+    <<http-headers>>` + test.expFront + `
+    use_backend %[var(req.backend)] if { var(req.backend) -m found }
+    use_backend %[var(req.defaultbackend)]
+    default_backend _error404` + frontend2 + `
 <<support>>
 `)
-	c.checkMap("_front_http_host__begin.map", `
-d1.local#/ d1_app_8080
-d2.local#/ d2_app_8080
-`)
-	c.checkMap("_front_https_host__begin.map", `
-d2.local#/ d2_app_8080
-`)
-	c.logger.CompareLogging(defaultLogging)
+			c.logger.CompareLogging(defaultLogging)
+		})
+	}
+
 }
+
 func TestInstanceStrictHost(t *testing.T) {
 	c := setup(t)
 	defer c.teardown()
@@ -2594,8 +2887,9 @@ func TestInstanceStrictHost(t *testing.T) {
 	var h *hatypes.Host
 	var b = c.config.Backends().AcquireBackend("d1", "app", "8080")
 	b.Endpoints = []*hatypes.Endpoint{endpointS1}
-	h = c.config.Hosts().AcquireHost("d1.local")
+	h = c.httpFrontend(80).AcquireHost("d1.local")
 	h.AddPath(b, "/path", hatypes.MatchBegin)
+
 	c.config.Global().StrictHost = true
 
 	c.Update()
@@ -2606,16 +2900,13 @@ backend d1_app_8080
     mode http
     server s1 172.17.0.11:8080 weight 100
 <<backends-default>>
-<<frontends-default>>
+<<frontend-http>>
+    default_backend _error404
 <<support>>
 `)
 	c.checkMap("_front_http_host__begin.map", `
 d1.local#/path d1_app_8080
 d1.local#/ _error404
-`)
-	c.checkMap("_front_https_host__begin.map", `
-d1.local#/path d1_app_8080
-d1.local#/ _error404
 `)
 	c.logger.CompareLogging(defaultLogging)
 }
@@ -2627,14 +2918,16 @@ func TestInstanceStrictHostDefaultHost(t *testing.T) {
 	var h *hatypes.Host
 	var b *hatypes.Backend
 
+	f := c.httpFrontend(80)
+
 	b = c.config.Backends().AcquireBackend("d1", "app", "8080")
 	b.Endpoints = []*hatypes.Endpoint{endpointS1}
-	h = c.config.Hosts().AcquireHost("d1.local")
+	h = f.AcquireHost("d1.local")
 	h.AddPath(b, "/path", hatypes.MatchBegin)
 
 	b = c.config.Backends().AcquireBackend("d2", "app", "8080")
 	b.Endpoints = []*hatypes.Endpoint{endpointS21}
-	h = c.config.Hosts().AcquireHost(hatypes.DefaultHost)
+	h = f.AcquireHost(hatypes.DefaultHost)
 	h.AddPath(b, "/", hatypes.MatchBegin)
 
 	c.config.Global().StrictHost = true
@@ -2658,33 +2951,17 @@ frontend _front_http
     http-request set-var(req.base) var(req.host),concat(\#,req.path)
     <<http-headers>>
     http-request set-var(req.backend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_host__begin.map)
-    http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path),lower,map_beg(/etc/haproxy/maps/_front_defaulthost__begin.map) if !{ var(req.backend) -m found }
+    http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path),lower,map_beg(/etc/haproxy/maps/_front_http_defaulthost__begin.map) if !{ var(req.backend) -m found }
     use_backend %[var(req.backend)] if { var(req.backend) -m found }
     use_backend %[var(req.defaultbackend)]
     default_backend _error404
-frontend _front_https
-    mode http
-    bind :443 ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_bind_crt.list ca-ignore-err all crt-ignore-err all
-    http-request set-var(req.path) path
-    http-request set-var(req.host) hdr(host),field(1,:),lower
-    http-request set-var(req.base) var(req.host),concat(\#,req.path)
-    http-request set-var(req.hostbackend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_https_host__begin.map)
-    http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path),lower,map_beg(/etc/haproxy/maps/_front_defaulthost__begin.map) if !{ var(req.hostbackend) -m found }
-    <<https-headers>>
-    use_backend %[var(req.hostbackend)] if { var(req.hostbackend) -m found }
-    use_backend %[var(req.defaultbackend)]
-    default_backend _error404
 <<support>>
 `)
 	c.checkMap("_front_http_host__begin.map", `
 d1.local#/path d1_app_8080
 d1.local#/ d2_app_8080
 `)
-	c.checkMap("_front_https_host__begin.map", `
-d1.local#/path d1_app_8080
-d1.local#/ d2_app_8080
-`)
-	c.checkMap("_front_defaulthost__begin.map", `
+	c.checkMap("_front_http_defaulthost__begin.map", `
 <default>#/ d2_app_8080`)
 	c.logger.CompareLogging(defaultLogging)
 }
@@ -2704,19 +2981,19 @@ func TestInstanceFrontend(t *testing.T) {
 	var h *hatypes.Host
 	var b *hatypes.Backend
 
+	f := c.httpsFrontend(443)
+
 	b = c.config.Backends().AcquireBackend("d1", "app", "8080")
-	h = c.config.Hosts().AcquireHost("d1.local")
-	h.AddPath(b, "/", hatypes.MatchBegin)
-	b.FindBackendPath(h.FindPath("/")[0].Link).SSLRedirect = true
+	h = f.AcquireHost("d1.local")
+	h.AddPath(b, "/", hatypes.MatchBegin).SSLRedirect = true
 	b.Endpoints = []*hatypes.Endpoint{endpointS1}
 	h.VarNamespace = true
 	h.TLS.TLSFilename = "/var/haproxy/ssl/certs/d1.pem"
 	h.TLS.TLSHash = "1"
 
 	b = c.config.Backends().AcquireBackend("d2", "app", "8080")
-	h = c.config.Hosts().AcquireHost("d2.local")
-	h.AddPath(b, "/app", hatypes.MatchPrefix)
-	b.FindBackendPath(h.FindPath("/app")[0].Link).SSLRedirect = true
+	h = f.AcquireHost("d2.local")
+	h.AddPath(b, "/app", hatypes.MatchPrefix).SSLRedirect = true
 	b.Endpoints = []*hatypes.Endpoint{endpointS1}
 	h.TLS.TLSFilename = "/var/haproxy/ssl/certs/d2.pem"
 	h.TLS.TLSHash = "2"
@@ -2738,30 +3015,15 @@ backend d2_app_8080
 backend default_default-backend_8080
     mode http
     server s0 172.17.0.99:8080 weight 100
-frontend _front_http
-    mode http
-    bind :80
-    <<set-req-base>>
-    http-request set-var(txn.namespace) var(req.base),map_dir(/etc/haproxy/maps/_front_namespace__prefix.map)
-    http-request set-var(txn.namespace) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_namespace__begin.map) if !{ var(txn.namespace) -m found }
-    http-request set-var(txn.namespace) str(-) if !{ var(txn.namespace) -m found }
-    <<http-headers>>
-    http-request set-var(req.backend) var(req.base),map_dir(/etc/haproxy/maps/_front_http_host__prefix.map)
-    http-request set-var(req.backend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_host__begin.map) if !{ var(req.backend) -m found }
-    http-request set-header X-Original-Forwarded-For %[hdr(x-forwarded-for)] if { hdr(x-forwarded-for) -m found }
-    http-request del-header x-forwarded-for
-    option forwardfor
-    http-request set-header X-Real-IP %[src]
-    use_backend %[var(req.backend)] if { var(req.backend) -m found }
-    default_backend default_default-backend_8080
+<<backends-default>>
 frontend _front_https
     mode http
-    bind :443 ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_bind_crt.list ca-ignore-err all crt-ignore-err all
+    bind :443 ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_https_bind_crt.list ca-ignore-err all crt-ignore-err all
     <<set-req-base>>
     http-request set-var(req.hostbackend) var(req.base),map_dir(/etc/haproxy/maps/_front_https_host__prefix.map)
     http-request set-var(req.hostbackend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_https_host__begin.map) if !{ var(req.hostbackend) -m found }
-    http-request set-var(txn.namespace) var(req.base),map_dir(/etc/haproxy/maps/_front_namespace__prefix.map)
-    http-request set-var(txn.namespace) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_namespace__begin.map) if !{ var(txn.namespace) -m found }
+    http-request set-var(txn.namespace) var(req.base),map_dir(/etc/haproxy/maps/_front_https_namespace__prefix.map)
+    http-request set-var(txn.namespace) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_https_namespace__begin.map) if !{ var(txn.namespace) -m found }
     http-request set-var(txn.namespace) str(-) if !{ var(txn.namespace) -m found }
     <<https-headers>>
     http-request set-header X-Original-Forwarded-For %[hdr(x-forwarded-for)] if { hdr(x-forwarded-for) -m found }
@@ -2773,26 +3035,20 @@ frontend _front_https
 <<support>>
 `)
 
-	c.checkMap("_front_http_host__begin.map", `
-d1.local#/ d1_app_8080
-`)
-	c.checkMap("_front_http_host__prefix.map", `
-d2.local#/app d2_app_8080
-`)
 	c.checkMap("_front_https_host__begin.map", `
 d1.local#/ d1_app_8080
 `)
 	c.checkMap("_front_https_host__prefix.map", `
 d2.local#/app d2_app_8080
 `)
-	c.checkMap("_front_namespace__begin.map", `
+	c.checkMap("_front_https_namespace__begin.map", `
 d1.local#/ d1
 `)
-	c.checkMap("_front_namespace__prefix.map", `
+	c.checkMap("_front_https_namespace__prefix.map", `
 d2.local#/app -
 `)
 
-	c.checkMap("_front_bind_crt.list", `
+	c.checkMap("_front_https_bind_crt.list", `
 /var/haproxy/ssl/certs/default.pem !*
 /var/haproxy/ssl/certs/d1.pem d1.local
 /var/haproxy/ssl/certs/d2.pem d2.local
@@ -2805,13 +3061,17 @@ func TestInstanceFrontendMatchHeader(t *testing.T) {
 	c := setup(t)
 	defer c.teardown()
 
-	c.config.frontend.RedirectToCode = 302
+	fhttp := c.httpFrontend(80)
+	fhttp.RedirectToCode = 302
+	fhttps := c.httpsFrontend(443)
+	fhttps.RedirectToCode = 302
 
 	def := c.config.Backends().AcquireBackend("default", "default-backend", "8080")
 	def.Endpoints = []*hatypes.Endpoint{endpointS0}
 	c.config.Backends().DefaultBackend = def
 
-	var h1, h2, h3, h4, h5 *hatypes.Host
+	var h1t, h2t, h3t, h4t, h5t *hatypes.Host
+	var h1s, h2s, h3s, h4s, h5s *hatypes.Host
 	var b1, b21 *hatypes.Backend
 	var link1, link2, link3 *hatypes.PathLink
 
@@ -2870,22 +3130,33 @@ func TestInstanceFrontendMatchHeader(t *testing.T) {
 	b21 = c.config.Backends().AcquireBackend("b21", "app", "8080")
 	b1.Endpoints = []*hatypes.Endpoint{endpointS1}
 	b21.Endpoints = []*hatypes.Endpoint{endpointS21}
-	h1 = c.config.Hosts().AcquireHost(hatypes.DefaultHost)
-	h2 = c.config.Hosts().AcquireHost("h2.local")
-	h2.TLS.TLSFilename = "/var/haproxy/ssl/certs/h2.pem"
-	h3 = c.config.hosts.AcquireHost("h3.local")
-	h4 = c.config.hosts.AcquireHost("h4.local")
-	h4.TLS.TLSFilename = "/var/haproxy/ssl/certs/h4.pem"
-	h4.TLS.CAFilename = "/var/haproxy/ssl/cacerts/h4.pem"
-	h4.TLS.CAHash = "1"
-	h5 = c.config.hosts.AcquireHost("h5.local")
-	h5.VarNamespace = true
-
-	h1.AddPath(b1, "/", hatypes.MatchBegin)
-	h2.AddPath(b1, "/", hatypes.MatchBegin)
-	h3.AddPath(b1, "/", hatypes.MatchBegin)
-	h4.AddPath(b1, "/", hatypes.MatchBegin)
-	h5.AddPath(b1, "/", hatypes.MatchBegin)
+	h1t = fhttp.AcquireHost(hatypes.DefaultHost)
+	h1s = fhttps.AcquireHost(hatypes.DefaultHost)
+	h2t = fhttp.AcquireHost("h2.local")
+	h2s = fhttps.AcquireHost("h2.local")
+	h2s.TLS.TLSFilename = "/var/haproxy/ssl/certs/h2.pem"
+	h3t = fhttp.AcquireHost("h3.local")
+	h3s = fhttps.AcquireHost("h3.local")
+	h4t = fhttp.AcquireHost("h4.local")
+	h4s = fhttps.AcquireHost("h4.local")
+	h4s.TLS.TLSFilename = "/var/haproxy/ssl/certs/h4.pem"
+	h4s.TLS.CAFilename = "/var/haproxy/ssl/cacerts/h4.pem"
+	h4s.TLS.CAHash = "1"
+	h5t = fhttp.AcquireHost("h5.local")
+	h5t.VarNamespace = true
+	h5s = fhttps.AcquireHost("h5.local")
+	h5s.VarNamespace = true
+
+	h1t.AddPath(b1, "/", hatypes.MatchBegin)
+	h2t.AddPath(b1, "/", hatypes.MatchBegin)
+	h3t.AddPath(b1, "/", hatypes.MatchBegin)
+	h4t.AddPath(b1, "/", hatypes.MatchBegin)
+	h5t.AddPath(b1, "/", hatypes.MatchBegin)
+	h1s.AddPath(b1, "/", hatypes.MatchBegin)
+	h2s.AddPath(b1, "/", hatypes.MatchBegin)
+	h3s.AddPath(b1, "/", hatypes.MatchBegin)
+	h4s.AddPath(b1, "/", hatypes.MatchBegin)
+	h5s.AddPath(b1, "/", hatypes.MatchBegin)
 
 	link1 = hatypes.CreatePathLink("/", hatypes.MatchBegin)
 	link1.WithHeadersMatch(hatypes.HTTPHeaderMatch{
@@ -2901,21 +3172,32 @@ func TestInstanceFrontendMatchHeader(t *testing.T) {
 		{Name: "x-version", Value: "^[Tt]est$", Regex: true},
 	})
 
-	h1.AddLink(b1, link1)
-	h1.AddLink(b21, link2)
-
-	h2.AddLink(b1, link1)
-	h2.AddLink(b21, link2)
-
-	h3.AddLinkRedirect(link1, "/login1")
-	h3.AddLinkRedirect(link2, "/login2")
-	h3.AddLinkRedirect(link3, "/login3")
-
-	h4.AddLink(b1, link1)
-	h4.AddLink(b21, link2)
-
-	h5.AddLink(b1, link1)
-	h5.AddLink(b21, link2)
+	h1t.AddLink(b1, link1)
+	h1s.AddLink(b1, link1)
+	h1t.AddLink(b21, link2)
+	h1s.AddLink(b21, link2)
+
+	h2t.AddLink(b1, link1)
+	h2s.AddLink(b1, link1)
+	h2t.AddLink(b21, link2)
+	h2s.AddLink(b21, link2)
+
+	h3t.AddLinkRedirect(link1, "/login1")
+	h3s.AddLinkRedirect(link1, "/login1")
+	h3t.AddLinkRedirect(link2, "/login2")
+	h3s.AddLinkRedirect(link2, "/login2")
+	h3t.AddLinkRedirect(link3, "/login3")
+	h3s.AddLinkRedirect(link3, "/login3")
+
+	h4t.AddLink(b1, link1)
+	h4s.AddLink(b1, link1)
+	h4t.AddLink(b21, link2)
+	h4s.AddLink(b21, link2)
+
+	h5t.AddLink(b1, link1)
+	h5s.AddLink(b1, link1)
+	h5t.AddLink(b21, link2)
+	h5s.AddLink(b21, link2)
 
 	c.Update()
 	c.checkConfig(`
@@ -2938,50 +3220,51 @@ backend b21_app_8080
 backend default_default-backend_8080
     mode http
     server s0 172.17.0.99:8080 weight 100
+<<backends-default>>
 frontend _front_http
     mode http
     bind :80
     <<set-req-base>>
-    http-request set-var(txn.namespace) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_namespace__begin_01.map) if { hdr(x-user) -- 'id' } { hdr(x-version) -m reg -- '^[Ss]taging$' }
-    http-request set-var(txn.namespace) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_namespace__begin_02.map) if !{ var(txn.namespace) -m found } { hdr(x-version) -m reg -- '^[Tt]est$' }
-    http-request set-var(txn.namespace) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_namespace__begin.map) if !{ var(txn.namespace) -m found }
+    http-request set-var(txn.namespace) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_namespace__begin_01.map) if { hdr(x-user) -- 'id' } { hdr(x-version) -m reg -- '^[Ss]taging$' }
+    http-request set-var(txn.namespace) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_namespace__begin_02.map) if !{ var(txn.namespace) -m found } { hdr(x-version) -m reg -- '^[Tt]est$' }
+    http-request set-var(txn.namespace) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_namespace__begin.map) if !{ var(txn.namespace) -m found }
     http-request set-var(txn.namespace) str(-) if !{ var(txn.namespace) -m found }
     <<http-headers>>
-    http-request set-var(req.redirto) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_redir_to__begin_01.map) if { hdr(x-user) -- 'id' } { hdr(x-version) -m reg -- '^[Ss]taging$' }
-    http-request set-var(req.redirto) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_redir_to__begin_02.map) if !{ var(req.redirto) -m found } { hdr(x-version) -m reg -- '^[Tt]est$' }
+    http-request set-var(req.redirto) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_redir_to__begin_01.map) if { hdr(x-user) -- 'id' } { hdr(x-version) -m reg -- '^[Ss]taging$' }
+    http-request set-var(req.redirto) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_redir_to__begin_02.map) if !{ var(req.redirto) -m found } { hdr(x-version) -m reg -- '^[Tt]est$' }
     http-request redirect location %[var(req.redirto)] code 302 if { var(req.redirto) -m found }
     http-request set-var(req.backend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_host__begin_01.map) if { hdr(x-user) -- 'id' } { hdr(x-version) -m reg -- '^[Ss]taging$' }
     http-request set-var(req.backend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_host__begin_02.map) if !{ var(req.backend) -m found } { hdr(x-version) -m reg -- '^[Tt]est$' }
     http-request set-var(req.backend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_host__begin.map) if !{ var(req.backend) -m found }
-    http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path),lower,map_beg(/etc/haproxy/maps/_front_defaulthost__begin_01.map) if !{ var(req.backend) -m found } { hdr(x-user) -- 'id' } { hdr(x-version) -m reg -- '^[Ss]taging$' }
-    http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path),lower,map_beg(/etc/haproxy/maps/_front_defaulthost__begin_02.map) if !{ var(req.backend) -m found } !{ var(req.defaultbackend) -m found } { hdr(x-version) -m reg -- '^[Tt]est$' }
-    http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path),lower,map_beg(/etc/haproxy/maps/_front_defaulthost__begin.map) if !{ var(req.backend) -m found } !{ var(req.defaultbackend) -m found }
+    http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path),lower,map_beg(/etc/haproxy/maps/_front_http_defaulthost__begin_01.map) if !{ var(req.backend) -m found } { hdr(x-user) -- 'id' } { hdr(x-version) -m reg -- '^[Ss]taging$' }
+    http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path),lower,map_beg(/etc/haproxy/maps/_front_http_defaulthost__begin_02.map) if !{ var(req.backend) -m found } !{ var(req.defaultbackend) -m found } { hdr(x-version) -m reg -- '^[Tt]est$' }
+    http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path),lower,map_beg(/etc/haproxy/maps/_front_http_defaulthost__begin.map) if !{ var(req.backend) -m found } !{ var(req.defaultbackend) -m found }
     use_backend %[var(req.backend)] if { var(req.backend) -m found }
     use_backend %[var(req.defaultbackend)]
     default_backend default_default-backend_8080
 frontend _front_https
     mode http
-    bind :443 ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_bind_crt.list ca-ignore-err all crt-ignore-err all
+    bind :443 ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_https_bind_crt.list ca-ignore-err all crt-ignore-err all
     <<set-req-base>>
-    http-request set-var(req.redirto) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_redir_to__begin_01.map) if { hdr(x-user) -- 'id' } { hdr(x-version) -m reg -- '^[Ss]taging$' }
-    http-request set-var(req.redirto) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_redir_to__begin_02.map) if !{ var(req.redirto) -m found } { hdr(x-version) -m reg -- '^[Tt]est$' }
+    http-request set-var(req.redirto) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_https_redir_to__begin_01.map) if { hdr(x-user) -- 'id' } { hdr(x-version) -m reg -- '^[Ss]taging$' }
+    http-request set-var(req.redirto) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_https_redir_to__begin_02.map) if !{ var(req.redirto) -m found } { hdr(x-version) -m reg -- '^[Tt]est$' }
     http-request redirect location %[var(req.redirto)] code 302 if { var(req.redirto) -m found }
     http-request set-var(req.hostbackend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_https_host__begin_01.map) if { hdr(x-user) -- 'id' } { hdr(x-version) -m reg -- '^[Ss]taging$' }
     http-request set-var(req.hostbackend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_https_host__begin_02.map) if !{ var(req.hostbackend) -m found } { hdr(x-version) -m reg -- '^[Tt]est$' }
     http-request set-var(req.hostbackend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_https_host__begin.map) if !{ var(req.hostbackend) -m found }
-    http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path),lower,map_beg(/etc/haproxy/maps/_front_defaulthost__begin_01.map) if !{ var(req.hostbackend) -m found } { hdr(x-user) -- 'id' } { hdr(x-version) -m reg -- '^[Ss]taging$' }
-    http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path),lower,map_beg(/etc/haproxy/maps/_front_defaulthost__begin_02.map) if !{ var(req.hostbackend) -m found } !{ var(req.defaultbackend) -m found } { hdr(x-version) -m reg -- '^[Tt]est$' }
-    http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path),lower,map_beg(/etc/haproxy/maps/_front_defaulthost__begin.map) if !{ var(req.hostbackend) -m found } !{ var(req.defaultbackend) -m found }
-    http-request set-var(txn.namespace) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_namespace__begin_01.map) if { hdr(x-user) -- 'id' } { hdr(x-version) -m reg -- '^[Ss]taging$' }
-    http-request set-var(txn.namespace) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_namespace__begin_02.map) if !{ var(txn.namespace) -m found } { hdr(x-version) -m reg -- '^[Tt]est$' }
-    http-request set-var(txn.namespace) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_namespace__begin.map) if !{ var(txn.namespace) -m found }
+    http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path),lower,map_beg(/etc/haproxy/maps/_front_https_defaulthost__begin_01.map) if !{ var(req.hostbackend) -m found } { hdr(x-user) -- 'id' } { hdr(x-version) -m reg -- '^[Ss]taging$' }
+    http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path),lower,map_beg(/etc/haproxy/maps/_front_https_defaulthost__begin_02.map) if !{ var(req.hostbackend) -m found } !{ var(req.defaultbackend) -m found } { hdr(x-version) -m reg -- '^[Tt]est$' }
+    http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path),lower,map_beg(/etc/haproxy/maps/_front_https_defaulthost__begin.map) if !{ var(req.hostbackend) -m found } !{ var(req.defaultbackend) -m found }
+    http-request set-var(txn.namespace) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_https_namespace__begin_01.map) if { hdr(x-user) -- 'id' } { hdr(x-version) -m reg -- '^[Ss]taging$' }
+    http-request set-var(txn.namespace) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_https_namespace__begin_02.map) if !{ var(txn.namespace) -m found } { hdr(x-version) -m reg -- '^[Tt]est$' }
+    http-request set-var(txn.namespace) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_https_namespace__begin.map) if !{ var(txn.namespace) -m found }
     http-request set-var(txn.namespace) str(-) if !{ var(txn.namespace) -m found }
     <<https-headers>>
     acl tls-has-crt ssl_c_used
-    acl tls-need-crt ssl_fc_sni -i -m str -f /etc/haproxy/maps/_front_tls_needcrt__exact.list
-    acl tls-host-need-crt var(req.host) -i -m str -f /etc/haproxy/maps/_front_tls_needcrt__exact.list
+    acl tls-need-crt ssl_fc_sni -i -m str -f /etc/haproxy/maps/_front_https_tls_needcrt__exact.list
+    acl tls-host-need-crt var(req.host) -i -m str -f /etc/haproxy/maps/_front_https_tls_needcrt__exact.list
     acl tls-has-invalid-crt ssl_c_verify gt 0
-    acl tls-check-crt ssl_fc_sni -i -m str -f /etc/haproxy/maps/_front_tls_auth__exact.list
+    acl tls-check-crt ssl_fc_sni -i -m str -f /etc/haproxy/maps/_front_https_tls_auth__exact.list
     http-request set-var(req.tls_nocrt_redir) str(_internal) if !tls-has-crt tls-need-crt
     http-request set-var(req.tls_invalidcrt_redir) str(_internal) if tls-has-invalid-crt tls-check-crt
     http-request use-service lua.send-421 if tls-has-crt { ssl_fc_has_sni } !{ ssl_fc_sni,strcmp(req.host) eq 0 }
@@ -2995,11 +3278,11 @@ frontend _front_https
 <<support>>
 `)
 
-	c.checkMap("_front_redir_to__begin_02.map", `
+	c.checkMap("_front_http_redir_to__begin_02.map", `
 h3.local#/app3 /login3
 h3.local#/app2 /login2
 `)
-	c.checkMap("_front_redir_to__begin_01.map", `
+	c.checkMap("_front_http_redir_to__begin_01.map", `
 h3.local#/ /login1
 `)
 	c.checkMap("_front_http_host__begin_01.map", `
@@ -3017,6 +3300,32 @@ h2.local#/ b1_app_8080
 h3.local#/ b1_app_8080
 h4.local#/ b1_app_8080
 h5.local#/ b1_app_8080
+`)
+	c.checkMap("_front_http_namespace__begin_01.map", `
+h2.local#/ -
+h3.local#/ -
+h4.local#/ -
+h5.local#/ b1
+`)
+	c.checkMap("_front_http_namespace__begin_02.map", `
+h2.local#/app2 -
+h3.local#/app3 -
+h3.local#/app2 -
+h4.local#/app2 -
+h5.local#/app2 b21
+`)
+	c.checkMap("_front_http_namespace__begin.map", `
+h2.local#/ -
+h3.local#/ -
+h4.local#/ -
+h5.local#/ b1
+`)
+	c.checkMap("_front_https_redir_to__begin_02.map", `
+h3.local#/app3 /login3
+h3.local#/app2 /login2
+`)
+	c.checkMap("_front_https_redir_to__begin_01.map", `
+h3.local#/ /login1
 `)
 	c.checkMap("_front_https_host__begin_01.map", `
 h2.local#/ b1_app_8080
@@ -3034,36 +3343,36 @@ h3.local#/ b1_app_8080
 h4.local#/ b1_app_8080
 h5.local#/ b1_app_8080
 `)
-	c.checkMap("_front_namespace__begin_01.map", `
+	c.checkMap("_front_https_namespace__begin_01.map", `
 h2.local#/ -
 h3.local#/ -
 h4.local#/ -
 h5.local#/ b1
 `)
-	c.checkMap("_front_namespace__begin_02.map", `
+	c.checkMap("_front_https_namespace__begin_02.map", `
 h2.local#/app2 -
 h3.local#/app3 -
 h3.local#/app2 -
 h4.local#/app2 -
 h5.local#/app2 b21
 `)
-	c.checkMap("_front_namespace__begin.map", `
+	c.checkMap("_front_https_namespace__begin.map", `
 h2.local#/ -
 h3.local#/ -
 h4.local#/ -
 h5.local#/ b1
 `)
-	c.checkMap("_front_defaulthost__begin_01.map", `
+	c.checkMap("_front_https_defaulthost__begin_01.map", `
 <default>#/ b1_app_8080
 `)
-	c.checkMap("_front_defaulthost__begin_02.map", `
+	c.checkMap("_front_https_defaulthost__begin_02.map", `
 <default>#/app2 b21_app_8080
 `)
-	c.checkMap("_front_defaulthost__begin.map", `
+	c.checkMap("_front_https_defaulthost__begin.map", `
 <default>#/ b1_app_8080
 `)
 
-	c.checkMap("_front_bind_crt.list", `
+	c.checkMap("_front_https_bind_crt.list", `
 /var/haproxy/ssl/certs/default.pem !*
 /var/haproxy/ssl/certs/h2.pem h2.local
 /var/haproxy/ssl/certs/h4.pem [ca-file /var/haproxy/ssl/cacerts/h4.pem verify optional] h4.local
@@ -3080,9 +3389,11 @@ func TestInstanceFrontendCA(t *testing.T) {
 	def.Endpoints = []*hatypes.Endpoint{endpointS0}
 	c.config.Backends().DefaultBackend = def
 
+	f := c.httpsFrontend(443)
+
 	var h *hatypes.Host
 	var b = c.config.Backends().AcquireBackend("d", "app", "8080")
-	h = c.config.Hosts().AcquireHost("*.d1.local")
+	h = f.AcquireHost("*.d1.local")
 	h.AddPath(b, "/", hatypes.MatchBegin)
 	h.TLS.TLSFilename = "/var/haproxy/ssl/certs/default.pem"
 	h.TLS.TLSHash = "0"
@@ -3090,7 +3401,7 @@ func TestInstanceFrontendCA(t *testing.T) {
 	h.TLS.CAHash = "1"
 	h.TLS.CAErrorPage = "http://d1.local/error.html"
 
-	h = c.config.Hosts().AcquireHost("d2.local")
+	h = f.AcquireHost("d2.local")
 	h.AddPath(b, "/", hatypes.MatchBegin)
 	h.TLS.TLSFilename = "/var/haproxy/ssl/certs/default.pem"
 	h.TLS.TLSHash = "0"
@@ -3100,33 +3411,30 @@ func TestInstanceFrontendCA(t *testing.T) {
 	h.TLS.CRLHash = "2"
 	h.TLS.CAErrorPage = "http://d2.local/error.html"
 
-	h = c.config.Hosts().AcquireHost("d3.local")
+	h = f.AcquireHost("d3.local")
 	h.AddPath(b, "/", hatypes.MatchBegin)
 	h.TLS.TLSFilename = "/var/haproxy/ssl/certs/default.pem"
 	h.TLS.TLSHash = "0"
 	h.TLS.Ciphers = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384"
 
-	h = c.config.Hosts().AcquireHost("d4.local")
+	h = f.AcquireHost("d4.local")
 	h.AddPath(b, "/", hatypes.MatchBegin)
 	h.TLS.TLSFilename = "/var/haproxy/ssl/certs/default.pem"
 	h.TLS.TLSHash = "0"
 	h.TLS.CipherSuites = "TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256"
 
-	h = c.config.Hosts().AcquireHost("d5.local")
+	h = f.AcquireHost("d5.local")
 	h.AddPath(b, "/", hatypes.MatchBegin)
 	h.TLS.TLSFilename = "/var/haproxy/ssl/certs/default.pem"
 	h.TLS.TLSHash = "0"
 	h.TLS.ALPN = "h2"
 
-	h = c.config.Hosts().AcquireHost("d6.local")
+	h = f.AcquireHost("d6.local")
 	h.AddPath(b, "/", hatypes.MatchBegin)
 	h.TLS.TLSFilename = "/var/haproxy/ssl/certs/default.pem"
 	h.TLS.TLSHash = "0"
 	h.TLS.Options = "ssl-min-ver TLSv1.0 ssl-max-ver TLSv1.2"
 
-	for _, path := range b.Paths {
-		path.SSLRedirect = true
-	}
 	b.TLS.AddCertHeader = true
 	b.TLS.FingerprintLower = true
 	b.TLS.Sha2Bits = 384
@@ -3138,9 +3446,6 @@ func TestInstanceFrontendCA(t *testing.T) {
 <<defaults>>
 backend d_app_8080
     mode http
-    acl https-request ssl_fc
-    acl local-offload ssl_fc
-    http-request redirect scheme https if !https-request
     http-request set-header X-SSL-Client-CN   %{+Q}[ssl_c_s_dn(cn)]
     http-request set-header X-SSL-Client-DN   %{+Q}[ssl_c_s_dn]
     http-request set-header X-SSL-Client-SHA1 %{+Q}[ssl_c_sha1,hex,lower]
@@ -3150,34 +3455,26 @@ backend d_app_8080
 backend default_default-backend_8080
     mode http
     server s0 172.17.0.99:8080 weight 100
-frontend _front_http
-    mode http
-    bind :80
-    <<set-req-base>>
-    <<http-headers>>
-    http-request set-var(req.backend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_host__begin.map)
-    http-request set-var(req.backend) var(req.base),map_reg(/etc/haproxy/maps/_front_http_host__regex.map) if !{ var(req.backend) -m found }
-    use_backend %[var(req.backend)] if { var(req.backend) -m found }
-    default_backend default_default-backend_8080
+<<backends-default>>
 frontend _front_https
     mode http
-    bind :443 ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_bind_crt.list ca-ignore-err all crt-ignore-err all
+    bind :443 ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_https_bind_crt.list ca-ignore-err all crt-ignore-err all
     <<set-req-base>>
     http-request set-var(req.hostbackend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_https_host__begin.map)
     http-request set-var(req.hostbackend) var(req.base),map_reg(/etc/haproxy/maps/_front_https_host__regex.map) if !{ var(req.hostbackend) -m found }
     <<https-headers>>
     acl tls-has-crt ssl_c_used
-    acl tls-need-crt ssl_fc_sni -i -m str -f /etc/haproxy/maps/_front_tls_needcrt__exact.list
-    acl tls-need-crt ssl_fc_sni -i -m reg -f /etc/haproxy/maps/_front_tls_needcrt__regex.list
-    acl tls-host-need-crt var(req.host) -i -m str -f /etc/haproxy/maps/_front_tls_needcrt__exact.list
-    acl tls-host-need-crt var(req.host) -i -m reg -f /etc/haproxy/maps/_front_tls_needcrt__regex.list
+    acl tls-need-crt ssl_fc_sni -i -m str -f /etc/haproxy/maps/_front_https_tls_needcrt__exact.list
+    acl tls-need-crt ssl_fc_sni -i -m reg -f /etc/haproxy/maps/_front_https_tls_needcrt__regex.list
+    acl tls-host-need-crt var(req.host) -i -m str -f /etc/haproxy/maps/_front_https_tls_needcrt__exact.list
+    acl tls-host-need-crt var(req.host) -i -m reg -f /etc/haproxy/maps/_front_https_tls_needcrt__regex.list
     acl tls-has-invalid-crt ssl_c_verify gt 0
-    acl tls-check-crt ssl_fc_sni -i -m str -f /etc/haproxy/maps/_front_tls_auth__exact.list
-    acl tls-check-crt ssl_fc_sni -i -m reg -f /etc/haproxy/maps/_front_tls_auth__regex.list
-    http-request set-var(req.tls_nocrt_redir) ssl_fc_sni,lower,map_str(/etc/haproxy/maps/_front_tls_missingcrt_pages__exact.map,_internal) if !tls-has-crt tls-need-crt
-    http-request set-var(req.tls_nocrt_redir) ssl_fc_sni,lower,map_reg(/etc/haproxy/maps/_front_tls_missingcrt_pages__regex.map,_internal) if { var(req.tls_nocrt_redir) -m str _internal }
-    http-request set-var(req.tls_invalidcrt_redir) ssl_fc_sni,lower,map_str(/etc/haproxy/maps/_front_tls_invalidcrt_pages__exact.map,_internal) if tls-has-invalid-crt tls-check-crt
-    http-request set-var(req.tls_invalidcrt_redir) ssl_fc_sni,lower,map_reg(/etc/haproxy/maps/_front_tls_invalidcrt_pages__regex.map,_internal) if { var(req.tls_invalidcrt_redir) -m str _internal }
+    acl tls-check-crt ssl_fc_sni -i -m str -f /etc/haproxy/maps/_front_https_tls_auth__exact.list
+    acl tls-check-crt ssl_fc_sni -i -m reg -f /etc/haproxy/maps/_front_https_tls_auth__regex.list
+    http-request set-var(req.tls_nocrt_redir) ssl_fc_sni,lower,map_str(/etc/haproxy/maps/_front_https_tls_missingcrt_pages__exact.map,_internal) if !tls-has-crt tls-need-crt
+    http-request set-var(req.tls_nocrt_redir) ssl_fc_sni,lower,map_reg(/etc/haproxy/maps/_front_https_tls_missingcrt_pages__regex.map,_internal) if { var(req.tls_nocrt_redir) -m str _internal }
+    http-request set-var(req.tls_invalidcrt_redir) ssl_fc_sni,lower,map_str(/etc/haproxy/maps/_front_https_tls_invalidcrt_pages__exact.map,_internal) if tls-has-invalid-crt tls-check-crt
+    http-request set-var(req.tls_invalidcrt_redir) ssl_fc_sni,lower,map_reg(/etc/haproxy/maps/_front_https_tls_invalidcrt_pages__regex.map,_internal) if { var(req.tls_invalidcrt_redir) -m str _internal }
     http-request redirect location %[var(req.tls_nocrt_redir)] code 303 if { var(req.tls_nocrt_redir) -m found } !{ var(req.tls_nocrt_redir) -m str _internal }
     http-request redirect location %[var(req.tls_invalidcrt_redir)] code 303 if { var(req.tls_invalidcrt_redir) -m found } !{ var(req.tls_invalidcrt_redir) -m str _internal }
     http-request use-service lua.send-421 if tls-has-crt { ssl_fc_has_sni } !{ ssl_fc_sni,strcmp(req.host) eq 0 }
@@ -3190,17 +3487,7 @@ frontend _front_https
 <<support>>
 `)
 
-	c.checkMap("_front_http_host__begin.map", `
-d2.local#/ d_app_8080
-d3.local#/ d_app_8080
-d4.local#/ d_app_8080
-d5.local#/ d_app_8080
-d6.local#/ d_app_8080
-`)
-	c.checkMap("_front_http_host__regex.map", `
-^[^.]+\.d1\.local#/ d_app_8080
-`)
-	c.checkMap("_front_bind_crt.list", `
+	c.checkMap("_front_https_bind_crt.list", `
 /var/haproxy/ssl/certs/default.pem !*
 /var/haproxy/ssl/certs/default.pem [ca-file /var/haproxy/ssl/ca/d1.local.pem verify optional] *.d1.local
 /var/haproxy/ssl/certs/default.pem [ca-file /var/haproxy/ssl/ca/d2.local.pem verify optional crl-file /var/haproxy/ssl/ca/d2.local.crl.pem] d2.local
@@ -3219,22 +3506,22 @@ d6.local#/ d_app_8080
 	c.checkMap("_front_https_host__regex.map", `
 ^[^.]+\.d1\.local#/ d_app_8080
 `)
-	c.checkMap("_front_tls_needcrt__exact.list", `
+	c.checkMap("_front_https_tls_needcrt__exact.list", `
 d2.local
 `)
-	c.checkMap("_front_tls_needcrt__regex.list", `
+	c.checkMap("_front_https_tls_needcrt__regex.list", `
 ^[^.]+\.d1\.local$
 `)
-	c.checkMap("_front_tls_auth__exact.list", `
+	c.checkMap("_front_https_tls_auth__exact.list", `
 d2.local
 `)
-	c.checkMap("_front_tls_auth__regex.list", `
+	c.checkMap("_front_https_tls_auth__regex.list", `
 ^[^.]+\.d1\.local$
 `)
-	c.checkMap("_front_tls_missingcrt_pages__exact.map", `
+	c.checkMap("_front_https_tls_missingcrt_pages__exact.map", `
 d2.local http://d2.local/error.html
 `)
-	c.checkMap("_front_tls_invalidcrt_pages__regex.map", `
+	c.checkMap("_front_https_tls_invalidcrt_pages__regex.map", `
 ^[^.]+\.d1\.local$ http://d1.local/error.html
 `)
 
@@ -3314,17 +3601,17 @@ frontend _front__auth
 		var h *hatypes.Host
 		var b = c.config.Backends().AcquireBackend("d1", "app", "8080")
 		b.Endpoints = []*hatypes.Endpoint{endpointS1}
-		h = c.config.Hosts().AcquireHost("d1.local")
+		h = c.httpFrontend(80).AcquireHost("d1.local")
 		h.AddPath(b, "/", hatypes.MatchBegin)
 
-		auth := &c.config.Frontend().AuthProxy
+		auth := &c.config.frontends.AuthProxy
 		auth.Name = "_front__auth"
 		auth.RangeStart = 4001
 		auth.RangeEnd = 4010
 
 		for _, back := range test.backs {
 			backend := c.config.Backends().AcquireAuthBackend(back.iplist, back.port, back.hostname)
-			_, _ = c.config.Frontend().AcquireAuthBackendName(backend.BackendID())
+			_, _ = auth.AcquireAuthBackendName(backend.BackendID())
 		}
 
 		c.Update()
@@ -3335,7 +3622,8 @@ backend d1_app_8080
     mode http
     server s1 172.17.0.11:8080 weight 100
 <<backends-default>>` + test.expfront + `
-<<frontends-default>>
+<<frontend-http>>
+    default_backend _error404
 <<support>>
 `)
 
@@ -3347,20 +3635,17 @@ backend d1_app_8080
 func TestInstanceFrontendAuthExternal(t *testing.T) {
 	backend1ID := "d_app1_8080"
 	allHeaders := []string{"*"}
-	testCases := []struct {
-		authext   *hatypes.AuthExternal
+	testCases := map[string]struct {
+		authext   hatypes.AuthExternal
 		expconfig string
 		logging   string
 	}{
-		// 0
-		{},
-		// 1
-		{
-			authext: &hatypes.AuthExternal{},
+		"test01": {},
+		"test02": {
+			authext: hatypes.AuthExternal{},
 		},
-		// 2
-		{
-			authext: &hatypes.AuthExternal{
+		"test03": {
+			authext: hatypes.AuthExternal{
 				AuthBackendName: backend1ID,
 				AuthPath:        "/auth",
 				Method:          "GET",
@@ -3372,9 +3657,8 @@ func TestInstanceFrontendAuthExternal(t *testing.T) {
     http-request lua.auth-intercept d_app1_8080 /auth GET '*' '*' '*' if { var(req.base) -m str beg 'd.local#/' }
     http-request deny if !{ var(txn.auth_response_successful) -m bool } { var(req.base) -m str beg 'd.local#/' }`,
 		},
-		// 3
-		{
-			authext: &hatypes.AuthExternal{
+		"test04": {
+			authext: hatypes.AuthExternal{
 				AuthBackendName: backend1ID,
 				AuthPath:        "/auth",
 				Method:          "POST",
@@ -3388,9 +3672,8 @@ func TestInstanceFrontendAuthExternal(t *testing.T) {
     http-request deny if !{ var(txn.auth_response_successful) -m bool } { var(req.base) -m str beg 'd.local#/' }
     http-request set-header X-UserID %[var(req.auth_response_header.x_user_id)] if { var(req.auth_response_header.x_user_id) -m found } { var(req.base) -m str beg 'd.local#/' }`,
 		},
-		// 4
-		{
-			authext: &hatypes.AuthExternal{
+		"test05": {
+			authext: hatypes.AuthExternal{
 				AuthBackendName: backend1ID,
 				AuthPath:        "/auth",
 				Method:          "POST",
@@ -3403,9 +3686,8 @@ func TestInstanceFrontendAuthExternal(t *testing.T) {
     http-request lua.auth-intercept d_app1_8080 /auth POST 'X-UserID,X-GroupID' '*' '*' if { var(req.base) -m str beg 'd.local#/' }
     http-request redirect location /login if !{ var(txn.auth_response_successful) -m bool } { var(req.base) -m str beg 'd.local#/' }`,
 		},
-		// 5
-		{
-			authext: &hatypes.AuthExternal{
+		"test06": {
+			authext: hatypes.AuthExternal{
 				AuthBackendName: backend1ID,
 				AuthPath:        "/login",
 				Method:          "POST",
@@ -3419,9 +3701,9 @@ func TestInstanceFrontendAuthExternal(t *testing.T) {
     http-request lua.auth-intercept d_app1_8080 /login POST '*' 'X-Region,X-Tenant' '*' if !{ path_beg /login } { var(req.base) -m str beg 'd.local#/' }
     http-request redirect location /login if !{ var(txn.auth_response_successful) -m bool } !{ path_beg /login } { var(req.base) -m str beg 'd.local#/' }`,
 		},
-		// 6
-		{
-			authext: &hatypes.AuthExternal{
+		"test07": {
+			authext: hatypes.AuthExternal{
+				AuthPath:   "/",
 				AlwaysDeny: true,
 			},
 			expconfig: `
@@ -3429,25 +3711,24 @@ func TestInstanceFrontendAuthExternal(t *testing.T) {
 		},
 	}
 
-	for _, test := range testCases {
-		c := setup(t)
-		defer c.teardown()
+	for name, test := range testCases {
+		t.Run(name, func(t *testing.T) {
+			c := setup(t)
+			defer c.teardown()
 
-		h := c.config.Hosts().AcquireHost("d.local")
-		b := c.config.Backends().AcquireBackend("d", "app1", "8080")
+			h := c.httpFrontend(80).AcquireHost("d.local")
+			b := c.config.Backends().AcquireBackend("d", "app1", "8080")
 
-		path := h.AddPath(b, "/", hatypes.MatchBegin)
-		path.AuthExt = test.authext
+			path := h.AddPath(b, "/", hatypes.MatchBegin)
+			path.AuthExtFront = test.authext
 
-		c.Update()
-		c.checkConfig(`
+			c.Update()
+			c.checkConfig(`
 <<global>>
 <<defaults>>
 backend d_app1_8080
     mode http
-backend _error404
-    mode http
-    http-request use-service lua.send-404
+<<backends-default>>
 frontend _front_http
     mode http
     bind :80
@@ -3456,20 +3737,13 @@ frontend _front_http
     http-request set-var(req.backend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_host__begin.map)
     use_backend %[var(req.backend)] if { var(req.backend) -m found }
     default_backend _error404
-frontend _front_https
-    mode http
-    bind :443 ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_bind_crt.list ca-ignore-err all crt-ignore-err all
-    <<set-req-base>>` + test.expconfig + `
-    http-request set-var(req.hostbackend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_https_host__begin.map)
-    <<https-headers>>
-    use_backend %[var(req.hostbackend)] if { var(req.hostbackend) -m found }
-    default_backend _error404
 <<support>>
 `)
-		if test.logging == "" {
-			test.logging = defaultLogging
-		}
-		c.logger.CompareLogging(test.logging)
+			if test.logging == "" {
+				test.logging = defaultLogging
+			}
+			c.logger.CompareLogging(test.logging)
+		})
 	}
 }
 
@@ -3484,17 +3758,17 @@ func TestInstanceSomePaths(t *testing.T) {
 	var h *hatypes.Host
 	var b *hatypes.Backend
 
+	f := c.httpsFrontend(443)
+
 	b = c.config.Backends().AcquireBackend("d", "app0", "8080")
-	h = c.config.Hosts().AcquireHost("d.local")
+	h = f.AcquireHost("d.local")
 	h.TLS.TLSFilename = "/var/haproxy/ssl/certs/default.pem"
 	h.TLS.TLSHash = "0"
-	h.AddPath(b, "/", hatypes.MatchBegin)
-	b.FindBackendPath(h.FindPath("/")[0].Link).SSLRedirect = true
+	h.AddPath(b, "/", hatypes.MatchBegin).SSLRedirect = true
 	b.Endpoints = []*hatypes.Endpoint{endpointS1}
 
 	b = c.config.Backends().AcquireBackend("d", "app1", "8080")
-	h.AddPath(b, "/app", hatypes.MatchBegin)
-	b.FindBackendPath(h.FindPath("/app")[0].Link).SSLRedirect = true
+	h.AddPath(b, "/app", hatypes.MatchBegin).SSLRedirect = true
 	b.Endpoints = []*hatypes.Endpoint{endpointS1}
 
 	b = c.config.Backends().AcquireBackend("d", "app2", "8080")
@@ -3531,19 +3805,12 @@ backend d_app3_8080
 backend default_default-backend_8080
     mode http
     server s0 172.17.0.99:8080 weight 100
-<<frontend-http>>
-    default_backend default_default-backend_8080
+<<backends-default>>
 <<frontend-https>>
     default_backend default_default-backend_8080
 <<support>>
 `)
 
-	c.checkMap("_front_http_host__begin.map", `
-d.local#/sub d_app3_8080
-d.local#/app/sub d_app2_8080
-d.local#/app d_app1_8080
-d.local#/ d_app0_8080
-`)
 	c.checkMap("_front_https_host__begin.map", `
 d.local#/sub d_app3_8080
 d.local#/app/sub d_app2_8080
@@ -3558,11 +3825,12 @@ func TestInstanceCustomFrontend(t *testing.T) {
 	c := setup(t)
 	defer c.teardown()
 
-	var h *hatypes.Host
-	var b = c.config.Backends().AcquireBackend("d1", "app", "8080")
+	b := c.config.Backends().AcquireBackend("d1", "app", "8080")
 	b.Endpoints = []*hatypes.Endpoint{endpointS1}
-	h = c.config.Hosts().AcquireHost("d1.local")
-	h.AddPath(b, "/", hatypes.MatchBegin)
+	h1 := c.httpFrontend(80).AcquireHost("d1.local")
+	h1.AddPath(b, "/", hatypes.MatchBegin)
+	h2 := c.httpsFrontend(443).AcquireHost("d1.local")
+	h2.AddPath(b, "/", hatypes.MatchBegin)
 	c.config.Global().CustomFrontendEarly = []string{
 		"# dummy tcp-request",
 		"tcp-request connection accept",
@@ -3594,7 +3862,7 @@ frontend _front_http
     default_backend _error404
 frontend _front_https
     mode http
-    bind :443 ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_bind_crt.list ca-ignore-err all crt-ignore-err all
+    bind :443 ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_https_bind_crt.list ca-ignore-err all crt-ignore-err all
     # dummy tcp-request
     tcp-request connection accept
     <<set-req-base>>
@@ -3618,12 +3886,6 @@ func TestInstanceCustomSections(t *testing.T) {
 	c := setup(t)
 	defer c.teardown()
 
-	var h *hatypes.Host
-	var b = c.config.Backends().AcquireBackend("d1", "app", "8080")
-	b.Endpoints = []*hatypes.Endpoint{endpointS1}
-	h = c.config.Hosts().AcquireHost("d1.local")
-	h.AddPath(b, "/", hatypes.MatchBegin)
-
 	c.config.Global().CustomSections = []string{
 		"cache icons",
 		"	total-max-size 4",
@@ -3637,11 +3899,7 @@ func TestInstanceCustomSections(t *testing.T) {
 cache icons
 	total-max-size 4
 	max-age 240
-backend d1_app_8080
-    mode http
-    server s1 172.17.0.11:8080 weight 100
 <<backends-default>>
-<<frontends-default>>
 <<support>>
 `)
 	c.logger.CompareLogging(defaultLogging)
@@ -3667,10 +3925,6 @@ listen _tcp_default_postgresql_5432
     ## multi line
     server srv001 172.17.0.11:5432
 <<backends-default>>
-<<frontend-http-clean>>
-    default_backend _error404
-<<frontend-https-clean>>
-    default_backend _error404
 <<support>>
 `)
 	c.logger.CompareLogging(defaultLogging)
@@ -3680,27 +3934,31 @@ func TestInstanceCustomProxy(t *testing.T) {
 	c := setup(t)
 	defer c.teardown()
 
+	fhttp := c.httpFrontend(80)
+	fhttps := c.httpsFrontend(443)
+	_ = c.config.backends.AcquireRedirectHTTPSBackend()
+	_ = c.config.backends.AcquireNotFoundBackend()
+
 	var h *hatypes.Host
 	var b = c.config.Backends().AcquireBackend("d1", "app", "8080")
 	b.Endpoints = []*hatypes.Endpoint{endpointS1}
-	h = c.config.Hosts().AcquireHost("d1.local")
+	h = fhttp.AcquireHost("d1.local")
 	h.AddPath(b, "/", hatypes.MatchBegin)
-	h.SetSSLPassthrough(true)
 
 	var h2 *hatypes.Host
 	var b2 = c.config.Backends().AcquireBackend("d2", "app", "8080")
 	b2.Endpoints = []*hatypes.Endpoint{endpointS21}
 	b2.ModeTCP = true
-	h2 = c.config.Hosts().AcquireHost("d2.local")
-	h2.AddPath(b, "/", hatypes.MatchBegin)
-	h2.SetSSLPassthrough(true)
+	h2 = fhttps.AcquireHost("d2.local")
+	h2.AddPath(b2, "/", hatypes.MatchBegin)
+	h2.SSLPassthrough = true
 
-	auth := &c.config.Frontend().AuthProxy
+	auth := &c.config.frontends.AuthProxy
 	auth.Name = "_front__auth"
 	auth.RangeStart = 4001
 	auth.RangeEnd = 4010
 	authBackend := c.config.Backends().AcquireAuthBackend([]string{"172.17.100.11"}, 5000, "")
-	_, _ = c.config.Frontend().AcquireAuthBackendName(authBackend.BackendID())
+	_, _ = auth.AcquireAuthBackendName(authBackend.BackendID())
 
 	tcp := c.config.tcpbackends.Acquire("default_pgsql", 5432)
 	tcp.AddEndpoint("172.17.0.21", 5432)
@@ -3770,15 +4028,6 @@ frontend _front_tcp_7001
     mode tcp
     ## custom for _front_tcp_7001
     default_backend d1_app_8080
-listen _front__tls
-    mode tcp
-    bind :443
-    tcp-request inspect-delay 5s
-    tcp-request content set-var(req.sslpassback) req.ssl_sni,lower,map_str(/etc/haproxy/maps/_front_sslpassthrough__exact.map)
-    ## custom for _front__tls
-    tcp-request content accept if { req.ssl_hello_type 1 }
-    use_backend %[var(req.sslpassback)] if { var(req.sslpassback) -m found }
-    server _default_server_https_socket unix@/var/run/haproxy/_https_socket.sock send-proxy-v2
 frontend _front_http
     mode http
     bind :80
@@ -3788,9 +4037,18 @@ frontend _front_http
     ## custom for _front_http
     use_backend %[var(req.backend)] if { var(req.backend) -m found }
     default_backend _error404
+listen _front__tls
+    mode tcp
+    bind :443
+    tcp-request inspect-delay 5s
+    tcp-request content set-var(req.sslpassback) req.ssl_sni,lower,map_str(/etc/haproxy/maps/_front_https_sslpassthrough__exact.map)
+    ## custom for _front__tls
+    tcp-request content accept if { req.ssl_hello_type 1 }
+    use_backend %[var(req.sslpassback)] if { var(req.sslpassback) -m found }
+    server _default_server_front_https_socket unix@/var/run/haproxy/_front_https_socket.sock send-proxy-v2
 frontend _front_https__local
     mode http
-    bind unix@/var/run/haproxy/_https_socket.sock accept-proxy ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_bind_crt.list ca-ignore-err all crt-ignore-err all
+    bind unix@/var/run/haproxy/_front_https_socket.sock accept-proxy ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_https_bind_crt.list ca-ignore-err all crt-ignore-err all
     <<set-req-base>>
     <<https-headers>>
     ## custom for _front_https__local
@@ -3864,8 +4122,10 @@ func TestCustomResponseLua(t *testing.T) {
 		},
 	}
 
-	host1 := c.config.hosts.AcquireHost("server1.local")
+	f := c.httpsFrontend(443)
+	host1 := f.AcquireHost("server1.local")
 	host1.TLS.CAFilename = "/var/haproxy/ssl/ca/ca.pem" // adds mTLS, all host based lua services are being called from there
+	host1.TLS.CAHash = "1"
 	host1.CustomHTTPResponses.ID = host1.Hostname
 	host1.CustomHTTPResponses.Lua = []hatypes.HTTPResponse{
 		{
@@ -3879,7 +4139,7 @@ func TestCustomResponseLua(t *testing.T) {
 
 	backend1 := c.config.Backends().AcquireBackend("default", "server1", "8080")
 	path1 := host1.AddPath(backend1, "/", hatypes.MatchBegin)
-	backend1.FindBackendPath(path1.Link).MaxBodySize = 1024
+	path1.MaxBodySize = 1024
 	backend1.CustomHTTPResponses.ID = backend1.ID
 	backend1.CustomHTTPResponses.Lua = []hatypes.HTTPResponse{
 		{
@@ -3921,24 +4181,33 @@ backend default_server1_8080
     mode http
     http-request set-var(txn.lua_scope) str(default_server1_8080)
     http-request use-service lua.send-413 if { req.body_size,sub(1024) gt 0 }
+    http-request set-header X-SSL-Client-CN   %{+Q}[ssl_c_s_dn(cn)]
+    http-request set-header X-SSL-Client-DN   %{+Q}[ssl_c_s_dn]
+    http-request set-header X-SSL-Client-SHA1 %{+Q}[ssl_c_sha1,hex]
 backend default_server2_8080
     mode http
 backend default_server3_8080
     mode http
     errorfile 503 /etc/haproxy/errorfiles/503-default_server3_8080.http
 <<backends-default>>
-<<frontend-http>>
-    default_backend _error404
 frontend _front_https
     mode http
-    bind :443 ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_bind_crt.list ca-ignore-err all crt-ignore-err all
+    bind :443 ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_https_bind_crt.list ca-ignore-err all crt-ignore-err all
     <<set-req-base>>
     http-request set-var(req.hostbackend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_https_host__begin.map)
     <<https-headers>>
     acl tls-has-crt ssl_c_used
+    acl tls-need-crt ssl_fc_sni -i -m str -f /etc/haproxy/maps/_front_https_tls_needcrt__exact.list
+    acl tls-host-need-crt var(req.host) -i -m str -f /etc/haproxy/maps/_front_https_tls_needcrt__exact.list
     acl tls-has-invalid-crt ssl_c_verify gt 0
-    http-request set-var(txn.lua_scope) var(req.host)
+    acl tls-check-crt ssl_fc_sni -i -m str -f /etc/haproxy/maps/_front_https_tls_auth__exact.list
+    http-request set-var(req.tls_nocrt_redir) str(_internal) if !tls-has-crt tls-need-crt
+    http-request set-var(req.tls_invalidcrt_redir) str(_internal) if tls-has-invalid-crt tls-check-crt
+    http-request set-var(txn.lua_scope) str(_front_https),concat(--,req.host)
     http-request use-service lua.send-421 if tls-has-crt { ssl_fc_has_sni } !{ ssl_fc_sni,strcmp(req.host) eq 0 }
+    http-request use-service lua.send-496 if { var(req.tls_nocrt_redir) -m str _internal }
+    http-request use-service lua.send-421 if !tls-has-crt tls-host-need-crt
+    http-request use-service lua.send-495 if { var(req.tls_invalidcrt_redir) -m str _internal }
     use_backend %[var(req.hostbackend)] if { var(req.hostbackend) -m found }
     use_backend %[var(req.snibackend)] if { var(req.snibackend) -m found }
     default_backend _error404
@@ -3989,7 +4258,7 @@ end)
 core.register_service("send-495", "http", function(applet)
     local scope = applet:get_var("txn.lua_scope")
     local response = ""
-    if scope == "server1.local" then
+    if scope == "_front_https--server1.local" then
         response = [==[
 crt error
 ]==]
@@ -4108,10 +4377,6 @@ backend default_server1_8080
     errorfile 403 /etc/haproxy/errorfiles/403-default_server1_8080.http
     errorfile 503 /etc/haproxy/errorfiles/503-default_server1_8080.http
 <<backends-default>>
-<<frontend-http-clean>>
-    default_backend _error404
-<<frontend-https-clean>>
-    default_backend _error404
 <<support>>
 `)
 
@@ -4159,41 +4424,50 @@ func TestInstanceSSLPassthrough(t *testing.T) {
 	c := setup(t)
 	defer c.teardown()
 
-	var h *hatypes.Host
-	var b = c.config.Backends().AcquireBackend("d2", "app", "8080")
-	h = c.config.Hosts().AcquireHost("d2.local")
-	h.AddPath(b, "/", hatypes.MatchBegin)
-	b.FindBackendPath(h.FindPath("/")[0].Link).SSLRedirect = true
-	b.Endpoints = []*hatypes.Endpoint{endpointS31}
-	// TODO should ingress converter configure mode tcp?
-	b.ModeTCP = true
-	h.SetSSLPassthrough(true)
+	var hhttp, hhttps *hatypes.Host
+	var b *hatypes.Backend
 
-	b = c.config.Backends().AcquireBackend("d3", "app-ssl", "8443")
-	h = c.config.Hosts().AcquireHost("d3.local")
-	h.AddPath(b, "/", hatypes.MatchBegin)
-	b.Endpoints = []*hatypes.Endpoint{endpointS41s}
-	b.ModeTCP = true
-	h.SetSSLPassthrough(true)
+	fhttp := c.httpFrontend(80)
+	fhttps := c.httpsFrontend(443)
+	redir := c.config.Backends().AcquireRedirectHTTPSBackend()
+
+	hhttp = fhttp.AcquireHost("d2.local")
+	hhttp.AddPath(redir, "/", hatypes.MatchBegin)
+
+	b = c.config.Backends().AcquireBackend("d2", "app", "8080")
+	b.Endpoints = []*hatypes.Endpoint{endpointS31}
+	b.ModeTCP = true // TODO should ingress converter configure mode tcp?
+	hhttps = fhttps.AcquireHost("d2.local")
+	hhttps.AddPath(b, "/", hatypes.MatchBegin)
+	hhttps.SSLPassthrough = true
 
 	b = c.config.Backends().AcquireBackend("d3", "app1-http", "8080")
 	b.Endpoints = []*hatypes.Endpoint{endpointS41h}
-	h.HTTPPassthroughBackend = b.ID
+	hhttp = fhttp.AcquireHost("d3.local")
+	hhttp.AddPath(b, "/", hatypes.MatchBegin)
 
 	b = c.config.Backends().AcquireBackend("d3", "app2-http", "8080")
 	b.Endpoints = []*hatypes.Endpoint{endpointS41h}
-	h.AddPath(b, "/app", hatypes.MatchBegin)
+	hhttp.AddPath(b, "/app", hatypes.MatchBegin)
 
-	b = c.config.Backends().AcquireBackend("d4", "app4-ssl", "8443")
-	h = c.config.Hosts().AcquireHost(hatypes.DefaultHost)
-	h.AddPath(b, "/", hatypes.MatchBegin)
+	b = c.config.Backends().AcquireBackend("d3", "app-ssl", "8443")
 	b.Endpoints = []*hatypes.Endpoint{endpointS41s}
 	b.ModeTCP = true
-	h.SetSSLPassthrough(true)
+	hhttps = fhttps.AcquireHost("d3.local")
+	hhttps.AddPath(b, "/", hatypes.MatchBegin)
+	hhttps.SSLPassthrough = true
 
 	b = c.config.Backends().AcquireBackend("d4", "app4-http", "8080")
 	b.Endpoints = []*hatypes.Endpoint{endpointS41h}
-	h.HTTPPassthroughBackend = b.ID
+	hhttp = fhttp.AcquireHost(hatypes.DefaultHost)
+	hhttp.AddPath(b, "/", hatypes.MatchBegin)
+
+	b = c.config.Backends().AcquireBackend("d4", "app4-ssl", "8443")
+	b.Endpoints = []*hatypes.Endpoint{endpointS41s}
+	b.ModeTCP = true
+	hhttps = fhttps.AcquireHost(hatypes.DefaultHost)
+	hhttps.AddPath(b, "/", hatypes.MatchBegin)
+	hhttps.SSLPassthrough = true
 
 	c.Update()
 	c.checkConfig(`
@@ -4221,21 +4495,28 @@ backend _redirect_https
     mode http
     http-request redirect scheme https
 <<backends-default>>
+frontend _front_http
+    mode http
+    bind :80
+    <<set-req-base>>
+    <<http-headers>>
+    http-request set-var(req.backend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_host__begin.map)
+    http-request set-var(req.defaultbackend) str(<default>\#),concat(,req.path),lower,map_beg(/etc/haproxy/maps/_front_http_defaulthost__begin.map) if !{ var(req.backend) -m found }
+    use_backend %[var(req.backend)] if { var(req.backend) -m found }
+    use_backend %[var(req.defaultbackend)]
+    default_backend _error404
 listen _front__tls
     mode tcp
     bind :443
     tcp-request inspect-delay 5s
-    tcp-request content set-var(req.sslpassback) req.ssl_sni,lower,map_str(/etc/haproxy/maps/_front_sslpassthrough__exact.map)
+    tcp-request content set-var(req.sslpassback) req.ssl_sni,lower,map_str(/etc/haproxy/maps/_front_https_sslpassthrough__exact.map)
     tcp-request content accept if { req.ssl_hello_type 1 }
     use_backend %[var(req.sslpassback)] if { var(req.sslpassback) -m found }
     use_backend d4_app4-ssl_8443
-    server _default_server_https_socket unix@/var/run/haproxy/_https_socket.sock send-proxy-v2
-<<frontend-http>>
-    use_backend d4_app4-http_8080
-    default_backend _error404
+    server _default_server_front_https_socket unix@/var/run/haproxy/_front_https_socket.sock send-proxy-v2
 frontend _front_https__local
     mode http
-    bind unix@/var/run/haproxy/_https_socket.sock accept-proxy ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_bind_crt.list ca-ignore-err all crt-ignore-err all
+    bind unix@/var/run/haproxy/_front_https_socket.sock accept-proxy ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_https_bind_crt.list ca-ignore-err all crt-ignore-err all
     <<set-req-base>>
     <<https-headers>>
     use_backend %[var(req.hostbackend)] if { var(req.hostbackend) -m found }
@@ -4243,7 +4524,7 @@ frontend _front_https__local
 <<support>>
 `)
 
-	c.checkMap("_front_sslpassthrough__exact.map", `
+	c.checkMap("_front_https_sslpassthrough__exact.map", `
 d2.local d2_app_8080
 d3.local d3_app-ssl_8443
 `)
@@ -4252,7 +4533,10 @@ d2.local#/ _redirect_https
 d3.local#/app d3_app2-http_8080
 d3.local#/ d3_app1-http_8080
 `)
-	c.checkMap("_front_bind_crt.list", `
+	c.checkMap("_front_http_defaulthost__begin.map", `
+<default>#/ d4_app4-http_8080
+`)
+	c.checkMap("_front_https_bind_crt.list", `
 /var/haproxy/ssl/certs/default.pem !*
 `)
 
@@ -4266,25 +4550,41 @@ func TestInstanceRootRedirect(t *testing.T) {
 	c.config.global.SSL.SSLRedirect = true
 
 	var h *hatypes.Host
-	var b = c.config.Backends().AcquireBackend("d1", "app", "8080")
-	h = c.config.Hosts().AcquireHost("*.d1.local")
+	var b *hatypes.Backend
+
+	fhttp := c.httpFrontend(80)
+	fhttps := c.httpsFrontend(443)
+
+	b = c.config.Backends().AcquireBackend("d1", "app", "8080")
+	b.Endpoints = []*hatypes.Endpoint{endpointS1}
+
+	h = fhttp.AcquireHost("*.d1.local")
+	h.AddPath(b, "/", hatypes.MatchBegin)
+	h.RootRedirect = "/app"
+
+	h = fhttps.AcquireHost("*.d1.local")
+	h.AddPath(b, "/", hatypes.MatchBegin)
 	h.TLS.TLSFilename = "/var/haproxy/ssl/certs/default.pem"
 	h.TLS.TLSHash = "0"
-	h.AddPath(b, "/", hatypes.MatchBegin)
 	h.RootRedirect = "/app"
-	b.Endpoints = []*hatypes.Endpoint{endpointS1}
 
 	b = c.config.Backends().AcquireBackend("d2", "app", "8080")
-	h = c.config.Hosts().AcquireHost("d2.local")
+	b.Endpoints = []*hatypes.Endpoint{endpointS21}
+
+	h = fhttp.AcquireHost("d2.local")
+	app1path := h.AddPath(b, "/app1", hatypes.MatchBegin)
+	app2path := h.AddPath(b, "/app2", hatypes.MatchBegin)
+	h.RootRedirect = "/app1"
+	app1path.SSLRedirect = true
+	app2path.SSLRedirect = true
+
+	h = fhttps.AcquireHost("d2.local")
+	h.AddPath(b, "/app1", hatypes.MatchBegin).SSLRedirect = true // mimic ingress API by making http and https identical
+	h.AddPath(b, "/app2", hatypes.MatchBegin).SSLRedirect = true
+
 	h.TLS.TLSFilename = "/var/haproxy/ssl/certs/default.pem"
 	h.TLS.TLSHash = "0"
-	h.AddPath(b, "/app1", hatypes.MatchBegin)
-	h.AddPath(b, "/app2", hatypes.MatchBegin)
 	h.RootRedirect = "/app1"
-	for _, path := range b.Paths {
-		path.SSLRedirect = true
-	}
-	b.Endpoints = []*hatypes.Endpoint{endpointS21}
 
 	c.Update()
 
@@ -4304,9 +4604,9 @@ frontend _front_http
     mode http
     bind :80
     <<set-req-base>>
-    http-request redirect scheme https if { path / } { var(req.host) -i -m str -f /etc/haproxy/maps/_front_redir_root_ssl__exact.map }
-    http-request set-var(req.rootredir) var(req.host),map_str(/etc/haproxy/maps/_front_redir_fromroot__exact.map)
-    http-request set-var(req.rootredir) var(req.host),map_reg(/etc/haproxy/maps/_front_redir_fromroot__regex.map) if !{ var(req.rootredir) -m found }
+    http-request redirect scheme https if { path / } { var(req.host) -i -m str -f /etc/haproxy/maps/_front_http_redir_root_ssl__exact.map }
+    http-request set-var(req.rootredir) var(req.host),map_str(/etc/haproxy/maps/_front_http_redir_fromroot__exact.map)
+    http-request set-var(req.rootredir) var(req.host),map_reg(/etc/haproxy/maps/_front_http_redir_fromroot__regex.map) if !{ var(req.rootredir) -m found }
     http-request redirect location %[var(req.rootredir)] if { path / } { var(req.rootredir) -m found }
     <<http-headers>>
     http-request set-var(req.backend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_host__begin.map)
@@ -4315,12 +4615,12 @@ frontend _front_http
     default_backend _error404
 frontend _front_https
     mode http
-    bind :443 ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_bind_crt.list ca-ignore-err all crt-ignore-err all
+    bind :443 ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_https_bind_crt.list ca-ignore-err all crt-ignore-err all
     <<set-req-base>>
     http-request set-var(req.hostbackend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_https_host__begin.map)
     http-request set-var(req.hostbackend) var(req.base),map_reg(/etc/haproxy/maps/_front_https_host__regex.map) if !{ var(req.hostbackend) -m found }
-    http-request set-var(req.rootredir) var(req.host),map_str(/etc/haproxy/maps/_front_redir_fromroot__exact.map)
-    http-request set-var(req.rootredir) var(req.host),map_reg(/etc/haproxy/maps/_front_redir_fromroot__regex.map) if !{ var(req.rootredir) -m found }
+    http-request set-var(req.rootredir) var(req.host),map_str(/etc/haproxy/maps/_front_https_redir_fromroot__exact.map)
+    http-request set-var(req.rootredir) var(req.host),map_reg(/etc/haproxy/maps/_front_https_redir_fromroot__regex.map) if !{ var(req.rootredir) -m found }
     http-request redirect location %[var(req.rootredir)] if { path / } { var(req.rootredir) -m found }
     <<https-headers>>
     use_backend %[var(req.hostbackend)] if { var(req.hostbackend) -m found }
@@ -4328,9 +4628,6 @@ frontend _front_https
 <<support>>
 `)
 
-	c.checkMap("_front_http_host__regex.map", `
-^[^.]+\.d1\.local#/ d1_app_8080
-`)
 	c.checkMap("_front_http_host__begin.map", `
 d2.local#/app2 d2_app_8080
 d2.local#/app1 d2_app_8080
@@ -4338,13 +4635,13 @@ d2.local#/app1 d2_app_8080
 	c.checkMap("_front_http_host__regex.map", `
 ^[^.]+\.d1\.local#/ d1_app_8080
 `)
-	c.checkMap("_front_redir_fromroot__exact.map", `
+	c.checkMap("_front_http_redir_fromroot__exact.map", `
 d2.local /app1
 `)
-	c.checkMap("_front_redir_root_ssl__exact.map", `
+	c.checkMap("_front_http_redir_root_ssl__exact.map", `
 d2.local
 `)
-	c.checkMap("_front_redir_fromroot__regex.map", `
+	c.checkMap("_front_http_redir_fromroot__regex.map", `
 ^[^.]+\.d1\.local$ /app
 `)
 	c.checkMap("_front_https_host__begin.map", `
@@ -4353,6 +4650,12 @@ d2.local#/app1 d2_app_8080
 `)
 	c.checkMap("_front_https_host__regex.map", `
 ^[^.]+\.d1\.local#/ d1_app_8080
+`)
+	c.checkMap("_front_https_redir_fromroot__exact.map", `
+d2.local /app1
+`)
+	c.checkMap("_front_https_redir_fromroot__regex.map", `
+^[^.]+\.d1\.local$ /app
 `)
 
 	c.logger.CompareLogging(defaultLogging)
@@ -4363,22 +4666,26 @@ func TestInstanceAlias(t *testing.T) {
 	defer c.teardown()
 
 	var h *hatypes.Host
-	var b = c.config.Backends().AcquireBackend("d1", "app", "8080")
+	var b *hatypes.Backend
+
+	f := c.httpFrontend(80)
+
+	b = c.config.Backends().AcquireBackend("d1", "app", "8080")
 	b.Endpoints = []*hatypes.Endpoint{endpointS1}
-	h = c.config.Hosts().AcquireHost("d1.local")
+	h = f.AcquireHost("d1.local")
 	h.AddPath(b, "/", hatypes.MatchBegin)
 	h.Alias.AliasName = "*.d1.local"
 
 	b = c.config.Backends().AcquireBackend("d2", "app", "8080")
 	b.Endpoints = []*hatypes.Endpoint{endpointS21}
-	h = c.config.Hosts().AcquireHost("d2.local")
+	h = f.AcquireHost("d2.local")
 	h.AddPath(b, "/", hatypes.MatchBegin)
 	h.Alias.AliasName = "sub.d2.local"
 	h.Alias.AliasRegex = "^[a-z]+\\.d2\\.local$"
 
 	b = c.config.Backends().AcquireBackend("d3", "app", "8080")
 	b.Endpoints = []*hatypes.Endpoint{endpointS31}
-	h = c.config.Hosts().AcquireHost("d3.local")
+	h = f.AcquireHost("d3.local")
 	h.AddPath(b, "/", hatypes.MatchBegin)
 	h.Alias.AliasRegex = "d3\\.local$"
 
@@ -4405,15 +4712,6 @@ frontend _front_http
     http-request set-var(req.backend) var(req.base),map_reg(/etc/haproxy/maps/_front_http_host__regex.map) if !{ var(req.backend) -m found }
     use_backend %[var(req.backend)] if { var(req.backend) -m found }
     default_backend _error404
-frontend _front_https
-    mode http
-    bind :443 ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_bind_crt.list ca-ignore-err all crt-ignore-err all
-    <<set-req-base>>
-    http-request set-var(req.hostbackend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_https_host__begin.map)
-    http-request set-var(req.hostbackend) var(req.base),map_reg(/etc/haproxy/maps/_front_https_host__regex.map) if !{ var(req.hostbackend) -m found }
-    <<https-headers>>
-    use_backend %[var(req.hostbackend)] if { var(req.hostbackend) -m found }
-    default_backend _error404
 <<support>>
 `)
 
@@ -4423,13 +4721,7 @@ d2.local#/ d2_app_8080
 d3.local#/ d3_app_8080
 sub.d2.local#/ d2_app_8080
 `)
-	c.checkMap("_front_https_host__begin.map", `
-d1.local#/ d1_app_8080
-d2.local#/ d2_app_8080
-d3.local#/ d3_app_8080
-sub.d2.local#/ d2_app_8080
-`)
-	c.checkMap("_front_https_host__regex.map", `
+	c.checkMap("_front_http_host__regex.map", `
 ^[a-z]+\.d2\.local#/ d2_app_8080
 ^[^.]+\.d1\.local#/ d1_app_8080
 d3\.local#/ d3_app_8080
@@ -4453,13 +4745,13 @@ func TestInstanceRedirectFrom(t *testing.T) {
 			},
 			code: 301,
 			expHTTP: `
-    http-request set-var(req.redirdest) var(req.host),map_reg(/etc/haproxy/maps/_front_redir_from__regex.map) if !{ var(req.backend) -m found }
+    http-request set-var(req.redirdest) var(req.host),map_reg(/etc/haproxy/maps/_front_http_redir_from__regex.map) if !{ var(req.backend) -m found }
     http-request redirect prefix //%[var(req.redirdest)] code 301 if { var(req.redirdest) -m found }`,
 			expHTTPS: `
-    http-request set-var(req.redirdest) var(req.host),map_reg(/etc/haproxy/maps/_front_redir_from__regex.map) if !{ var(req.hostbackend) -m found }
+    http-request set-var(req.redirdest) var(req.host),map_reg(/etc/haproxy/maps/_front_http_redir_from__regex.map) if !{ var(req.hostbackend) -m found }
     http-request redirect prefix //%[var(req.redirdest)] code 301 if { var(req.redirdest) -m found }`,
 			expMaps: map[string]string{
-				"_front_redir_from__regex.map": `
+				"_front_http_redir_from__regex.map": `
 ^[^.]+\.d1\.local$ d1.local
 `,
 			},
@@ -4472,18 +4764,18 @@ func TestInstanceRedirectFrom(t *testing.T) {
 				{RedirectHostRegex: "\\.d3\\.local$"},
 			},
 			expHTTP: `
-    http-request set-var(req.redirdest) var(req.host),map_str(/etc/haproxy/maps/_front_redir_from__exact.map) if !{ var(req.backend) -m found }
-    http-request set-var(req.redirdest) var(req.host),map_reg(/etc/haproxy/maps/_front_redir_from__regex.map) if !{ var(req.backend) -m found } !{ var(req.redirdest) -m found }
+    http-request set-var(req.redirdest) var(req.host),map_str(/etc/haproxy/maps/_front_http_redir_from__exact.map) if !{ var(req.backend) -m found }
+    http-request set-var(req.redirdest) var(req.host),map_reg(/etc/haproxy/maps/_front_http_redir_from__regex.map) if !{ var(req.backend) -m found } !{ var(req.redirdest) -m found }
     http-request redirect prefix //%[var(req.redirdest)] code 302 if { var(req.redirdest) -m found }`,
 			expHTTPS: `
-    http-request set-var(req.redirdest) var(req.host),map_str(/etc/haproxy/maps/_front_redir_from__exact.map) if !{ var(req.hostbackend) -m found }
-    http-request set-var(req.redirdest) var(req.host),map_reg(/etc/haproxy/maps/_front_redir_from__regex.map) if !{ var(req.hostbackend) -m found } !{ var(req.redirdest) -m found }
+    http-request set-var(req.redirdest) var(req.host),map_str(/etc/haproxy/maps/_front_http_redir_from__exact.map) if !{ var(req.hostbackend) -m found }
+    http-request set-var(req.redirdest) var(req.host),map_reg(/etc/haproxy/maps/_front_http_redir_from__regex.map) if !{ var(req.hostbackend) -m found } !{ var(req.redirdest) -m found }
     http-request redirect prefix //%[var(req.redirdest)] code 302 if { var(req.redirdest) -m found }`,
 			expMaps: map[string]string{
-				"_front_redir_from__exact.map": `
+				"_front_http_redir_from__exact.map": `
 sub.d2.local d2.local
 `,
-				"_front_redir_from__regex.map": `
+				"_front_http_redir_from__regex.map": `
 ^[a-z]+\.d2\.local$ d2.local
 ^[^.]+\.d1\.local$ d1.local
 \.d3\.local$ d3.local
@@ -4498,13 +4790,13 @@ sub.d2.local d2.local
 			code:     301,
 			noredirs: []string{"/.well-known/acme-challenge"},
 			expHTTP: `
-    http-request set-var(req.redirdest) var(req.host),map_reg(/etc/haproxy/maps/_front_redir_from__regex.map) if !{ path_beg "/.well-known/acme-challenge" } !{ var(req.backend) -m found }
+    http-request set-var(req.redirdest) var(req.host),map_reg(/etc/haproxy/maps/_front_http_redir_from__regex.map) if !{ path_beg "/.well-known/acme-challenge" } !{ var(req.backend) -m found }
     http-request redirect prefix //%[var(req.redirdest)] code 301 if !{ path_beg "/.well-known/acme-challenge" } { var(req.redirdest) -m found }`,
 			expHTTPS: `
-    http-request set-var(req.redirdest) var(req.host),map_reg(/etc/haproxy/maps/_front_redir_from__regex.map) if !{ path_beg "/.well-known/acme-challenge" } !{ var(req.hostbackend) -m found }
+    http-request set-var(req.redirdest) var(req.host),map_reg(/etc/haproxy/maps/_front_http_redir_from__regex.map) if !{ path_beg "/.well-known/acme-challenge" } !{ var(req.hostbackend) -m found }
     http-request redirect prefix //%[var(req.redirdest)] code 301 if !{ path_beg "/.well-known/acme-challenge" } { var(req.redirdest) -m found }`,
 			expMaps: map[string]string{
-				"_front_redir_from__regex.map": `
+				"_front_http_redir_from__regex.map": `
 ^[^.]+\.d1\.local$ d1.local
 `,
 			},
@@ -4517,29 +4809,31 @@ sub.d2.local d2.local
 
 		c.config.global.NoRedirects = test.noredirs
 
+		f := c.httpFrontend(80)
+
 		var h *hatypes.Host
 		var b = c.config.Backends().AcquireBackend("d1", "app", "8080")
 		b.Endpoints = []*hatypes.Endpoint{endpointS1}
-		h = c.config.Hosts().AcquireHost("d1.local")
+		h = f.AcquireHost("d1.local")
 		h.AddPath(b, "/", hatypes.MatchBegin)
 		h.Redirect = test.data[0]
 
 		b = c.config.Backends().AcquireBackend("d2", "app", "8080")
 		b.Endpoints = []*hatypes.Endpoint{endpointS21}
-		h = c.config.Hosts().AcquireHost("d2.local")
+		h = f.AcquireHost("d2.local")
 		h.AddPath(b, "/", hatypes.MatchBegin)
 		h.Redirect = test.data[1]
 
 		b = c.config.Backends().AcquireBackend("d3", "app", "8080")
 		b.Endpoints = []*hatypes.Endpoint{endpointS31}
-		h = c.config.Hosts().AcquireHost("d3.local")
+		h = f.AcquireHost("d3.local")
 		h.AddPath(b, "/", hatypes.MatchBegin)
 		h.Redirect = test.data[2]
 
 		if test.code != 0 {
-			c.config.frontend.RedirectFromCode = test.code
+			f.RedirectFromCode = test.code
 		} else {
-			c.config.frontend.RedirectFromCode = 302
+			f.RedirectFromCode = 302
 		}
 
 		c.Update()
@@ -4559,33 +4853,11 @@ backend d3_app_8080
 frontend _front_http
     mode http
     bind :80
-    http-request set-var(req.path) path
-    http-request set-var(req.host) hdr(host),field(1,:),lower
-    http-request set-var(req.base) var(req.host),concat(\#,req.path)
-    http-request set-header X-Forwarded-Proto http
-    http-request del-header X-SSL-Client-CN
-    http-request del-header X-SSL-Client-DN
-    http-request del-header X-SSL-Client-SHA1
-    http-request del-header X-SSL-Client-SHA2
-    http-request del-header X-SSL-Client-Cert
+    <<set-req-base>>
+    <<http-headers>>
     http-request set-var(req.backend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_host__begin.map)` + test.expHTTP + `
     use_backend %[var(req.backend)] if { var(req.backend) -m found }
     default_backend _error404
-frontend _front_https
-    mode http
-    bind :443 ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_bind_crt.list ca-ignore-err all crt-ignore-err all
-    http-request set-var(req.path) path
-    http-request set-var(req.host) hdr(host),field(1,:),lower
-    http-request set-var(req.base) var(req.host),concat(\#,req.path)
-    http-request set-var(req.hostbackend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_https_host__begin.map)` + test.expHTTPS + `
-    http-request set-header X-Forwarded-Proto https
-    http-request del-header X-SSL-Client-CN
-    http-request del-header X-SSL-Client-DN
-    http-request del-header X-SSL-Client-SHA1
-    http-request del-header X-SSL-Client-SHA2
-    http-request del-header X-SSL-Client-Cert
-    use_backend %[var(req.hostbackend)] if { var(req.hostbackend) -m found }
-    default_backend _error404
 <<support>>
 `)
 		for file, content := range test.expMaps {
@@ -4609,10 +4881,10 @@ func TestInstanceRedirectTo(t *testing.T) {
 				"https://app.local",
 			},
 			expected: `
-    http-request set-var(req.redirto) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_redir_to__begin.map)
+    http-request set-var(req.redirto) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_redir_to__begin.map)
     http-request redirect location %[var(req.redirto)] code 302 if { var(req.redirto) -m found }`,
 			expMaps: map[string]string{
-				"_front_redir_to__begin.map": `
+				"_front_http_redir_to__begin.map": `
 d1.local#/ https://app.local
 `,
 			},
@@ -4624,14 +4896,14 @@ d1.local#/ https://app.local
 				"https://app.local/app2",
 			},
 			expected: `
-    http-request set-var(req.redirto) var(req.base),map_dir(/etc/haproxy/maps/_front_redir_to__prefix_01.map)
-    http-request set-var(req.redirto) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_redir_to__begin.map) if !{ var(req.redirto) -m found }
+    http-request set-var(req.redirto) var(req.base),map_dir(/etc/haproxy/maps/_front_http_redir_to__prefix_01.map)
+    http-request set-var(req.redirto) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_redir_to__begin.map) if !{ var(req.redirto) -m found }
     http-request redirect location %[var(req.redirto)] code 302 if { var(req.redirto) -m found }`,
 			expMaps: map[string]string{
-				"_front_redir_to__prefix_01.map": `
+				"_front_http_redir_to__prefix_01.map": `
 d1.local#/app2 https://app.local/app2
 `,
-				"_front_redir_to__begin.map": `
+				"_front_http_redir_to__begin.map": `
 d1.local#/ https://app.local/login
 `,
 			},
@@ -4644,10 +4916,10 @@ d1.local#/ https://app.local/login
 				"https://app.local/app3",
 			},
 			expected: `
-    http-request set-var(req.redirto) var(req.base),map_dir(/etc/haproxy/maps/_front_redir_to__prefix.map)
+    http-request set-var(req.redirto) var(req.base),map_dir(/etc/haproxy/maps/_front_http_redir_to__prefix.map)
     http-request redirect location %[var(req.redirto)] code 302 if { var(req.redirto) -m found }`,
 			expMaps: map[string]string{
-				"_front_redir_to__prefix.map": `
+				"_front_http_redir_to__prefix.map": `
 d1.local#/app3 https://app.local/app3
 d1.local#/app2 https://app.local/app2
 `,
@@ -4660,10 +4932,10 @@ d1.local#/app2 https://app.local/app2
 			},
 			noredirs: []string{"/.well-known/acme-challenge"},
 			expected: `
-    http-request set-var(req.redirto) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_redir_to__begin.map) if !{ path_beg "/.well-known/acme-challenge" }
+    http-request set-var(req.redirto) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_redir_to__begin.map) if !{ path_beg "/.well-known/acme-challenge" }
     http-request redirect location %[var(req.redirto)] code 302 if !{ path_beg "/.well-known/acme-challenge" } { var(req.redirto) -m found }`,
 			expMaps: map[string]string{
-				"_front_redir_to__begin.map": `
+				"_front_http_redir_to__begin.map": `
 d1.local#/ https://app.local
 `,
 			},
@@ -4676,7 +4948,8 @@ d1.local#/ https://app.local
 
 		c.config.global.NoRedirects = test.noredirs
 
-		var h = c.config.Hosts().AcquireHost("d1.local")
+		f := c.httpFrontend(80)
+		var h = f.AcquireHost("d1.local")
 		var b = c.config.Backends().AcquireBackend("d1", "app", "8080")
 		b.Endpoints = []*hatypes.Endpoint{endpointS1}
 		if test.to[0] != "" {
@@ -4702,9 +4975,9 @@ d1.local#/ https://app.local
 		}
 
 		if test.code != 0 {
-			c.config.frontend.RedirectToCode = test.code
+			f.RedirectToCode = test.code
 		} else {
-			c.config.frontend.RedirectToCode = 302
+			f.RedirectToCode = 302
 		}
 
 		c.Update()
@@ -4724,33 +4997,11 @@ backend d3_app_8080
 frontend _front_http
     mode http
     bind :80
-    http-request set-var(req.path) path
-    http-request set-var(req.host) hdr(host),field(1,:),lower
-    http-request set-var(req.base) var(req.host),concat(\#,req.path)
-    http-request set-header X-Forwarded-Proto http
-    http-request del-header X-SSL-Client-CN
-    http-request del-header X-SSL-Client-DN
-    http-request del-header X-SSL-Client-SHA1
-    http-request del-header X-SSL-Client-SHA2
-    http-request del-header X-SSL-Client-Cert` + test.expected + `
+    <<set-req-base>>
+    <<http-headers>>` + test.expected + `
     http-request set-var(req.backend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_host__begin.map)
     use_backend %[var(req.backend)] if { var(req.backend) -m found }
     default_backend _error404
-frontend _front_https
-    mode http
-    bind :443 ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_bind_crt.list ca-ignore-err all crt-ignore-err all
-    http-request set-var(req.path) path
-    http-request set-var(req.host) hdr(host),field(1,:),lower
-    http-request set-var(req.base) var(req.host),concat(\#,req.path)` + test.expected + `
-    http-request set-var(req.hostbackend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_https_host__begin.map)
-    http-request set-header X-Forwarded-Proto https
-    http-request del-header X-SSL-Client-CN
-    http-request del-header X-SSL-Client-DN
-    http-request del-header X-SSL-Client-SHA1
-    http-request del-header X-SSL-Client-SHA2
-    http-request del-header X-SSL-Client-Cert
-    use_backend %[var(req.hostbackend)] if { var(req.hostbackend) -m found }
-    default_backend _error404
 <<support>>
 `)
 		for file, content := range test.expMaps {
@@ -4764,10 +5015,9 @@ func TestInstanceSyslog(t *testing.T) {
 	c := setup(t)
 	defer c.teardown()
 
-	var h *hatypes.Host
-	var b = c.config.Backends().AcquireBackend("d1", "app", "8080")
+	b := c.config.Backends().AcquireBackend("d1", "app", "8080")
 	b.Endpoints = []*hatypes.Endpoint{endpointS1}
-	h = c.config.Hosts().AcquireHost("d1.local")
+	h := c.httpFrontend(80).AcquireHost("d1.local")
 	h.AddPath(b, "/", hatypes.MatchBegin)
 
 	tcpPort1, tcpHost1 := c.config.tcpservices.AcquireTCPService("<default>:7001")
@@ -4828,15 +5078,6 @@ frontend _front_http
     http-request set-var(req.backend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_host__begin.map)
     use_backend %[var(req.backend)] if { var(req.backend) -m found }
     default_backend _error404
-frontend _front_https
-    mode http
-    bind :443 ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_bind_crt.list ca-ignore-err all crt-ignore-err all
-    option httplog
-    <<set-req-base>>
-    http-request set-var(req.hostbackend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_https_host__begin.map)
-    <<https-headers>>
-    use_backend %[var(req.hostbackend)] if { var(req.hostbackend) -m found }
-    default_backend _error404
 <<support>>
 `)
 	c.logger.CompareLogging(defaultLogging)
@@ -4903,20 +5144,6 @@ backend _peers_default_app1_8080_proxy03
 backend default_app1_8080
     mode http
 <<backends-default>>
-frontend _front_http
-    mode http
-    bind :80
-    <<set-req-base>>
-    <<http-headers>>
-    use_backend %[var(req.backend)] if { var(req.backend) -m found }
-    default_backend _error404
-frontend _front_https
-    mode http
-    bind :443 ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_bind_crt.list ca-ignore-err all crt-ignore-err all
-    <<set-req-base>>
-    <<https-headers>>
-    use_backend %[var(req.hostbackend)] if { var(req.hostbackend) -m found }
-    default_backend _error404
 <<support>>
 `)
 
@@ -4975,23 +5202,25 @@ func TestDNS(t *testing.T) {
 		},
 	}
 
+	f := c.httpFrontend(80)
+
 	b = c.config.Backends().AcquireBackend("d1", "app", "8080")
 	b.Endpoints = []*hatypes.Endpoint{endpointS21, endpointS22}
 	b.Resolver = "k8s"
-	h = c.config.Hosts().AcquireHost("d1.local")
+	h = f.AcquireHost("d1.local")
 	h.AddPath(b, "/", hatypes.MatchBegin)
 
 	b = c.config.Backends().AcquireBackend("d2", "app", "http")
 	b.Endpoints = []*hatypes.Endpoint{endpointS21, endpointS22}
 	b.Resolver = "k8s"
-	h = c.config.Hosts().AcquireHost("d2.local")
+	h = f.AcquireHost("d2.local")
 	h.AddPath(b, "/", hatypes.MatchBegin)
 
 	b = c.config.Backends().AcquireBackend("d3", "app", "http")
 	b.DNSPort = "named"
 	b.Endpoints = []*hatypes.Endpoint{endpointS21, endpointS22}
 	b.Resolver = "k8s"
-	h = c.config.Hosts().AcquireHost("d3.local")
+	h = f.AcquireHost("d3.local")
 	h.AddPath(b, "/", hatypes.MatchBegin)
 
 	c.Update()
@@ -5016,7 +5245,8 @@ backend d3_app_http
     mode http
     server-template srv 2 _named._tcp.app.d3.svc.cluster.local resolvers k8s resolve-prefer ipv4 init-addr none weight 1
 <<backends-default>>
-<<frontends-default>>
+<<frontend-http>>
+    default_backend _error404
 <<support>>
 `)
 	c.logger.CompareLogging(defaultLogging)
@@ -5091,17 +5321,17 @@ userlist default_auth2
 	for _, test := range testCase {
 		c := setup(t)
 
-		var h *hatypes.Host
-		var b = c.config.Backends().AcquireBackend("d1", "app", "8080")
+		f := c.httpFrontend(80)
+		b := c.config.Backends().AcquireBackend("d1", "app", "8080")
 		b.Endpoints = []*hatypes.Endpoint{endpointS1}
-		h = c.config.Hosts().AcquireHost("d1.local")
+		h := f.AcquireHost("d1.local")
 		h.AddPath(b, "/", hatypes.MatchBegin)
-		h.AddPath(b, "/admin", hatypes.MatchBegin)
+		path := h.AddPath(b, "/admin", hatypes.MatchBegin)
 
 		for _, list := range test.lists {
 			c.config.Userlists().Replace(list.name, list.users)
 		}
-		b.FindBackendPath(h.FindPath("/admin")[0].Link).AuthHTTP = hatypes.AuthHTTP{
+		path.AuthHTTP = hatypes.AuthHTTP{
 			UserlistName: test.listname,
 			Realm:        test.realm,
 		}
@@ -5119,11 +5349,12 @@ backend d1_app_8080
     mode http
     # path01 = d1.local/
     # path02 = d1.local/admin
-    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_idpath__begin.map)
+    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_front_http_req__begin.map)
     http-request auth` + realm + ` if { var(txn.pathID) -m str path02 } !{ http_auth(` + test.listname + `) }
     server s1 172.17.0.11:8080 weight 100
 <<backends-default>>
-<<frontends-default>>
+<<frontend-http>>
+    default_backend _error404
 <<support>>
 `)
 		c.logger.CompareLogging(defaultLogging)
@@ -5168,10 +5399,10 @@ frontend _front_http
 	for _, test := range testCases {
 		c := setup(t)
 
-		var h *hatypes.Host
-		var b = c.config.Backends().AcquireBackend("d1", "app", "8080")
+		f := c.httpFrontend(80)
+		b := c.config.Backends().AcquireBackend("d1", "app", "8080")
 		b.Endpoints = []*hatypes.Endpoint{endpointS1}
-		h = c.config.Hosts().AcquireHost("d1.local")
+		h := f.AcquireHost("d1.local")
 		h.AddPath(b, "/", hatypes.MatchBegin)
 
 		acme := &c.config.Global().Acme
@@ -5191,8 +5422,6 @@ backend _acme_challenge
     mode http
     server _acme_server unix@/run/acme.sock
 <<backends-default>>` + test.expected + `
-<<frontend-https>>
-    default_backend _error404
 <<support>>
 `)
 		c.logger.CompareLogging(defaultLogging)
@@ -5279,13 +5508,7 @@ frontend prometheus
 		c.checkConfig(`
 <<global>>
 <<defaults>>
-backend _error404
-    mode http
-    http-request use-service lua.send-404
-<<frontend-http-clean>>
-    default_backend _error404
-<<frontend-https-clean>>
-    default_backend _error404
+<<backends-default>>
 listen stats
     mode http` + test.expectedStats + `
     stats enable
@@ -5380,7 +5603,7 @@ func TestModSecurity(t *testing.T) {
 			backendExp: `
     # path02 = d1.local/
     # path01 = d1.local/sub
-    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_idpath__begin.map)
+    http-request set-var(txn.pathID) var(req.base),lower,map_beg(/etc/haproxy/maps/_back_d1_app_8080_front_http_req__begin.map)
     filter spoe engine modsecurity config /etc/haproxy/spoe-modsecurity.conf
     http-request deny if { var(txn.modsec.code) -m int gt 0 } { var(txn.pathID) -m str path01 }`,
 			modsecExp: `
@@ -5443,14 +5666,16 @@ spoe-message coraza-req
 		c := setup(t)
 
 		var h *hatypes.Host
-		var b = c.config.Backends().AcquireBackend("d1", "app", "8080")
+
+		f := c.httpFrontend(80)
+
+		b := c.config.Backends().AcquireBackend("d1", "app", "8080")
 		b.Endpoints = []*hatypes.Endpoint{endpointS1}
-		h = c.config.Hosts().AcquireHost("d1.local")
+		h = f.AcquireHost("d1.local")
 		if test.path == "" {
 			test.path = "/"
 		}
-		h.AddPath(b, test.path, hatypes.MatchBegin)
-		b.FindBackendPath(h.FindPath(test.path)[0].Link).WAF = hatypes.WAF{
+		h.AddPath(b, test.path, hatypes.MatchBegin).WAF = hatypes.WAF{
 			Module: test.waf,
 			Mode:   test.wafmode,
 		}
@@ -5482,7 +5707,8 @@ backend d1_app_8080
     mode http` + test.backendExp + `
     server s1 172.17.0.11:8080 weight 100
 <<backends-default>>
-<<frontends-default>>
+<<frontend-http>>
+    default_backend _error404
 <<support>>` + modsec)
 		} else {
 			c.checkConfig(`
@@ -5492,7 +5718,8 @@ backend d1_app_8080
     mode http` + test.backendExp + `
     server s1 172.17.0.11:8080 weight 100
 <<backends-default>>
-<<frontends-default>>
+<<frontend-http>>
+    default_backend _error404
 <<support>>` + modsec)
 		}
 		if test.modsecAgentExp != "" {
@@ -5512,17 +5739,20 @@ func TestInstanceWildcardHostname(t *testing.T) {
 	defer c.teardown()
 
 	var h *hatypes.Host
+	var b *hatypes.Backend
 
-	var b = c.config.Backends().AcquireBackend("d1", "app", "8080")
-	h = c.config.Hosts().AcquireHost("d1.local")
+	f := c.httpsFrontend(443)
+
+	b = c.config.Backends().AcquireBackend("d1", "app", "8080")
+	h = f.AcquireHost("d1.local")
 	h.TLS.TLSFilename = "/var/haproxy/ssl/certs/default.pem"
 	h.TLS.TLSHash = "0"
 	h.AddPath(b, "/", hatypes.MatchBegin)
-	h = c.config.Hosts().AcquireHost("*.app.d1.local")
+	h = f.AcquireHost("*.app.d1.local")
 	h.TLS.TLSFilename = "/var/haproxy/ssl/certs/default.pem"
 	h.TLS.TLSHash = "0"
 	h.AddPath(b, "/", hatypes.MatchBegin)
-	h = c.config.Hosts().AcquireHost("*.sub.d1.local")
+	h = f.AcquireHost("*.sub.d1.local")
 	h.TLS.TLSFilename = "/var/haproxy/ssl/certs/default.pem"
 	h.TLS.TLSHash = "0"
 	h.AddPath(b, "/", hatypes.MatchBegin)
@@ -5536,7 +5766,7 @@ func TestInstanceWildcardHostname(t *testing.T) {
 	b.Endpoints = []*hatypes.Endpoint{endpointS1}
 
 	b = c.config.Backends().AcquireBackend("d2", "app", "8080")
-	h = c.config.Hosts().AcquireHost("*.d2.local")
+	h = f.AcquireHost("*.d2.local")
 	h.AddPath(b, "/", hatypes.MatchBegin)
 	h.RootRedirect = "/app"
 	b.Endpoints = []*hatypes.Endpoint{endpointS21}
@@ -5548,7 +5778,6 @@ func TestInstanceWildcardHostname(t *testing.T) {
 backend d1_app_8080
     mode http
     acl https-request ssl_fc
-    acl local-offload ssl_fc
     http-request redirect scheme https if !https-request
     http-request set-header X-SSL-Client-CN   %{+Q}[ssl_c_s_dn(cn)]
     http-request set-header X-SSL-Client-DN   %{+Q}[ssl_c_s_dn]
@@ -5558,30 +5787,19 @@ backend d2_app_8080
     mode http
     server s21 172.17.0.121:8080 weight 100
 <<backends-default>>
-frontend _front_http
-    mode http
-    bind :80
-    <<set-req-base>>
-    http-request set-var(req.rootredir) var(req.host),map_reg(/etc/haproxy/maps/_front_redir_fromroot__regex.map)
-    http-request redirect location %[var(req.rootredir)] if { path / } { var(req.rootredir) -m found }
-    <<http-headers>>
-    http-request set-var(req.backend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_host__begin.map)
-    http-request set-var(req.backend) var(req.base),map_reg(/etc/haproxy/maps/_front_http_host__regex.map) if !{ var(req.backend) -m found }
-    use_backend %[var(req.backend)] if { var(req.backend) -m found }
-    default_backend _error404
 frontend _front_https
     mode http
-    bind :443 ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_bind_crt.list ca-ignore-err all crt-ignore-err all
+    bind :443 ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_https_bind_crt.list ca-ignore-err all crt-ignore-err all
     <<set-req-base>>
     http-request set-var(req.hostbackend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_https_host__begin.map)
     http-request set-var(req.hostbackend) var(req.base),map_reg(/etc/haproxy/maps/_front_https_host__regex.map) if !{ var(req.hostbackend) -m found }
-    http-request set-var(req.rootredir) var(req.host),map_reg(/etc/haproxy/maps/_front_redir_fromroot__regex.map)
+    http-request set-var(req.rootredir) var(req.host),map_reg(/etc/haproxy/maps/_front_https_redir_fromroot__regex.map)
     http-request redirect location %[var(req.rootredir)] if { path / } { var(req.rootredir) -m found }
     <<https-headers>>
     acl tls-has-crt ssl_c_used
     acl tls-has-invalid-crt ssl_c_verify gt 0
-    acl tls-check-crt ssl_fc_sni -i -m reg -f /etc/haproxy/maps/_front_tls_auth__regex.list
-    http-request set-var(req.tls_invalidcrt_redir) ssl_fc_sni,lower,map_reg(/etc/haproxy/maps/_front_tls_invalidcrt_pages__regex.map,_internal) if tls-has-invalid-crt tls-check-crt
+    acl tls-check-crt ssl_fc_sni -i -m reg -f /etc/haproxy/maps/_front_https_tls_auth__regex.list
+    http-request set-var(req.tls_invalidcrt_redir) ssl_fc_sni,lower,map_reg(/etc/haproxy/maps/_front_https_tls_invalidcrt_pages__regex.map,_internal) if tls-has-invalid-crt tls-check-crt
     http-request redirect location %[var(req.tls_invalidcrt_redir)] code 303 if { var(req.tls_invalidcrt_redir) -m found } !{ var(req.tls_invalidcrt_redir) -m str _internal }
     http-request use-service lua.send-421 if tls-has-crt { ssl_fc_has_sni } !{ ssl_fc_sni,strcmp(req.host) eq 0 }
     http-request use-service lua.send-495 if { var(req.tls_invalidcrt_redir) -m str _internal }
@@ -5591,15 +5809,7 @@ frontend _front_https
 <<support>>
 `)
 
-	c.checkMap("_front_http_host__begin.map", `
-d1.local#/ d1_app_8080
-`)
-	c.checkMap("_front_http_host__regex.map", `
-^[^.]+\.app\.d1\.local#/ d1_app_8080
-^[^.]+\.sub\.d1\.local#/ d1_app_8080
-^[^.]+\.d2\.local#/ d2_app_8080
-`)
-	c.checkMap("_front_redir_fromroot__regex.map", `
+	c.checkMap("_front_https_redir_fromroot__regex.map", `
 ^[^.]+\.d2\.local$ /app
 `)
 	c.checkMap("_front_https_host__begin.map", `
@@ -5610,13 +5820,13 @@ d1.local#/ d1_app_8080
 ^[^.]+\.sub\.d1\.local#/ d1_app_8080
 ^[^.]+\.d2\.local#/ d2_app_8080
 `)
-	c.checkMap("_front_redir_fromroot__regex.map", `
+	c.checkMap("_front_https_redir_fromroot__regex.map", `
 ^[^.]+\.d2\.local$ /app
 `)
-	c.checkMap("_front_tls_auth__regex.list", `
+	c.checkMap("_front_https_tls_auth__regex.list", `
 ^[^.]+\.sub\.d1\.local$
 `)
-	c.checkMap("_front_tls_invalidcrt_pages__regex.map", `
+	c.checkMap("_front_https_tls_invalidcrt_pages__regex.map", `
 ^[^.]+\.sub\.d1\.local$ http://sub.d1.local/error.html
 `)
 
@@ -5627,12 +5837,11 @@ func TestCAVerifySkip(t *testing.T) {
 	c := setup(t)
 	defer c.teardown()
 
-	var h *hatypes.Host
-
-	var b = c.config.Backends().AcquireBackend("d1", "app", "8080")
+	f := c.httpsFrontend(443)
+	b := c.config.Backends().AcquireBackend("d1", "app", "8080")
 	b.Endpoints = []*hatypes.Endpoint{endpointS1}
 
-	h = c.config.Hosts().AcquireHost("d1.local")
+	h := f.AcquireHost("d1.local")
 	h.TLS.TLSFilename = "/var/haproxy/ssl/certs/default.pem"
 	h.TLS.TLSHash = "0"
 	h.AddPath(b, "/", hatypes.MatchBegin)
@@ -5646,23 +5855,14 @@ func TestCAVerifySkip(t *testing.T) {
 <<defaults>>
 backend d1_app_8080
     mode http
-    acl local-offload ssl_fc
-    http-request set-header X-SSL-Client-CN   %{+Q}[ssl_c_s_dn(cn)]   if local-offload
-    http-request set-header X-SSL-Client-DN   %{+Q}[ssl_c_s_dn]       if local-offload
-    http-request set-header X-SSL-Client-SHA1 %{+Q}[ssl_c_sha1,hex]   if local-offload
+    http-request set-header X-SSL-Client-CN   %{+Q}[ssl_c_s_dn(cn)]
+    http-request set-header X-SSL-Client-DN   %{+Q}[ssl_c_s_dn]
+    http-request set-header X-SSL-Client-SHA1 %{+Q}[ssl_c_sha1,hex]
     server s1 172.17.0.11:8080 weight 100
 <<backends-default>>
-frontend _front_http
-    mode http
-    bind :80
-    <<set-req-base>>
-    <<http-headers>>
-    http-request set-var(req.backend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_host__begin.map)
-    use_backend %[var(req.backend)] if { var(req.backend) -m found }
-    default_backend _error404
 frontend _front_https
     mode http
-    bind :443 ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_bind_crt.list ca-ignore-err all crt-ignore-err all
+    bind :443 ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_https_bind_crt.list ca-ignore-err all crt-ignore-err all
     <<set-req-base>>
     http-request set-var(req.hostbackend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_https_host__begin.map)
     <<https-headers>>
@@ -5675,10 +5875,7 @@ frontend _front_https
 <<support>>
 `)
 
-	c.checkMap("_front_http_host__begin.map", `
-d1.local#/ d1_app_8080
-`)
-	c.checkMap("_front_bind_crt.list", `
+	c.checkMap("_front_https_bind_crt.list", `
 /var/haproxy/ssl/certs/default.pem !*
 /var/haproxy/ssl/certs/default.pem [ca-file /var/haproxy/ssl/ca/d1.local.pem verify optional] d1.local
 `)
@@ -5697,20 +5894,23 @@ func TestShards(t *testing.T) {
 	defer c.teardown()
 
 	var h *hatypes.Host
+	var b *hatypes.Backend
 
-	var b = c.config.Backends().AcquireBackend("d1", "app", "8080")
+	f := c.httpFrontend(80)
+
+	b = c.config.Backends().AcquireBackend("d1", "app", "8080")
 	b.Endpoints = []*hatypes.Endpoint{endpointS1}
-	h = c.config.Hosts().AcquireHost("d1.local")
+	h = f.AcquireHost("d1.local")
 	h.AddPath(b, "/", hatypes.MatchBegin)
 
 	b = c.config.Backends().AcquireBackend("d2", "app", "8080")
 	b.Endpoints = []*hatypes.Endpoint{endpointS21}
-	h = c.config.Hosts().AcquireHost("d2.local")
+	h = f.AcquireHost("d2.local")
 	h.AddPath(b, "/", hatypes.MatchBegin)
 
 	b = c.config.Backends().AcquireBackend("d3", "app", "8080")
 	b.Endpoints = []*hatypes.Endpoint{endpointS31}
-	h = c.config.Hosts().AcquireHost("d3.local")
+	h = f.AcquireHost("d3.local")
 	h.AddPath(b, "/", hatypes.MatchBegin)
 
 	c.Update()
@@ -5718,7 +5918,8 @@ func TestShards(t *testing.T) {
 <<global>>
 <<defaults>>
 <<backends-default>>
-<<frontends-default>>
+<<frontend-http>>
+    default_backend _error404
 <<support>>
 `)
 
@@ -5847,7 +6048,7 @@ func setupOptions(options testOptions) *testConfig {
 		t.Errorf("error parsing modsecurity.tmpl: %v", err)
 	}
 	config := instance.Config().(*config)
-	config.frontend.DefaultCrtFile = "/var/haproxy/ssl/certs/default.pem"
+	config.frontends.DefaultCrtFile = "/var/haproxy/ssl/certs/default.pem"
 	c := &testConfig{
 		t:        t,
 		logger:   logger,
@@ -5868,8 +6069,6 @@ func (c *testConfig) teardown() {
 
 func (c *testConfig) configGlobal(global *hatypes.Global) {
 	global.AdminSocket = "/var/run/haproxy.sock"
-	global.Bind.HTTPBind = ":80"
-	global.Bind.HTTPSBind = ":443"
 	global.Cookie.Key = "Ingress"
 	global.DefaultBackendRedirCode = 301
 	global.Healthz.Port = 10253
@@ -5898,6 +6097,16 @@ func (c *testConfig) configGlobal(global *hatypes.Global) {
 	global.UseHTX = true
 }
 
+func (c *testConfig) httpFrontend(port int32) *hatypes.Frontend {
+	f := c.config.frontends.AcquireFrontend(port, false)
+	return f
+}
+
+func (c *testConfig) httpsFrontend(port int32) *hatypes.Frontend {
+	f := c.config.frontends.AcquireFrontend(port, true)
+	return f
+}
+
 var endpointS0 = &hatypes.Endpoint{
 	Name:    "s0",
 	IP:      "172.17.0.99",
@@ -6037,15 +6246,11 @@ func (c *testConfig) checkConfigFile(expected, fileName string) {
     <<http-headers>>
     http-request set-var(req.backend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_host__begin.map)
     use_backend %[var(req.backend)] if { var(req.backend) -m found }`,
-		"<<frontend-http-match-4>>": `frontend _front_http
+		"<<frontend-fronting-http>>": `frontend _front_http
     mode http
     bind :80
     <<set-req-base>>
-    <<http-headers>>
-    http-request set-var(req.backend) var(req.base),map_str(/etc/haproxy/maps/_front_http_host__exact.map)
-    http-request set-var(req.backend) var(req.base),map_dir(/etc/haproxy/maps/_front_http_host__prefix_02.map) if !{ var(req.backend) -m found }
-    http-request set-var(req.backend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_host__begin.map) if !{ var(req.backend) -m found }
-    http-request set-var(req.backend) var(req.base),map_reg(/etc/haproxy/maps/_front_http_host__regex.map) if !{ var(req.backend) -m found }
+    http-request set-var(req.backend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_host__begin.map)
     use_backend %[var(req.backend)] if { var(req.backend) -m found }`,
 		"<<frontend-http-clean>>": `frontend _front_http
     mode http
@@ -6055,34 +6260,20 @@ func (c *testConfig) checkConfigFile(expected, fileName string) {
     use_backend %[var(req.backend)] if { var(req.backend) -m found }`,
 		"<<frontend-https>>": `frontend _front_https
     mode http
-    bind :443 ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_bind_crt.list ca-ignore-err all crt-ignore-err all
+    bind :443 ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_https_bind_crt.list ca-ignore-err all crt-ignore-err all
     <<set-req-base>>
     http-request set-var(req.hostbackend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_https_host__begin.map)
     <<https-headers>>
-    use_backend %[var(req.hostbackend)] if { var(req.hostbackend) -m found }`,
-		"<<frontend-https-match-4>>": `frontend _front_https
-    mode http
-    bind :443 ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_bind_crt.list ca-ignore-err all crt-ignore-err all
-    <<set-req-base>>
-    http-request set-var(req.hostbackend) var(req.base),map_str(/etc/haproxy/maps/_front_https_host__exact.map)
-    http-request set-var(req.hostbackend) var(req.base),map_dir(/etc/haproxy/maps/_front_https_host__prefix_02.map) if !{ var(req.hostbackend) -m found }
-    http-request set-var(req.hostbackend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_https_host__begin.map) if !{ var(req.hostbackend) -m found }
-    http-request set-var(req.hostbackend) var(req.base),map_reg(/etc/haproxy/maps/_front_https_host__regex.map) if !{ var(req.hostbackend) -m found }
-    <<https-headers>>
     use_backend %[var(req.hostbackend)] if { var(req.hostbackend) -m found }`,
 		"<<frontend-https-clean>>": `frontend _front_https
     mode http
-    bind :443 ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_bind_crt.list ca-ignore-err all crt-ignore-err all
+    bind :443 ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_https_bind_crt.list ca-ignore-err all crt-ignore-err all
     <<set-req-base>>
     <<https-headers>>
     use_backend %[var(req.hostbackend)] if { var(req.hostbackend) -m found }`,
 		"<<frontends-default>>": `<<frontend-http>>
     default_backend _error404
 <<frontend-https>>
-    default_backend _error404`,
-		"<<frontends-default-match-4>>": `<<frontend-http-match-4>>
-    default_backend _error404
-<<frontend-https-match-4>>
     default_backend _error404`,
 		"<<support>>": `listen stats
     mode http
diff --git a/pkg/haproxy/types/backend_test.go b/pkg/haproxy/types/backend_test.go
index b193163f..f81c7c0f 100644
--- a/pkg/haproxy/types/backend_test.go
+++ b/pkg/haproxy/types/backend_test.go
@@ -17,73 +17,54 @@ limitations under the License.
 package types
 
 import (
+	"fmt"
 	"net"
 	"reflect"
 	"strings"
 	"testing"
+
+	"github.com/stretchr/testify/assert"
 )
 
 func TestAddBackendPath(t *testing.T) {
 	testCases := []struct {
 		input    []string
-		expected []*BackendPath
+		expected []*Path
 	}{
 		// 0
 		{
 			input: []string{"/"},
-			expected: []*BackendPath{
-				{ID: "path01", Link: CreateHostPathLink("d1.local", "/", MatchBegin)},
+			expected: []*Path{
+				{ID: "path01", Link: CreatePathLink("/", MatchBegin)},
 			},
 		},
 		// 1
-		{
-			input: []string{"/app", "/app"},
-			expected: []*BackendPath{
-				{ID: "path01", Link: CreateHostPathLink("d1.local", "/app", MatchBegin)},
-			},
-		},
-		// 2
 		{
 			input: []string{"/app", "/root"},
-			expected: []*BackendPath{
-				{ID: "path01", Link: CreateHostPathLink("d1.local", "/app", MatchBegin)},
-				{ID: "path02", Link: CreateHostPathLink("d1.local", "/root", MatchBegin)},
-			},
-		},
-		// 3
-		{
-			input: []string{"/app", "/root", "/root"},
-			expected: []*BackendPath{
-				{ID: "path01", Link: CreateHostPathLink("d1.local", "/app", MatchBegin)},
-				{ID: "path02", Link: CreateHostPathLink("d1.local", "/root", MatchBegin)},
+			expected: []*Path{
+				{ID: "path01", Link: CreatePathLink("/app", MatchBegin)},
+				{ID: "path02", Link: CreatePathLink("/root", MatchBegin)},
 			},
 		},
-		// 4
-		{
-			input: []string{"/app", "/root", "/app"},
-			expected: []*BackendPath{
-				{ID: "path01", Link: CreateHostPathLink("d1.local", "/app", MatchBegin)},
-				{ID: "path02", Link: CreateHostPathLink("d1.local", "/root", MatchBegin)},
-			},
-		},
-		// 5
+		// 2
 		{
 			input: []string{"/", "/app", "/root"},
-			expected: []*BackendPath{
-				{ID: "path01", Link: CreateHostPathLink("d1.local", "/", MatchBegin)},
-				{ID: "path02", Link: CreateHostPathLink("d1.local", "/app", MatchBegin)},
-				{ID: "path03", Link: CreateHostPathLink("d1.local", "/root", MatchBegin)},
+			expected: []*Path{
+				{ID: "path01", Link: CreatePathLink("/", MatchBegin)},
+				{ID: "path02", Link: CreatePathLink("/app", MatchBegin)},
+				{ID: "path03", Link: CreatePathLink("/root", MatchBegin)},
 			},
 		},
 	}
 	for i, test := range testCases {
 		b := &Backend{}
 		for _, p := range test.input {
-			b.AddBackendPath(CreateHostPathLink("d1.local", p, MatchBegin))
-		}
-		if !reflect.DeepEqual(b.Paths, test.expected) {
-			t.Errorf("backend.Paths differs on %d - actual: %v - expected: %v", i, b.Paths, test.expected)
+			path := &Path{
+				Link: CreatePathLink(p, MatchBegin),
+			}
+			b.AddPath(path)
 		}
+		assert.Equal(t, test.expected, b.Paths, fmt.Sprintf("backend.Paths differs on %d", i))
 	}
 }
 
@@ -165,7 +146,7 @@ func TestCreatePathConfig(t *testing.T) {
 		config interface{}
 	}
 	testCases := []struct {
-		paths    []*BackendPath
+		paths    []*Path
 		filter   string
 		expected map[string][]pathConfig
 	}{
@@ -178,7 +159,7 @@ func TestCreatePathConfig(t *testing.T) {
 		},
 		// 1
 		{
-			paths:  []*BackendPath{{ID: "path1"}},
+			paths:  []*Path{{ID: "path1"}},
 			filter: "SSLRedirect",
 			expected: map[string][]pathConfig{
 				"SSLRedirect": {
@@ -188,7 +169,7 @@ func TestCreatePathConfig(t *testing.T) {
 		},
 		// 2
 		{
-			paths: []*BackendPath{
+			paths: []*Path{
 				{ID: "path1", HSTS: HSTS{Enabled: true, MaxAge: 10}},
 				{ID: "path2", HSTS: HSTS{Enabled: true, MaxAge: 10}},
 				{ID: "path3", HSTS: HSTS{Enabled: true, MaxAge: 20}},
@@ -209,7 +190,7 @@ func TestCreatePathConfig(t *testing.T) {
 		},
 		// 3
 		{
-			paths: []*BackendPath{
+			paths: []*Path{
 				{ID: "path1", HSTS: HSTS{Enabled: true, MaxAge: 10}, AllowedIPHTTP: AccessConfig{Rule: []string{"10.0.0.0/8"}}},
 				{ID: "path2", HSTS: HSTS{Enabled: true, MaxAge: 20}, AllowedIPHTTP: AccessConfig{Rule: []string{"10.0.0.0/8"}}},
 				{ID: "path3", HSTS: HSTS{Enabled: true, MaxAge: 20}},
@@ -241,6 +222,9 @@ func TestCreatePathConfig(t *testing.T) {
 	}
 	for i, test := range testCases {
 		c := setup(t)
+		for _, path := range test.paths {
+			path.Link = CreatePathLink("/", MatchPrefix)
+		}
 		backend := Backend{Paths: test.paths}
 		actualConfig := map[string][]pathConfig{}
 		for _, attr := range strings.Split(test.filter, ",") {
@@ -304,9 +288,9 @@ func TestPathIDs(t *testing.T) {
 	}
 	for i, test := range testCases {
 		c := setup(t)
-		paths := make([]*BackendPath, len(test.paths))
+		paths := make([]*Path, len(test.paths))
 		for j, path := range test.paths {
-			paths[j] = &BackendPath{ID: path}
+			paths[j] = &Path{ID: path}
 		}
 		b := BackendPathConfig{
 			items: []*BackendPathItem{{paths: paths}, {}},
@@ -366,3 +350,64 @@ func TestEndpointDeduplication(t *testing.T) {
 	c.compareObjects("len(ep)", 0, len(b.Endpoints), 6)
 	c.teardown()
 }
+
+func TestHasInPath(t *testing.T) {
+	testCases := map[string]struct {
+		has []bool
+		exp Has
+	}{
+		"test01": {
+			exp: HasNone,
+		},
+		"test02": {
+			has: []bool{false},
+			exp: HasNone,
+		},
+		"test03": {
+			has: []bool{false, false},
+			exp: HasNone,
+		},
+		"test04": {
+			has: []bool{false, false, true},
+			exp: HasSome,
+		},
+		"test05": {
+			has: []bool{true},
+			exp: HasOnly,
+		},
+		"test06": {
+			has: []bool{true, true},
+			exp: HasOnly,
+		},
+		"test07": {
+			has: []bool{true, true, false},
+			exp: HasSome,
+		},
+		"test08": {
+			has: []bool{true, false, true},
+			exp: HasSome,
+		},
+		"test09": {
+			has: []bool{false, true, false},
+			exp: HasSome,
+		},
+	}
+	for name, test := range testCases {
+		t.Run(name, func(t *testing.T) {
+			c := setup(t)
+			defer c.teardown()
+			b := createBackend(0, "default", "server", "8080")
+			for i, has := range test.has {
+				path := &Path{
+					Link:        CreatePathLink(fmt.Sprintf("/%d", i), MatchExact),
+					SSLRedirect: has,
+				}
+				b.AddPath(path)
+			}
+			has := b.hasInPath(func(path *Path) bool {
+				return path.SSLRedirect
+			})
+			assert.Equal(t, test.exp, has, "0=HasNone; 1=HasSome; 2=HasOnly")
+		})
+	}
+}
diff --git a/pkg/haproxy/types/frontend_test.go b/pkg/haproxy/types/frontend_test.go
index bd8be38c..e397abf3 100644
--- a/pkg/haproxy/types/frontend_test.go
+++ b/pkg/haproxy/types/frontend_test.go
@@ -55,16 +55,14 @@ func TestAcquireAuthFrontendLocalPort(t *testing.T) {
 	}
 	for i, test := range testCases {
 		c := setup(t)
-		frontend := &Frontend{
-			AuthProxy: AuthProxy{
-				RangeStart: test.rangeStart,
-				RangeEnd:   test.rangeEnd,
-			},
+		authProxy := AuthProxy{
+			RangeStart: test.rangeStart,
+			RangeEnd:   test.rangeEnd,
 		}
 		authBackendNames := make([]string, len(test.backends))
 		errs := make([]string, len(test.backends))
 		for j, back := range test.backends {
-			authBackendName, err := frontend.AcquireAuthBackendName(BackendID{Name: back})
+			authBackendName, err := authProxy.AcquireAuthBackendName(BackendID{Name: back})
 			authBackendNames[j] = authBackendName
 			if err != nil {
 				errs[j] = err.Error()
@@ -115,15 +113,15 @@ func TestRemoveAuthBackendExcept(t *testing.T) {
 	}
 	for i, test := range testCases {
 		c := setup(t)
-		frontend := Frontend{}
+		authProxy := AuthProxy{}
 		for _, input := range test.input {
-			frontend.AuthProxy.BindList = append(frontend.AuthProxy.BindList, &AuthProxyBind{
+			authProxy.BindList = append(authProxy.BindList, &AuthProxyBind{
 				AuthBackendName: input,
 			})
 		}
-		frontend.RemoveAuthBackendExcept(test.used)
+		authProxy.RemoveAuthBackendExcept(test.used)
 		actual := []string{}
-		for _, item := range frontend.AuthProxy.BindList {
+		for _, item := range authProxy.BindList {
 			actual = append(actual, item.AuthBackendName)
 		}
 		c.compareObjects("remove", i, actual, test.expected)
@@ -173,15 +171,15 @@ func TestRemoveAuthBackendByTarget(t *testing.T) {
 	}
 	for i, test := range testCases {
 		c := setup(t)
-		frontend := Frontend{}
+		authProxy := AuthProxy{}
 		for _, input := range test.input {
-			frontend.AuthProxy.BindList = append(frontend.AuthProxy.BindList, &AuthProxyBind{
+			authProxy.BindList = append(authProxy.BindList, &AuthProxyBind{
 				Backend: input,
 			})
 		}
-		frontend.RemoveAuthBackendByTarget(test.removed)
+		authProxy.RemoveAuthBackendByTarget(test.removed)
 		actual := []BackendID{}
-		for _, item := range frontend.AuthProxy.BindList {
+		for _, item := range authProxy.BindList {
 			actual = append(actual, item.Backend)
 		}
 		c.compareObjects("remove", i, actual, test.expected)
diff --git a/pkg/haproxy/types/host_test.go b/pkg/haproxy/types/host_test.go
index 11e57947..927ae686 100644
--- a/pkg/haproxy/types/host_test.go
+++ b/pkg/haproxy/types/host_test.go
@@ -22,37 +22,41 @@ import (
 )
 
 func TestCreatePathLink(t *testing.T) {
-	l1 := CreateHostPathLink("domain.local", "/app", MatchBegin)
-	l2 := CreateHostPathLink("domain.local", "/app", MatchBegin)
+	f := (&Frontends{}).AcquireFrontend(8000, false)
+	h0 := f.AcquireHost("domain.local")
+	h1 := f.AcquireHost("domain1.local")
+	h2 := f.AcquireHost("domain2.local")
+	l1 := CreatePathLink("/app", MatchBegin).WithHTTPHost(h0)
+	l2 := CreatePathLink("/app", MatchBegin).WithHTTPHost(h0)
 	if !l1.Equals(l2) {
 		t.Errorf("two distinct path links with same host and path should match")
 	}
-	l3 := CreateHostPathLink("domain1.local", "/app", MatchBegin)
-	l4 := CreateHostPathLink("domain2.local", "/app", MatchBegin)
+	l3 := CreatePathLink("/app", MatchBegin).WithHTTPHost(h1)
+	l4 := CreatePathLink("/app", MatchBegin).WithHTTPHost(h2)
 	if l3.Equals(l4) {
 		t.Errorf("path links with distinct domains should not match")
 	}
-	l5 := CreateHostPathLink("domain.local", "/app1", MatchBegin)
-	l6 := CreateHostPathLink("domain.local", "/app2", MatchBegin)
+	l5 := CreatePathLink("/app1", MatchBegin).WithHTTPHost(h0)
+	l6 := CreatePathLink("/app2", MatchBegin).WithHTTPHost(h0)
 	if l5.Equals(l6) {
 		t.Errorf("path links with distinct paths should not match")
 	}
-	l7 := CreateHostPathLink("domain.local", "/app", MatchBegin)
+	l7 := CreatePathLink("/app", MatchBegin).WithHTTPHost(h0)
 	l7.WithHeadersMatch(HTTPHeaderMatch{
 		{Name: "h1", Value: "v1", Regex: true},
 	})
-	l8 := CreateHostPathLink("domain.local", "/app", MatchBegin)
+	l8 := CreatePathLink("/app", MatchBegin).WithHTTPHost(h0)
 	l8.WithHeadersMatch(HTTPHeaderMatch{
 		{Name: "h1", Value: "v1", Regex: true},
 	})
 	if !l7.Equals(l8) {
 		t.Errorf("path links with same headers should match")
 	}
-	l9 := CreateHostPathLink("domain.local", "/app", MatchBegin)
+	l9 := CreatePathLink("/app", MatchBegin).WithHTTPHost(h0)
 	l9.WithHeadersMatch(HTTPHeaderMatch{
 		{Name: "h1", Value: "v1", Regex: true},
 	})
-	l10 := CreateHostPathLink("domain.local", "/app", MatchBegin)
+	l10 := CreatePathLink("/app", MatchBegin).WithHTTPHost(h0)
 	l10.WithHeadersMatch(HTTPHeaderMatch{
 		{Name: "h1", Value: "v2", Regex: true},
 	})
@@ -95,12 +99,12 @@ func TestShrinkHosts(t *testing.T) {
 	}
 	for i, test := range testCases {
 		c := setup(t)
-		h := CreateHosts()
+		f := (&Frontends{}).AcquireFrontend(8000, false)
 		for _, add := range test.add {
-			h.itemsAdd[add.Hostname] = add
+			f.hostsAdd[add.Hostname] = add
 		}
 		for _, del := range test.del {
-			h.itemsDel[del.Hostname] = del
+			f.hostsDel[del.Hostname] = del
 		}
 		expAdd := map[string]*Host{}
 		for _, add := range test.expAdd {
@@ -110,9 +114,9 @@ func TestShrinkHosts(t *testing.T) {
 		for _, del := range test.expDel {
 			expDel[del.Hostname] = del
 		}
-		h.Shrink()
-		c.compareObjects("add", i, h.itemsAdd, expAdd)
-		c.compareObjects("del", i, h.itemsDel, expDel)
+		f.ShrinkHosts()
+		c.compareObjects("add", i, f.hostsAdd, expAdd)
+		c.compareObjects("del", i, f.hostsDel, expDel)
 		c.teardown()
 	}
 }
@@ -185,7 +189,8 @@ func TestAddFindPath(t *testing.T) {
 	}
 	for i, test := range testCases {
 		c := setup(t)
-		h := CreateHosts().AcquireHost("d1.local")
+		f := (&Frontends{}).AcquireFrontend(8000, false)
+		h := f.AcquireHost("d1.local")
 		for _, p := range test.paths {
 			h.AddPath(p.backend, p.path, p.match)
 		}
@@ -254,8 +259,9 @@ func TestRemovePath(t *testing.T) {
 	}
 	for i, test := range testCases {
 		c := setup(t)
+		f := (&Frontends{}).AcquireFrontend(8000, false)
 		b := CreateBackends(0).AcquireBackend("default", "b", "8080")
-		h := CreateHosts().AcquireHost("d1.local")
+		h := f.AcquireHost("d1.local")
 		for _, path := range strings.Split(test.addPaths, ",") {
 			h.AddPath(b, path, MatchPrefix)
 		}
diff --git a/pkg/haproxy/types/maps_test.go b/pkg/haproxy/types/maps_test.go
index c2138375..fa47a426 100644
--- a/pkg/haproxy/types/maps_test.go
+++ b/pkg/haproxy/types/maps_test.go
@@ -182,10 +182,10 @@ func TestAddHostnamePathMapping(t *testing.T) {
 	}
 	for i, test := range testCases {
 		hm := CreateMaps(matchOrder).AddMap(test.filename)
-		hostPath := &HostPath{
+		path := &Path{
 			Link: CreatePathLink(test.path, test.match),
 		}
-		hm.AddHostnamePathMapping(test.hostname, hostPath, "backend")
+		hm.AddHostnamePathMapping(test.hostname, path, "backend")
 		entries := hm.rawfiles[test.expmatch].entries
 		if len(entries) != 1 {
 			t.Errorf("item %d, invalid match or value: %v", i, hm.rawfiles)
@@ -284,14 +284,14 @@ func TestAddAliasPathMapping(t *testing.T) {
 	}
 	for i, test := range testCases {
 		hm := CreateMaps(matchOrder).AddMap(test.filename)
-		hostPath := &HostPath{
+		path := &Path{
 			Link: CreatePathLink(test.path, test.match),
 		}
 		alias := HostAliasConfig{
 			AliasName:  test.aliasName,
 			AliasRegex: test.aliasRegex,
 		}
-		hm.AddAliasPathMapping(alias, hostPath, "backend")
+		hm.AddAliasPathMapping(alias, path, "backend")
 		actual := map[MatchType][]string{}
 		for match := range hm.rawfiles {
 			for _, entry := range hm.rawfiles[match].entries {
@@ -674,7 +674,7 @@ local1.tld /a2 prefix
 			if item.path == "" {
 				hm.AddHostnameMapping(item.hostname, item.target)
 			} else {
-				hm.AddHostnamePathMapping(item.hostname, &HostPath{Link: CreatePathLink(item.path, item.match).WithHeadersMatch(item.headers)}, item.target)
+				hm.AddHostnamePathMapping(item.hostname, &Path{Link: CreatePathLink(item.path, item.match).WithHeadersMatch(item.headers)}, item.target)
 			}
 		}
 		var output string
diff --git a/tests/framework/framework.go b/tests/framework/framework.go
index 95fcc03e..47d063c7 100644
--- a/tests/framework/framework.go
+++ b/tests/framework/framework.go
@@ -54,10 +54,10 @@ const (
 	PublishAddress  = "10.0.1.1"
 	PublishHostname = "ingress.local"
 
-	TestPortHTTP  = 28080
-	TestPortFront = 28081
-	TestPortHTTPS = 28443
-	TestPortStat  = 21936
+	TestPortHealthz = 28081
+	TestPortHTTP    = 28080
+	TestPortHTTPS   = 28443
+	TestPortStat    = 21936
 )
 
 func NewFramework(ctx context.Context, t *testing.T, o ...options.Framework) *framework {
@@ -180,6 +180,7 @@ func (f *framework) StartController(ctx context.Context, t *testing.T) {
 	global.Data = map[string]string{
 		"syslog-endpoint": "stdout",
 		"syslog-format":   "raw",
+		"healthz-port":    strconv.Itoa(TestPortHealthz),
 		"http-port":       strconv.Itoa(TestPortHTTP),
 		"https-port":      strconv.Itoa(TestPortHTTPS),
 		"stats-port":      strconv.Itoa(TestPortStat),
@@ -225,7 +226,7 @@ func (f *framework) StartController(ctx context.Context, t *testing.T) {
 	t.Log("waiting for controller and haproxy to be ready")
 	require.EventuallyWithT(t, func(collect *assert.CollectT) {
 		// ensuring controller is up and running avoids all the tests to fail due to misconfiguration
-		url := fmt.Sprintf("http://127.0.0.1:%d", TestPortHTTP)
+		url := fmt.Sprintf("http://127.0.0.1:%d", TestPortHealthz)
 		req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
 		if !assert.NoError(collect, err) {
 			return
@@ -246,12 +247,19 @@ func (*framework) Request(ctx context.Context, t *testing.T, method, host, path
 	t.Logf("request method=%s host=%s path=%s\n", method, host, path)
 	opt := options.ParseRequestOptions(o...)
 
-	var url string
+	var proto string
+	var port int32
 	if opt.TLS {
-		url = fmt.Sprintf("https://127.0.0.1:%d", TestPortHTTPS)
+		proto = "https"
+		port = TestPortHTTPS
 	} else {
-		url = fmt.Sprintf("http://127.0.0.1:%d", TestPortHTTP)
+		proto = "http"
+		port = TestPortHTTP
 	}
+	if opt.RequestPort > 0 {
+		port = opt.RequestPort
+	}
+	url := fmt.Sprintf("%s://127.0.0.1:%d", proto, port)
 	var reqBody io.Reader
 	if opt.Body != "" {
 		reqBody = strings.NewReader(opt.Body)
@@ -297,13 +305,16 @@ func (*framework) Request(ctx context.Context, t *testing.T, method, host, path
 	var res *http.Response
 	switch {
 	case opt.ExpectResponseCode > 0:
-		require.EventuallyWithT(t, func(collect *assert.CollectT) {
+		// assert the correct response code ...
+		assert.EventuallyWithT(t, func(collect *assert.CollectT) {
 			res, err = cli.Do(req)
 			if !assert.NoError(collect, err) {
 				return
 			}
 			assert.Equal(collect, opt.ExpectResponseCode, res.StatusCode)
 		}, 5*time.Second, time.Second)
+		// ... but requires that no request error happened.
+		require.NoError(t, err)
 	case opt.ExpectError != "":
 		require.EventuallyWithT(t, func(collect *assert.CollectT) {
 			_, err := cli.Do(req)
@@ -404,20 +415,21 @@ metadata:
   namespace: default
 spec:
   ports:
-  - port: 9999 ## meaningless, just need to match ingress' one
+  - name: port1
+    port: 9999 ## meaningless, just need to match ingress' one
     targetPort: 0
 `
 	name := RandomName("svc")
 
-	// we don't have real pods, so we don't have eps along with the service,
-	// so we need to create them manually.
-	_ = f.CreateEndpointSlice(ctx, t, name, serverPort)
-
 	svc := f.CreateObject(t, data).(*corev1.Service)
 	svc.Name = name
 	svc.Spec.Ports[0].TargetPort = intstr.IntOrString{IntVal: serverPort}
 	opt.Apply(svc)
 
+	// we don't have real pods, so we don't have eps along with the service,
+	// so we need to create them manually.
+	_ = f.CreateEndpointSlice(ctx, t, svc)
+
 	t.Logf("creating service %s/%s\n", svc.Namespace, svc.Name)
 
 	err := f.cli.Create(ctx, svc)
@@ -433,7 +445,7 @@ spec:
 	return svc
 }
 
-func (f *framework) CreateEndpointSlice(ctx context.Context, t *testing.T, svcname string, serverPort int32) *discoveryv1.EndpointSlice {
+func (f *framework) CreateEndpointSlice(ctx context.Context, t *testing.T, svc *corev1.Service) *discoveryv1.EndpointSlice {
 	data := `
 kind: EndpointSlice
 apiVersion: discovery.k8s.io/v1
@@ -449,14 +461,18 @@ metadata:
   labels: {}
   generateName: ""
   namespace: default
-ports:
-- port: 0
+ports: []
 `
 
 	eps := f.CreateObject(t, data).(*discoveryv1.EndpointSlice)
-	eps.GenerateName = svcname + "-"
-	eps.Labels["kubernetes.io/service-name"] = svcname
-	eps.Ports[0].Port = &serverPort
+	eps.GenerateName = svc.Name + "-"
+	eps.Labels["kubernetes.io/service-name"] = svc.Name
+	for _, svcport := range svc.Spec.Ports {
+		eps.Ports = append(eps.Ports, discoveryv1.EndpointPort{
+			Name: &svcport.Name,
+			Port: &svcport.TargetPort.IntVal,
+		})
+	}
 
 	t.Logf("creating endpointslice %s/%s\n", eps.Namespace, eps.Name)
 
@@ -598,7 +614,7 @@ func (f *framework) CreateGateway(ctx context.Context, t *testing.T, version str
 	if opt.Listeners == nil {
 		opt.Listeners = []options.ListenerOpt{{
 			Name:  "echoserver-gw",
-			Port:  80,
+			Port:  TestPortHTTP,
 			Proto: "HTTP",
 		}}
 	}
@@ -620,11 +636,28 @@ spec:
 	spec := reflect.ValueOf(gw).Elem().FieldByName("Spec").Addr().Interface().(*gatewayv1.GatewaySpec)
 	spec.GatewayClassName = gatewayv1.ObjectName(gc.Name)
 	for _, l := range opt.Listeners {
-		spec.Listeners = append(spec.Listeners, gatewayv1.Listener{
-			Name:     gatewayv1.SectionName(l.Name),
-			Protocol: gatewayv1.ProtocolType(l.Proto),
-			Port:     gatewayv1.PortNumber(l.Port),
-		})
+		listener := gatewayv1.Listener{
+			Name:     l.Name,
+			Protocol: l.Proto,
+			Port:     l.Port,
+		}
+		var mode gatewayv1.TLSModeType
+		switch listener.Protocol {
+		case gatewayv1.HTTPSProtocolType:
+			mode = gatewayv1.TLSModeTerminate
+		case gatewayv1.TLSProtocolType:
+			mode = gatewayv1.TLSModePassthrough
+			if len(l.Certs) > 0 {
+				mode = gatewayv1.TLSModeTerminate
+			}
+		}
+		if mode != "" {
+			listener.TLS = &gatewayv1.GatewayTLSConfig{
+				Mode:            &mode,
+				CertificateRefs: l.Certs,
+			}
+		}
+		spec.Listeners = append(spec.Listeners, listener)
 	}
 	opt.Apply(gw)
 
@@ -680,7 +713,12 @@ spec:
       port: 0
 `, api)
 	name := RandomName("httproute")
-	hostname := name + ".local"
+	var hostname string
+	if opt.CustomHostName != nil {
+		hostname = *opt.CustomHostName
+	} else {
+		hostname = name + ".local"
+	}
 
 	route := f.CreateObject(t, data)
 	route.SetName(name)
@@ -708,6 +746,59 @@ spec:
 	return route, hostname
 }
 
+func (f *framework) CreateTLSRouteA2(ctx context.Context, t *testing.T, gw *gatewayv1.Gateway, svc *corev1.Service, o ...options.Object) *gatewayv1alpha2.TLSRoute {
+	route := f.CreateTLSRoute(ctx, t, gatewayv1alpha2.GroupVersion.Version, gw, svc, o...)
+	return route.(*gatewayv1alpha2.TLSRoute)
+}
+
+func (f *framework) CreateTLSRoute(ctx context.Context, t *testing.T, version string, gw *gatewayv1.Gateway, svc *corev1.Service, o ...options.Object) client.Object {
+	opt := options.ParseObjectOptions(o...)
+	api := v1.GroupVersion{Group: gatewayv1.GroupName, Version: version}.String()
+	data := fmt.Sprintf(`
+apiVersion: %s
+kind: TLSRoute
+metadata:
+  name: ""
+  namespace: default
+spec:
+  parentRefs:
+  - name: ""
+  rules:
+  - backendRefs:
+    - name: ""
+      port: 0
+`, api)
+	name := RandomName("tlsroute")
+
+	route := f.CreateObject(t, data)
+	route.SetName(name)
+	spec := reflect.ValueOf(route).Elem().FieldByName("Spec").Addr().Interface().(*gatewayv1alpha2.TLSRouteSpec)
+	if opt.CustomHostName != nil {
+		spec.Hostnames = []gatewayv1alpha2.Hostname{gatewayv1alpha2.Hostname(*opt.CustomHostName)}
+	}
+	spec.ParentRefs[0].Name = gatewayv1.ObjectName(gw.Name)
+	spec.Rules[0].BackendRefs[0].Name = gatewayv1.ObjectName(svc.Name)
+	spec.Rules[0].BackendRefs[0].Port = (*gatewayv1.PortNumber)(&svc.Spec.Ports[0].Port)
+	opt.Apply(route)
+
+	t.Logf("creating TLSRoute %s/%s\n", route.GetNamespace(), route.GetName())
+
+	err := f.cli.Create(ctx, route)
+	require.NoError(t, err)
+
+	t.Cleanup(func() {
+		route := unstructured.Unstructured{}
+		route.SetAPIVersion(api)
+		route.SetKind("TLSRoute")
+		route.SetNamespace("default")
+		route.SetName(name)
+		err := f.cli.Delete(ctx, &route)
+		assert.NoError(t, client.IgnoreNotFound(err))
+	})
+	return route
+
+}
+
 func (f *framework) CreateTCPRouteA2(ctx context.Context, t *testing.T, gw *gatewayv1.Gateway, svc *corev1.Service, o ...options.Object) *gatewayv1alpha2.TCPRoute {
 	route := f.CreateTCPRoute(ctx, t, gatewayv1alpha2.GroupVersion.Version, gw, svc, o...)
 	return route.(*gatewayv1alpha2.TCPRoute)
diff --git a/tests/framework/options/objects.go b/tests/framework/options/objects.go
index 4afa3ba9..a79b1ec1 100644
--- a/tests/framework/options/objects.go
+++ b/tests/framework/options/objects.go
@@ -3,6 +3,7 @@ package options
 import (
 	"k8s.io/utils/ptr"
 	"sigs.k8s.io/controller-runtime/pkg/client"
+	gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
 )
 
 type Object func(o *objectOpt)
@@ -34,12 +35,17 @@ func CustomHostName(hostname string) Object {
 		o.CustomHostName = ptr.To(hostname)
 	}
 }
-func Listener(name, proto string, port int32) Object {
+
+// Listener creates a new listener on a Gateway object. certs is ignored on HTTP, TCP and UDP protos.
+// HTTPS proto requires len(certs)>0. TLS proto infers Terminate mode if len(certs)>0, Passthrough is
+// used otherwise.
+func Listener(name gatewayv1.SectionName, proto gatewayv1.ProtocolType, port gatewayv1.PortNumber, certs []gatewayv1.SecretObjectReference) Object {
 	return func(o *objectOpt) {
 		o.Listeners = append(o.Listeners, ListenerOpt{
 			Name:  name,
 			Proto: proto,
 			Port:  port,
+			Certs: certs,
 		})
 	}
 }
@@ -69,9 +75,10 @@ type GatewayOpt struct {
 }
 
 type ListenerOpt struct {
-	Name  string
-	Proto string
-	Port  int32
+	Name  gatewayv1.SectionName
+	Proto gatewayv1.ProtocolType
+	Port  gatewayv1.PortNumber
+	Certs []gatewayv1.SecretObjectReference
 }
 
 func (o *objectOpt) Apply(obj client.Object) {
diff --git a/tests/framework/options/request.go b/tests/framework/options/request.go
index 70c23376..c5e28047 100644
--- a/tests/framework/options/request.go
+++ b/tests/framework/options/request.go
@@ -59,6 +59,12 @@ func ClientCertificateKeyPEM(crt, key []byte) Request {
 	}
 }
 
+func RequestPort(port int32) Request {
+	return func(o *requestOpt) {
+		o.RequestPort = port
+	}
+}
+
 func CustomRequest(custom CustomRequestCallback) Request {
 	return func(o *requestOpt) {
 		o.CustomRequest = custom
@@ -77,6 +83,7 @@ type requestOpt struct {
 	SNI                string
 	ClientCrtPEM       []byte
 	ClientKeyPEM       []byte
+	RequestPort        int32
 	CustomRequest      CustomRequestCallback
 }
 
diff --git a/tests/integration/integration_test.go b/tests/integration/integration_test.go
index 32d518cc..e82ed09e 100644
--- a/tests/integration/integration_test.go
+++ b/tests/integration/integration_test.go
@@ -19,7 +19,7 @@ import (
 	"github.com/stretchr/testify/require"
 	corev1 "k8s.io/api/core/v1"
 	networkingv1 "k8s.io/api/networking/v1"
-	"k8s.io/apimachinery/pkg/types"
+	"k8s.io/apimachinery/pkg/util/intstr"
 	"k8s.io/client-go/tools/cache"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
@@ -72,6 +72,15 @@ func TestIntegrationIngress(t *testing.T) {
 		assert.Equal(t, "http", res.EchoResponse.ReqHeaders["x-forwarded-proto"])
 	})
 
+	t.Run("bare", func(t *testing.T) {
+		t.Parallel()
+		res := f.Request(ctx, t, http.MethodGet, "", "/healthz",
+			options.RequestPort(framework.TestPortHealthz),
+			options.ExpectResponseCode(http.StatusOK),
+		)
+		assert.Contains(t, res.Body, "Service ready")
+	})
+
 	t.Run("should not redirect to https", func(t *testing.T) {
 		t.Parallel()
 		svc := f.CreateService(ctx, t, httpServerPort)
@@ -572,120 +581,97 @@ Request forbidden by administrative rules.
 		assert.False(t, res.EchoResponse.Parsed)
 	})
 
-	t.Run("should handle proto header on fronting proxy", func(t *testing.T) {
-		// t.Parallel() // fronting proxy configuration changed, cannot run in parallel
+	t.Run("should cohexist regular and fronting-proxy http frontends", func(t *testing.T) {
+		t.Parallel()
 
+		port := framework.RandomPort()
 		svc := f.CreateService(ctx, t, httpServerPort)
-		_, hostname := f.CreateIngress(ctx, t, svc)
-
-		// global config to be changed
-		cm := corev1.ConfigMap{}
-		err := f.Client().Get(ctx, types.NamespacedName{Namespace: "default", Name: "ingress-controller"}, &cm)
-		require.NoError(t, err)
-
-		apply := func(t *testing.T, port int, useXFPHeader ...bool) {
-			if port > 0 {
-				cm.Data[ingtypes.GlobalFrontingProxyPort] = strconv.Itoa(port)
-				should := map[bool]string{false: "false", true: "true"}
-				cm.Data[ingtypes.GlobalUseForwardedProto] = should[useXFPHeader[0]]
-			} else {
-				delete(cm.Data, ingtypes.GlobalFrontingProxyPort)
-				delete(cm.Data, ingtypes.GlobalUseForwardedProto)
-			}
-			err = f.Client().Update(ctx, &cm)
-			require.NoError(t, err)
-			time.Sleep(3 * time.Second) // TODO need a better way to ensure the config was already applied
-		}
+		_, hproxy := f.CreateIngress(ctx, t, svc)
+		_, fproxy := f.CreateIngress(ctx, t, svc,
+			options.AddConfigKeyAnnotation(ingtypes.FrontFrontingProxyPort, strconv.Itoa(int(port))),
+		)
 
-		type reqtype string
-		const (
-			xfp = "X-Forwarded-Proto"
+		hres := f.Request(ctx, t, http.MethodGet, hproxy, "/",
+			options.ExpectResponseCode(http.StatusOK),
+		)
+		assert.True(t, hres.EchoResponse.Parsed)
 
-			reqempty reqtype = ""
-			reqhttp  reqtype = "http"
-			reqhttps reqtype = "https"
-			reqredir reqtype = "<redir>"
+		fres := f.Request(ctx, t, http.MethodGet, fproxy, "/",
+			options.RequestPort(port),
+			options.CustomRequest(func(req *http.Request) {
+				req.Header.Set("X-Forwarded-Proto", "https")
+			}),
+			options.ExpectResponseCode(http.StatusOK),
 		)
+		assert.True(t, fres.EchoResponse.Parsed)
+	})
 
-		req := func(t *testing.T, port int, xfpHeader string, expHeader reqtype) {
-			expcode := http.StatusOK
-			if expHeader == reqredir {
-				expcode = http.StatusFound
-			}
-			res := f.Request(ctx, t, http.MethodGet, hostname, "/",
-				options.CustomRequest(func(req *http.Request) {
-					if xfpHeader != "" {
-						req.Header.Set(xfp, xfpHeader)
-					}
-					req.URL.Host = fmt.Sprintf("127.0.0.1:%d", port)
-				}),
-				options.ExpectResponseCode(expcode),
-			)
-			if expHeader != reqredir {
-				assert.True(t, res.EchoResponse.Parsed)
-				assert.Equal(t, string(expHeader), res.EchoResponse.ReqHeaders[strings.ToLower(xfp)])
-			} else {
-				assert.False(t, res.EchoResponse.Parsed)
-			}
-		}
+	t.Run("should handle proto header on fronting proxy", func(t *testing.T) {
+		t.Parallel()
 
-		hport := framework.TestPortHTTP
-		fport := framework.TestPortFront
+		const xfp = "X-Forwarded-Proto"
+		const reqredir = "<redir>"
 		testCases := []struct {
-			frontingPort  int
-			useXFPHeader  bool
-			requestPort   int
-			expXFPMissing reqtype
-			expXFPHTTP    reqtype
-			expXFPHTTPS   reqtype
+			useXFPHeader bool
+			xfpContent   string
+			expRecHeader string
 		}{
-			// shared port
-			{frontingPort: hport, useXFPHeader: false, requestPort: hport, expXFPMissing: reqempty, expXFPHTTP: reqhttp, expXFPHTTPS: reqhttps},
-			{frontingPort: hport, useXFPHeader: true, requestPort: hport, expXFPMissing: reqhttp, expXFPHTTP: reqredir, expXFPHTTPS: reqhttps},
-
-			// dedicated port, calling `http` port
-			{frontingPort: fport, useXFPHeader: false, requestPort: hport, expXFPMissing: reqempty, expXFPHTTP: reqhttp, expXFPHTTPS: reqhttps},
-			{frontingPort: fport, useXFPHeader: true, requestPort: hport, expXFPMissing: reqhttp, expXFPHTTP: reqhttp, expXFPHTTPS: reqhttp},
-
-			// dedicated port, calling `fronting-proxy` port
-			{frontingPort: fport, useXFPHeader: false, requestPort: fport, expXFPMissing: reqempty, expXFPHTTP: reqhttp, expXFPHTTPS: reqhttps},
-			{frontingPort: fport, useXFPHeader: true, requestPort: fport, expXFPMissing: reqredir, expXFPHTTP: reqredir, expXFPHTTPS: reqhttps},
+			{useXFPHeader: false, xfpContent: "", expRecHeader: ""},
+			{useXFPHeader: false, xfpContent: "http", expRecHeader: "http"},
+			{useXFPHeader: false, xfpContent: "https", expRecHeader: "https"},
+			{useXFPHeader: true, xfpContent: "", expRecHeader: reqredir},
+			{useXFPHeader: true, xfpContent: "http", expRecHeader: reqredir},
+			{useXFPHeader: true, xfpContent: "https", expRecHeader: "https"},
+		}
+		should := map[bool]string{false: "false", true: "true"}
+		reqHeaders := map[string]string{
+			"x-ssl-client-cn":   "localhost",
+			"x-ssl-client-dn":   "/CN=localhost",
+			"x-ssl-client-sha1": "abc123",
+			"x-ssl-client-sha2": "abcd1234",
+			"x-ssl-client-cert": "LS0tLS1CRUdJT...",
 		}
 		for _, test := range testCases {
-			nport := "shared"
-			if test.frontingPort != framework.TestPortHTTP {
-				nport = "dedicated"
+			reqxfp := "missing"
+			if test.xfpContent != "" {
+				reqxfp = test.xfpContent
 			}
-			rport := "http"
-			if test.requestPort != framework.TestPortHTTP {
-				rport = "fronting-proxy"
-			}
-			prefix := fmt.Sprintf("port=%s usexfp=%t reqport=%s reqxfp=", nport, test.useXFPHeader, rport)
-			apply(t, test.frontingPort, test.useXFPHeader)
-			t.Run(prefix+"missing", func(t *testing.T) {
-				req(t, test.requestPort, "", test.expXFPMissing)
-			})
-			t.Run(prefix+"http", func(t *testing.T) {
-				req(t, test.requestPort, "http", test.expXFPHTTP)
-			})
-			t.Run(prefix+"https", func(t *testing.T) {
-				req(t, test.requestPort, "https", test.expXFPHTTPS)
+			name := fmt.Sprintf("usexfp=%t reqxfp=%s", test.useXFPHeader, reqxfp)
+			t.Run(name, func(t *testing.T) {
+				t.Parallel()
+				port := framework.RandomPort()
+				svc := f.CreateService(ctx, t, httpServerPort)
+				_, hostname := f.CreateIngress(ctx, t, svc,
+					options.AddConfigKeyAnnotation(ingtypes.FrontFrontingProxyPort, strconv.Itoa(int(port))),
+					options.AddConfigKeyAnnotation(ingtypes.FrontUseForwardedProto, should[test.useXFPHeader]),
+				)
+				expcode := http.StatusOK
+				if test.expRecHeader == reqredir {
+					expcode = http.StatusFound
+				}
+				res := f.Request(ctx, t, http.MethodGet, hostname, "/",
+					options.RequestPort(port),
+					options.CustomRequest(func(req *http.Request) {
+						for h, v := range reqHeaders {
+							req.Header.Set(h, v)
+						}
+						if test.xfpContent != "" {
+							req.Header.Set(xfp, test.xfpContent)
+						}
+					}),
+					options.ExpectResponseCode(expcode),
+				)
+				if test.expRecHeader != reqredir {
+					assert.True(t, res.EchoResponse.Parsed)
+					assert.Equal(t, test.expRecHeader, res.EchoResponse.ReqHeaders[strings.ToLower(xfp)])
+					for header, value := range reqHeaders {
+						assert.Equal(t, value, res.EchoResponse.ReqHeaders[header])
+					}
+				} else {
+					assert.False(t, res.EchoResponse.Parsed)
+				}
 			})
 		}
-
-		t.Run("restore global config", func(t *testing.T) {
-			apply(t, 0)
-			_ = f.Request(ctx, t, http.MethodGet, hostname, "/",
-				options.CustomRequest(func(req *http.Request) {
-					req.URL.Host = fmt.Sprintf("127.0.0.1:%d", framework.TestPortFront)
-				}),
-				options.ExpectError("connection refused"),
-			)
-
-			req(t, framework.TestPortHTTP, "", reqhttp)
-			req(t, framework.TestPortHTTP, "http", reqhttp)
-			req(t, framework.TestPortHTTP, "https", reqhttp)
-		})
 	})
 
 	t.Run("should take leader", func(t *testing.T) {
@@ -929,6 +915,108 @@ Request forbidden by administrative rules.
 		assert.Equal(t, "https1", res.EchoResponse.ServerName)
 	})
 
+	t.Run("should ssl-passthrough", func(t *testing.T) {
+		t.Parallel()
+
+		serverCrtPem, serverKeyPem := framework.CreateCertificate(t, caValid, cakeyValid, "localhost")
+		serverCrt, err := tls.X509KeyPair(serverCrtPem, serverKeyPem)
+		require.NoError(t, err)
+		httpsServer1Port := f.CreateHTTPServer(ctx, t, "https-server1",
+			options.ServerCertificates([]tls.Certificate{serverCrt}),
+		)
+		httpsServer2Port := f.CreateHTTPServer(ctx, t, "https-server2",
+			options.ServerCertificates([]tls.Certificate{serverCrt}),
+		)
+
+		svc1 := f.CreateService(ctx, t, httpsServer1Port)
+		svc2 := f.CreateService(ctx, t, httpsServer2Port, options.CustomObject(func(o client.Object) {
+			svc := o.(*corev1.Service)
+			svc.Spec.Ports = append(svc.Spec.Ports, corev1.ServicePort{
+				Name:       "port2",
+				Port:       8080,
+				TargetPort: intstr.IntOrString{IntVal: httpServerPort},
+			})
+		}))
+		_, hostname1 := f.CreateIngress(ctx, t, svc1,
+			options.AddConfigKeyAnnotation(ingtypes.HostSSLPassthrough, "True"),
+		)
+
+		_, hostname2 := f.CreateIngress(ctx, t, svc2,
+			options.AddConfigKeyAnnotation(ingtypes.HostSSLPassthrough, "True"),
+			options.AddConfigKeyAnnotation(ingtypes.HostSSLPassthroughHTTPPort, strconv.Itoa(int(httpServerPort))),
+		)
+
+		res1http := f.Request(ctx, t, http.MethodGet, hostname1, "/", options.ExpectResponseCode(http.StatusFound))
+		assert.False(t, res1http.EchoResponse.Parsed)
+
+		res1https := f.Request(ctx, t, http.MethodGet, hostname1, "/",
+			options.TLSRequest(),
+			options.TLSSkipVerify(),
+			options.SNI(hostname1),
+			options.ExpectResponseCode(http.StatusOK),
+		)
+		assert.True(t, res1https.EchoResponse.Parsed)
+		assert.Equal(t, "https-server1", res1https.EchoResponse.ServerName)
+
+		res2http := f.Request(ctx, t, http.MethodGet, hostname2, "/", options.ExpectResponseCode(http.StatusOK))
+		assert.True(t, res2http.EchoResponse.Parsed)
+		assert.Equal(t, "default", res2http.EchoResponse.ServerName)
+
+		res2https := f.Request(ctx, t, http.MethodGet, hostname2, "/",
+			options.TLSRequest(),
+			options.TLSSkipVerify(),
+			options.SNI(hostname2),
+			options.ExpectResponseCode(http.StatusOK),
+		)
+		assert.True(t, res2https.EchoResponse.Parsed)
+		assert.Equal(t, "https-server2", res2https.EchoResponse.ServerName)
+	})
+
+	t.Run("should distinguish same hostname and path from distinct frontends", func(t *testing.T) {
+		t.Parallel()
+
+		hostname := framework.RandomHostName()
+		svc := f.CreateService(ctx, t, httpServerPort)
+		ing := func(httpport, httpsport int32, location string) {
+			_, _ = f.CreateIngress(ctx, t, svc,
+				options.DefaultTLS(),
+				options.CustomHostName(hostname),
+				options.AddConfigKeyAnnotation(ingtypes.FrontHTTPPort, strconv.Itoa(int(httpport))),
+				options.AddConfigKeyAnnotation(ingtypes.FrontHTTPSPort, strconv.Itoa(int(httpsport))),
+				options.AddConfigKeyAnnotation(ingtypes.BackSSLRedirect, "false"),
+				options.AddConfigKeyAnnotation(ingtypes.BackRewriteTarget, location),
+			)
+		}
+		req := func(port int32, ssl bool, location string) {
+			opt := []options.Request{
+				options.RequestPort(port),
+				options.ExpectResponseCode(http.StatusOK),
+			}
+			if ssl {
+				opt = append(opt,
+					options.TLSRequest(),
+					options.TLSSkipVerify(),
+				)
+			}
+			res := f.Request(ctx, t, http.MethodGet, hostname, "/", opt...)
+			assert.True(t, res.EchoResponse.Parsed)
+			assert.Equal(t, location, res.EchoResponse.Path)
+		}
+
+		http1 := framework.RandomPort()
+		https1 := framework.RandomPort()
+		http2 := framework.RandomPort()
+		https2 := framework.RandomPort()
+
+		ing(http1, https1, "/api1")
+		ing(http2, https2, "/api2")
+
+		req(http1, false, "/api1/")
+		req(https1, true, "/api1/")
+		req(http2, false, "/api2/")
+		req(https2, true, "/api2/")
+	})
+
 	// should match wildcard host
 
 	// should match domain conflicting with wildcard host
@@ -980,10 +1068,14 @@ func TestIntegrationGateway(t *testing.T) {
 	t.Run("v1", func(t *testing.T) {
 		f := framework.NewFramework(ctx, t, options.CRDs("gateway-api-v100-v1-experimental"))
 		f.StartController(ctx, t)
-		httpServerPort := f.CreateHTTPServer(ctx, t, "gw-v1")
+
+		httpServerPort := f.CreateHTTPServer(ctx, t, "gw-v1-http")
 		tcpServerPort := f.CreateTCPServer(ctx, t)
 		gc := f.CreateGatewayClassV1(ctx, t)
 
+		caValid, cakeyValid := framework.CreateCA(t, framework.CertificateIssuerCN)
+		crtValidPem, keyValidPem := framework.CreateCertificate(t, caValid, cakeyValid, framework.CertificateClientCN)
+
 		t.Run("hello world", func(t *testing.T) {
 			t.Parallel()
 			gw := f.CreateGatewayV1(ctx, t, gc)
@@ -997,7 +1089,7 @@ func TestIntegrationGateway(t *testing.T) {
 		t.Run("expose TCPRoute", func(t *testing.T) {
 			t.Parallel()
 			listenerPort := framework.RandomPort()
-			gw := f.CreateGatewayV1(ctx, t, gc, options.Listener("tcpserver", "TCP", listenerPort))
+			gw := f.CreateGatewayV1(ctx, t, gc, options.Listener("tcpserver", gatewayv1.TCPProtocolType, gatewayv1.PortNumber(listenerPort), nil))
 			svc := f.CreateService(ctx, t, tcpServerPort)
 			_ = f.CreateTCPRouteA2(ctx, t, gw, svc)
 			res1 := f.TCPRequest(ctx, t, listenerPort, "ping")
@@ -1006,6 +1098,20 @@ func TestIntegrationGateway(t *testing.T) {
 			assert.Equal(t, "reply", res2)
 		})
 
+		t.Run("expose TLSRoute", func(t *testing.T) {
+			t.Parallel()
+			secret := f.CreateSecretTLS(ctx, t, crtValidPem, keyValidPem)
+			certs := []gatewayv1.SecretObjectReference{{Name: gatewayv1.ObjectName(secret.Name)}}
+			listenerPort := framework.RandomPort()
+			gw := f.CreateGatewayV1(ctx, t, gc, options.Listener("tlsserver", gatewayv1.TLSProtocolType, gatewayv1.PortNumber(listenerPort), certs))
+			svc := f.CreateService(ctx, t, tcpServerPort)
+			_ = f.CreateTLSRouteA2(ctx, t, gw, svc)
+			res1 := f.TCPRequest(ctx, t, listenerPort, "ping", options.TLSRequest())
+			assert.Equal(t, "ping", res1)
+			res2 := f.TCPRequest(ctx, t, listenerPort, "reply", options.TLSRequest())
+			assert.Equal(t, "reply", res2)
+		})
+
 		t.Run("multi certificates on listener", func(t *testing.T) {
 			t.Parallel()
 			caCrt, caKey := framework.CreateCA(t, framework.CertificateIssuerCN)
@@ -1018,7 +1124,7 @@ func TestIntegrationGateway(t *testing.T) {
 					g := o.(*gatewayv1.Gateway)
 					g.Spec.Listeners = []gatewayv1.Listener{{
 						Name:     "l1",
-						Port:     443,
+						Port:     framework.TestPortHTTPS,
 						Protocol: gatewayv1.HTTPSProtocolType,
 						TLS: &gatewayv1.GatewayTLSConfig{
 							CertificateRefs: []gatewayv1.SecretObjectReference{
@@ -1062,5 +1168,56 @@ func TestIntegrationGateway(t *testing.T) {
 				require.NoError(t, conn.Close())
 			}
 		})
+
+		t.Run("should ssl-passthrough", func(t *testing.T) {
+			t.Parallel()
+
+			hostnamehttp := framework.RandomName("httproute") + ".local"
+			hostnametls := framework.RandomName("tlsroute") + ".local"
+			crtPem, keyPem := framework.CreateCertificate(t, caValid, cakeyValid, "localhost", options.DNS(hostnamehttp, hostnametls))
+			crt, err := tls.X509KeyPair(crtPem, keyPem)
+			require.NoError(t, err)
+			httpsServerPort := f.CreateHTTPServer(ctx, t, "gw-v1-https",
+				options.ServerCertificates([]tls.Certificate{crt}),
+			)
+
+			// configure ssl offload ...
+			secret := f.CreateSecretTLS(ctx, t, crtPem, keyPem)
+			certs := []gatewayv1.SecretObjectReference{{Name: gatewayv1.ObjectName(secret.Name)}}
+			gwhttp := f.CreateGatewayV1(ctx, t, gc,
+				options.Listener("gw-https", gatewayv1.HTTPSProtocolType, framework.TestPortHTTPS, certs),
+			)
+			svchttp := f.CreateService(ctx, t, httpServerPort)
+			_, _ = f.CreateHTTPRouteV1(ctx, t, gwhttp, svchttp, options.CustomHostName(hostnamehttp))
+
+			// ... along with ssl passthrough
+			gwtls := f.CreateGatewayV1(ctx, t, gc,
+				options.Listener("gw-passthrough", gatewayv1.TLSProtocolType, framework.TestPortHTTPS, nil),
+			)
+			svctls := f.CreateService(ctx, t, httpsServerPort)
+			_ = f.CreateTLSRouteA2(ctx, t, gwtls, svctls,
+				options.CustomHostName(hostnametls),
+			)
+
+			// TLS request on both hostnames
+			req := func(hostname string) framework.Response {
+				res := f.Request(ctx, t, http.MethodGet, hostname, "/",
+					options.TLSRequest(),
+					options.TLSVerify(true),
+					options.ClientCA(caValid),
+					options.SNI(hostname),
+					options.ExpectResponseCode(http.StatusOK),
+				)
+				assert.True(t, res.EchoResponse.Parsed)
+				return res
+			}
+
+			reshttp := req(hostnamehttp)
+			assert.Equal(t, "https", reshttp.EchoResponse.ReqHeaders["x-forwarded-proto"])
+			assert.Equal(t, "gw-v1-http", reshttp.EchoResponse.ServerName)
+
+			restls := req(hostnametls)
+			assert.Equal(t, "gw-v1-https", restls.EchoResponse.ServerName)
+		})
 	})
 }