Repository: envoyproxy/gateway

Issue:
Surface new forwarding configuration of APIKeyAuth filter
*Description*:
The Envoy APIKeyAuth can now forward the client ID to an upstream service and/or hide the original credential from it. It would be great if Gateway could surface this configuration in the SecurityPolicy resource.

*Relevant Links*:
[Docs](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/api_key_auth_filter)
[PR](https://github.com/envoyproxy/envoy/pull/39763)

Discussion:

Solution patch:
diff --git a/.github/workflows/license-scan.yml b/.github/workflows/license-scan.yml
index 42cb3e522..eaa79acd0 100644
--- a/.github/workflows/license-scan.yml
+++ b/.github/workflows/license-scan.yml
@@ -18,7 +18,7 @@ jobs:
     - name: Checkout code
       uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683  # v4.2.2
     - name: Run scanner
-      uses: google/osv-scanner-action/osv-scanner-action@40a8940a65eab1544a6af759e43d936201a131a2  # v2.0.3
+      uses: google/osv-scanner-action/osv-scanner-action@b00f71e051ddddc6e46a193c31c8c0bf283bf9e6  # v2.1.0
       continue-on-error: true  # remove this after https://github.com/google/deps.dev/issues/146 has been resolved
       with:
         scan-args: |-
diff --git a/.github/workflows/osv-scanner.yml b/.github/workflows/osv-scanner.yml
index 0112df156..687d1d72e 100644
--- a/.github/workflows/osv-scanner.yml
+++ b/.github/workflows/osv-scanner.yml
@@ -19,7 +19,7 @@ permissions:
 jobs:
   scan-scheduled:
     if: ${{ github.event_name == 'push' || github.event_name == 'schedule' }}
-    uses: "google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml@40a8940a65eab1544a6af759e43d936201a131a2"  # v2.0.3
+    uses: "google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml@b00f71e051ddddc6e46a193c31c8c0bf283bf9e6"  # v2.1.0
     with:
       scan-args: |-
         --recursive
@@ -32,7 +32,7 @@ jobs:
 
   scan-pr:
     if: ${{ github.event_name == 'pull_request' || github.event_name == 'merge_group' }}
-    uses: "google/osv-scanner-action/.github/workflows/osv-scanner-reusable-pr.yml@40a8940a65eab1544a6af759e43d936201a131a2"  # v2.0.3
+    uses: "google/osv-scanner-action/.github/workflows/osv-scanner-reusable-pr.yml@b00f71e051ddddc6e46a193c31c8c0bf283bf9e6"  # v2.1.0
     with:
       scan-args: |-
         --recursive
diff --git a/api/v1alpha1/api_key_auth_types.go b/api/v1alpha1/api_key_auth_types.go
index 52b2f0580..1986f386a 100644
--- a/api/v1alpha1/api_key_auth_types.go
+++ b/api/v1alpha1/api_key_auth_types.go
@@ -22,6 +22,17 @@ type APIKeyAuth struct {
 	// ExtractFrom is where to fetch the key from the coming request.
 	// The value from the first source that has a key will be used.
 	ExtractFrom []*ExtractFrom `json:"extractFrom"`
+
+	// ForwardClientIDHeader is the name of the header to forward the client identity to the backend
+	// service. The header will be added to the request with the client id as the value.
+	//
+	// +optional
+	ForwardClientIDHeader *string `json:"forwardClientIDHeader,omitempty"`
+
+	// Sanitize indicates whether to remove the API key from the request before forwarding it to the backend service.
+	//
+	// +optional
+	Sanitize *bool `json:"sanitize,omitempty"`
 }
 
 // ExtractFrom is where to fetch the key from the coming request.
diff --git a/api/v1alpha1/ext_proc_types.go b/api/v1alpha1/ext_proc_types.go
index 0fdf1b424..41f57e954 100644
--- a/api/v1alpha1/ext_proc_types.go
+++ b/api/v1alpha1/ext_proc_types.go
@@ -68,7 +68,7 @@ type ExtProcProcessingMode struct {
 // +kubebuilder:validation:XValidation:message="BackendRefs must be used, backendRef is not supported.",rule="!has(self.backendRef)"
 // +kubebuilder:validation:XValidation:message="BackendRefs only supports Service, ServiceImport, and Backend kind.",rule="has(self.backendRefs) ? self.backendRefs.all(f, f.kind == 'Service' || f.kind == 'ServiceImport' || f.kind == 'Backend') : true"
 // +kubebuilder:validation:XValidation:message="BackendRefs only supports Core, multicluster.x-k8s.io, and gateway.envoyproxy.io groups.",rule="has(self.backendRefs) ? (self.backendRefs.all(f, f.group == \"\" || f.group == 'multicluster.x-k8s.io' || f.group == 'gateway.envoyproxy.io')) : true"
-// +kubebuilder:validation:XValidation:message="If FullDuplexStreamed body processing mode is used, FailOpen must be false.",rule="!(has(self.failOpen) && self.failOpen == true && ((has(self.processingMode.request.body) && self.processingMode.request.body == 'FullDuplexStreamed') || (has(self.processingMode.response.body) && self.processingMode.response.body == 'FullDuplexStreamed')))"
+// +kubebuilder:validation:XValidation:message="If FullDuplexStreamed body processing mode is used, FailOpen must be false.",rule="!(has(self.failOpen) && self.failOpen == true && has(self.processingMode) && ((has(self.processingMode.request) && has(self.processingMode.request.body) && self.processingMode.request.body == 'FullDuplexStreamed') || (has(self.processingMode.response) && has(self.processingMode.response.body) && self.processingMode.response.body == 'FullDuplexStreamed')))"
 type ExtProc struct {
 	BackendCluster `json:",inline"`
 
diff --git a/api/v1alpha1/healthcheck_types.go b/api/v1alpha1/healthcheck_types.go
index 11f667773..772e78166 100644
--- a/api/v1alpha1/healthcheck_types.go
+++ b/api/v1alpha1/healthcheck_types.go
@@ -5,7 +5,10 @@
 
 package v1alpha1
 
-import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+import (
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
+)
 
 // HealthCheck configuration to decide which endpoints
 // are healthy and can be used for routing.
@@ -99,6 +102,13 @@ type ActiveHealthCheck struct {
 	// +optional
 	Interval *metav1.Duration `json:"interval"`
 
+	// InitialJitter defines the maximum time Envoy will wait before the first health check.
+	// Envoy will randomly select a value between 0 and the initial jitter value.
+	//
+	// +kubebuilder:validation:Format=duration
+	// +optional
+	InitialJitter *gwapiv1.Duration `json:"initialJitter,omitempty"`
+
 	// UnhealthyThreshold defines the number of unhealthy health checks required before a backend host is marked unhealthy.
 	//
 	// +kubebuilder:validation:Minimum=1
diff --git a/api/v1alpha1/retry_types.go b/api/v1alpha1/retry_types.go
index 1bee8ce75..d5f3eb24d 100644
--- a/api/v1alpha1/retry_types.go
+++ b/api/v1alpha1/retry_types.go
@@ -51,7 +51,7 @@ type RetryOn struct {
 }
 
 // TriggerEnum specifies the conditions that trigger retries.
-// +kubebuilder:validation:Enum={"5xx","gateway-error","reset","connect-failure","retriable-4xx","refused-stream","retriable-status-codes","cancelled","deadline-exceeded","internal","resource-exhausted","unavailable"}
+// +kubebuilder:validation:Enum={"5xx","gateway-error","reset","reset-before-request","connect-failure","retriable-4xx","refused-stream","retriable-status-codes","cancelled","deadline-exceeded","internal","resource-exhausted","unavailable"}
 type TriggerEnum string
 
 const (
@@ -65,6 +65,8 @@ const (
 	GatewayError TriggerEnum = "gateway-error"
 	// The upstream server does not respond at all (disconnect/reset/read timeout.)
 	Reset TriggerEnum = "reset"
+	// Like reset, but only retry if the request headers have not been sent to the upstream server.
+	ResetBeforeRequest TriggerEnum = "reset-before-request"
 	// Connection failure to the upstream server (connect timeout, etc.). (Included in *5xx*)
 	ConnectFailure TriggerEnum = "connect-failure"
 	// The upstream server responds with a retriable 4xx response code.
diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go
index 7257d8f0b..a53936761 100644
--- a/api/v1alpha1/zz_generated.deepcopy.go
+++ b/api/v1alpha1/zz_generated.deepcopy.go
@@ -99,6 +99,16 @@ func (in *APIKeyAuth) DeepCopyInto(out *APIKeyAuth) {
 			}
 		}
 	}
+	if in.ForwardClientIDHeader != nil {
+		in, out := &in.ForwardClientIDHeader, &out.ForwardClientIDHeader
+		*out = new(string)
+		**out = **in
+	}
+	if in.Sanitize != nil {
+		in, out := &in.Sanitize, &out.Sanitize
+		*out = new(bool)
+		**out = **in
+	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new APIKeyAuth.
@@ -124,6 +134,11 @@ func (in *ActiveHealthCheck) DeepCopyInto(out *ActiveHealthCheck) {
 		*out = new(metav1.Duration)
 		**out = **in
 	}
+	if in.InitialJitter != nil {
+		in, out := &in.InitialJitter, &out.InitialJitter
+		*out = new(v1.Duration)
+		**out = **in
+	}
 	if in.UnhealthyThreshold != nil {
 		in, out := &in.UnhealthyThreshold, &out.UnhealthyThreshold
 		*out = new(uint32)
diff --git a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml
index 5059c61db..884f72a51 100644
--- a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml
+++ b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml
@@ -345,6 +345,13 @@ spec:
                         required:
                         - path
                         type: object
+                      initialJitter:
+                        description: |-
+                          InitialJitter defines the maximum time Envoy will wait before the first health check.
+                          Envoy will randomly select a value between 0 and the initial jitter value.
+                        format: duration
+                        pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+                        type: string
                       interval:
                         default: 3s
                         description: Interval defines the time between active health
@@ -1648,6 +1655,7 @@ spec:
                           - 5xx
                           - gateway-error
                           - reset
+                          - reset-before-request
                           - connect-failure
                           - retriable-4xx
                           - refused-stream
diff --git a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml
index d960c7066..efb39d5fc 100644
--- a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml
+++ b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml
@@ -443,6 +443,13 @@ spec:
                                   required:
                                   - path
                                   type: object
+                                initialJitter:
+                                  description: |-
+                                    InitialJitter defines the maximum time Envoy will wait before the first health check.
+                                    Envoy will randomly select a value between 0 and the initial jitter value.
+                                  format: duration
+                                  pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+                                  type: string
                                 interval:
                                   default: 3s
                                   description: Interval defines the time between active
@@ -905,6 +912,7 @@ spec:
                                     - 5xx
                                     - gateway-error
                                     - reset
+                                    - reset-before-request
                                     - connect-failure
                                     - retriable-4xx
                                     - refused-stream
@@ -1101,10 +1109,11 @@ spec:
                       ''gateway.envoyproxy.io'')) : true'
                   - message: If FullDuplexStreamed body processing mode is used, FailOpen
                       must be false.
-                    rule: '!(has(self.failOpen) && self.failOpen == true && ((has(self.processingMode.request.body)
+                    rule: '!(has(self.failOpen) && self.failOpen == true && has(self.processingMode)
+                      && ((has(self.processingMode.request) && has(self.processingMode.request.body)
                       && self.processingMode.request.body == ''FullDuplexStreamed'')
-                      || (has(self.processingMode.response.body) && self.processingMode.response.body
-                      == ''FullDuplexStreamed'')))'
+                      || (has(self.processingMode.response) && has(self.processingMode.response.body)
+                      && self.processingMode.response.body == ''FullDuplexStreamed'')))'
                 maxItems: 16
                 type: array
               lua:
diff --git a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyproxies.yaml b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyproxies.yaml
index e4df4ec33..c2b518ab3 100644
--- a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyproxies.yaml
+++ b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyproxies.yaml
@@ -11003,6 +11003,13 @@ spec:
                                                     required:
                                                     - path
                                                     type: object
+                                                  initialJitter:
+                                                    description: |-
+                                                      InitialJitter defines the maximum time Envoy will wait before the first health check.
+                                                      Envoy will randomly select a value between 0 and the initial jitter value.
+                                                    format: duration
+                                                    pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+                                                    type: string
                                                   interval:
                                                     default: 3s
                                                     description: Interval defines
@@ -11511,6 +11518,7 @@ spec:
                                                       - 5xx
                                                       - gateway-error
                                                       - reset
+                                                      - reset-before-request
                                                       - connect-failure
                                                       - retriable-4xx
                                                       - refused-stream
@@ -12075,6 +12083,13 @@ spec:
                                                     required:
                                                     - path
                                                     type: object
+                                                  initialJitter:
+                                                    description: |-
+                                                      InitialJitter defines the maximum time Envoy will wait before the first health check.
+                                                      Envoy will randomly select a value between 0 and the initial jitter value.
+                                                    format: duration
+                                                    pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+                                                    type: string
                                                   interval:
                                                     default: 3s
                                                     description: Interval defines
@@ -12583,6 +12598,7 @@ spec:
                                                       - 5xx
                                                       - gateway-error
                                                       - reset
+                                                      - reset-before-request
                                                       - connect-failure
                                                       - retriable-4xx
                                                       - refused-stream
@@ -13250,6 +13266,13 @@ spec:
                                               required:
                                               - path
                                               type: object
+                                            initialJitter:
+                                              description: |-
+                                                InitialJitter defines the maximum time Envoy will wait before the first health check.
+                                                Envoy will randomly select a value between 0 and the initial jitter value.
+                                              format: duration
+                                              pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+                                              type: string
                                             interval:
                                               default: 3s
                                               description: Interval defines the time
@@ -13734,6 +13757,7 @@ spec:
                                                 - 5xx
                                                 - gateway-error
                                                 - reset
+                                                - reset-before-request
                                                 - connect-failure
                                                 - retriable-4xx
                                                 - refused-stream
@@ -14327,6 +14351,13 @@ spec:
                                         required:
                                         - path
                                         type: object
+                                      initialJitter:
+                                        description: |-
+                                          InitialJitter defines the maximum time Envoy will wait before the first health check.
+                                          Envoy will randomly select a value between 0 and the initial jitter value.
+                                        format: duration
+                                        pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+                                        type: string
                                       interval:
                                         default: 3s
                                         description: Interval defines the time between
@@ -14807,6 +14838,7 @@ spec:
                                           - 5xx
                                           - gateway-error
                                           - reset
+                                          - reset-before-request
                                           - connect-failure
                                           - retriable-4xx
                                           - refused-stream
diff --git a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_securitypolicies.yaml b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_securitypolicies.yaml
index cb21ac230..8414c7f24 100644
--- a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_securitypolicies.yaml
+++ b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_securitypolicies.yaml
@@ -146,6 +146,15 @@ spec:
                           type: array
                       type: object
                     type: array
+                  forwardClientIDHeader:
+                    description: |-
+                      ForwardClientIDHeader is the name of the header to forward the client identity to the backend
+                      service. The header will be added to the request with the client id as the value.
+                    type: string
+                  sanitize:
+                    description: Sanitize indicates whether to remove the API key
+                      from the request before forwarding it to the backend service.
+                    type: boolean
                 required:
                 - credentialRefs
                 - extractFrom
@@ -938,6 +947,13 @@ spec:
                                     required:
                                     - path
                                     type: object
+                                  initialJitter:
+                                    description: |-
+                                      InitialJitter defines the maximum time Envoy will wait before the first health check.
+                                      Envoy will randomly select a value between 0 and the initial jitter value.
+                                    format: duration
+                                    pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+                                    type: string
                                   interval:
                                     default: 3s
                                     description: Interval defines the time between
@@ -1409,6 +1425,7 @@ spec:
                                       - 5xx
                                       - gateway-error
                                       - reset
+                                      - reset-before-request
                                       - connect-failure
                                       - retriable-4xx
                                       - refused-stream
@@ -1908,6 +1925,13 @@ spec:
                                     required:
                                     - path
                                     type: object
+                                  initialJitter:
+                                    description: |-
+                                      InitialJitter defines the maximum time Envoy will wait before the first health check.
+                                      Envoy will randomly select a value between 0 and the initial jitter value.
+                                    format: duration
+                                    pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+                                    type: string
                                   interval:
                                     default: 3s
                                     description: Interval defines the time between
@@ -2379,6 +2403,7 @@ spec:
                                       - 5xx
                                       - gateway-error
                                       - reset
+                                      - reset-before-request
                                       - connect-failure
                                       - retriable-4xx
                                       - refused-stream
@@ -3074,6 +3099,13 @@ spec:
                                           required:
                                           - path
                                           type: object
+                                        initialJitter:
+                                          description: |-
+                                            InitialJitter defines the maximum time Envoy will wait before the first health check.
+                                            Envoy will randomly select a value between 0 and the initial jitter value.
+                                          format: duration
+                                          pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+                                          type: string
                                         interval:
                                           default: 3s
                                           description: Interval defines the time between
@@ -3556,6 +3588,7 @@ spec:
                                             - 5xx
                                             - gateway-error
                                             - reset
+                                            - reset-before-request
                                             - connect-failure
                                             - retriable-4xx
                                             - refused-stream
@@ -4291,6 +4324,13 @@ spec:
                                     required:
                                     - path
                                     type: object
+                                  initialJitter:
+                                    description: |-
+                                      InitialJitter defines the maximum time Envoy will wait before the first health check.
+                                      Envoy will randomly select a value between 0 and the initial jitter value.
+                                    format: duration
+                                    pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+                                    type: string
                                   interval:
                                     default: 3s
                                     description: Interval defines the time between
@@ -4762,6 +4802,7 @@ spec:
                                       - 5xx
                                       - gateway-error
                                       - reset
+                                      - reset-before-request
                                       - connect-failure
                                       - retriable-4xx
                                       - refused-stream
diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml
index a1d2400b8..aa6a8c69a 100644
--- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml
+++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml
@@ -344,6 +344,13 @@ spec:
                         required:
                         - path
                         type: object
+                      initialJitter:
+                        description: |-
+                          InitialJitter defines the maximum time Envoy will wait before the first health check.
+                          Envoy will randomly select a value between 0 and the initial jitter value.
+                        format: duration
+                        pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+                        type: string
                       interval:
                         default: 3s
                         description: Interval defines the time between active health
@@ -1647,6 +1654,7 @@ spec:
                           - 5xx
                           - gateway-error
                           - reset
+                          - reset-before-request
                           - connect-failure
                           - retriable-4xx
                           - refused-stream
diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml
index d5f015472..22b5e3c1a 100644
--- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml
+++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml
@@ -442,6 +442,13 @@ spec:
                                   required:
                                   - path
                                   type: object
+                                initialJitter:
+                                  description: |-
+                                    InitialJitter defines the maximum time Envoy will wait before the first health check.
+                                    Envoy will randomly select a value between 0 and the initial jitter value.
+                                  format: duration
+                                  pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+                                  type: string
                                 interval:
                                   default: 3s
                                   description: Interval defines the time between active
@@ -904,6 +911,7 @@ spec:
                                     - 5xx
                                     - gateway-error
                                     - reset
+                                    - reset-before-request
                                     - connect-failure
                                     - retriable-4xx
                                     - refused-stream
@@ -1100,10 +1108,11 @@ spec:
                       ''gateway.envoyproxy.io'')) : true'
                   - message: If FullDuplexStreamed body processing mode is used, FailOpen
                       must be false.
-                    rule: '!(has(self.failOpen) && self.failOpen == true && ((has(self.processingMode.request.body)
+                    rule: '!(has(self.failOpen) && self.failOpen == true && has(self.processingMode)
+                      && ((has(self.processingMode.request) && has(self.processingMode.request.body)
                       && self.processingMode.request.body == ''FullDuplexStreamed'')
-                      || (has(self.processingMode.response.body) && self.processingMode.response.body
-                      == ''FullDuplexStreamed'')))'
+                      || (has(self.processingMode.response) && has(self.processingMode.response.body)
+                      && self.processingMode.response.body == ''FullDuplexStreamed'')))'
                 maxItems: 16
                 type: array
               lua:
diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml
index 59d44a753..ebe9d6c14 100644
--- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml
+++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml
@@ -11002,6 +11002,13 @@ spec:
                                                     required:
                                                     - path
                                                     type: object
+                                                  initialJitter:
+                                                    description: |-
+                                                      InitialJitter defines the maximum time Envoy will wait before the first health check.
+                                                      Envoy will randomly select a value between 0 and the initial jitter value.
+                                                    format: duration
+                                                    pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+                                                    type: string
                                                   interval:
                                                     default: 3s
                                                     description: Interval defines
@@ -11510,6 +11517,7 @@ spec:
                                                       - 5xx
                                                       - gateway-error
                                                       - reset
+                                                      - reset-before-request
                                                       - connect-failure
                                                       - retriable-4xx
                                                       - refused-stream
@@ -12074,6 +12082,13 @@ spec:
                                                     required:
                                                     - path
                                                     type: object
+                                                  initialJitter:
+                                                    description: |-
+                                                      InitialJitter defines the maximum time Envoy will wait before the first health check.
+                                                      Envoy will randomly select a value between 0 and the initial jitter value.
+                                                    format: duration
+                                                    pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+                                                    type: string
                                                   interval:
                                                     default: 3s
                                                     description: Interval defines
@@ -12582,6 +12597,7 @@ spec:
                                                       - 5xx
                                                       - gateway-error
                                                       - reset
+                                                      - reset-before-request
                                                       - connect-failure
                                                       - retriable-4xx
                                                       - refused-stream
@@ -13249,6 +13265,13 @@ spec:
                                               required:
                                               - path
                                               type: object
+                                            initialJitter:
+                                              description: |-
+                                                InitialJitter defines the maximum time Envoy will wait before the first health check.
+                                                Envoy will randomly select a value between 0 and the initial jitter value.
+                                              format: duration
+                                              pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+                                              type: string
                                             interval:
                                               default: 3s
                                               description: Interval defines the time
@@ -13733,6 +13756,7 @@ spec:
                                                 - 5xx
                                                 - gateway-error
                                                 - reset
+                                                - reset-before-request
                                                 - connect-failure
                                                 - retriable-4xx
                                                 - refused-stream
@@ -14326,6 +14350,13 @@ spec:
                                         required:
                                         - path
                                         type: object
+                                      initialJitter:
+                                        description: |-
+                                          InitialJitter defines the maximum time Envoy will wait before the first health check.
+                                          Envoy will randomly select a value between 0 and the initial jitter value.
+                                        format: duration
+                                        pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+                                        type: string
                                       interval:
                                         default: 3s
                                         description: Interval defines the time between
@@ -14806,6 +14837,7 @@ spec:
                                           - 5xx
                                           - gateway-error
                                           - reset
+                                          - reset-before-request
                                           - connect-failure
                                           - retriable-4xx
                                           - refused-stream
diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml
index 6cd283bac..d993f4677 100644
--- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml
+++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml
@@ -145,6 +145,15 @@ spec:
                           type: array
                       type: object
                     type: array
+                  forwardClientIDHeader:
+                    description: |-
+                      ForwardClientIDHeader is the name of the header to forward the client identity to the backend
+                      service. The header will be added to the request with the client id as the value.
+                    type: string
+                  sanitize:
+                    description: Sanitize indicates whether to remove the API key
+                      from the request before forwarding it to the backend service.
+                    type: boolean
                 required:
                 - credentialRefs
                 - extractFrom
@@ -937,6 +946,13 @@ spec:
                                     required:
                                     - path
                                     type: object
+                                  initialJitter:
+                                    description: |-
+                                      InitialJitter defines the maximum time Envoy will wait before the first health check.
+                                      Envoy will randomly select a value between 0 and the initial jitter value.
+                                    format: duration
+                                    pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+                                    type: string
                                   interval:
                                     default: 3s
                                     description: Interval defines the time between
@@ -1408,6 +1424,7 @@ spec:
                                       - 5xx
                                       - gateway-error
                                       - reset
+                                      - reset-before-request
                                       - connect-failure
                                       - retriable-4xx
                                       - refused-stream
@@ -1907,6 +1924,13 @@ spec:
                                     required:
                                     - path
                                     type: object
+                                  initialJitter:
+                                    description: |-
+                                      InitialJitter defines the maximum time Envoy will wait before the first health check.
+                                      Envoy will randomly select a value between 0 and the initial jitter value.
+                                    format: duration
+                                    pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+                                    type: string
                                   interval:
                                     default: 3s
                                     description: Interval defines the time between
@@ -2378,6 +2402,7 @@ spec:
                                       - 5xx
                                       - gateway-error
                                       - reset
+                                      - reset-before-request
                                       - connect-failure
                                       - retriable-4xx
                                       - refused-stream
@@ -3073,6 +3098,13 @@ spec:
                                           required:
                                           - path
                                           type: object
+                                        initialJitter:
+                                          description: |-
+                                            InitialJitter defines the maximum time Envoy will wait before the first health check.
+                                            Envoy will randomly select a value between 0 and the initial jitter value.
+                                          format: duration
+                                          pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+                                          type: string
                                         interval:
                                           default: 3s
                                           description: Interval defines the time between
@@ -3555,6 +3587,7 @@ spec:
                                             - 5xx
                                             - gateway-error
                                             - reset
+                                            - reset-before-request
                                             - connect-failure
                                             - retriable-4xx
                                             - refused-stream
@@ -4290,6 +4323,13 @@ spec:
                                     required:
                                     - path
                                     type: object
+                                  initialJitter:
+                                    description: |-
+                                      InitialJitter defines the maximum time Envoy will wait before the first health check.
+                                      Envoy will randomly select a value between 0 and the initial jitter value.
+                                    format: duration
+                                    pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+                                    type: string
                                   interval:
                                     default: 3s
                                     description: Interval defines the time between
@@ -4761,6 +4801,7 @@ spec:
                                       - 5xx
                                       - gateway-error
                                       - reset
+                                      - reset-before-request
                                       - connect-failure
                                       - retriable-4xx
                                       - refused-stream
diff --git a/examples/admin-console-config.yaml b/examples/admin-console-config.yaml
new file mode 100644
index 000000000..7fda3fc39
--- /dev/null
+++ b/examples/admin-console-config.yaml
@@ -0,0 +1,155 @@
+# Example EnvoyGateway configuration with Admin Console enabled
+apiVersion: gateway.envoyproxy.io/v1alpha1
+kind: EnvoyGateway
+metadata:
+  name: envoy-gateway-config
+  namespace: envoy-gateway-system
+spec:
+  # Gateway API configuration
+  gateway:
+    controllerName: gateway.envoyproxy.io/gatewayclass-controller
+
+  # Provider configuration
+  provider:
+    type: Kubernetes
+    kubernetes:
+      watch:
+        type: Namespaces
+        namespaces:
+        - default
+        - app-namespace
+
+  # Admin server configuration
+  admin:
+    # Web console is always enabled and provides access to:
+    # - Server status and component information
+    # - Configuration dump and EnvoyGateway settings
+    # - Performance metrics and statistics
+
+    # Enable pprof endpoints for debugging (default: false)
+    # Only enable in development/debugging scenarios
+    enablePprof: true
+
+    # Enable config dump in logs (default: false)
+    enableDumpConfig: false
+
+    # Admin server address configuration
+    address:
+      # Host to bind the admin server (default: 127.0.0.1)
+      # Use 127.0.0.1 for localhost-only access (recommended for security)
+      # Use 0.0.0.0 to allow external access (use with caution)
+      host: "127.0.0.1"
+
+      # Port for the admin server (default: 19000)
+      port: 19000
+
+  # Logging configuration
+  logging:
+    level:
+      default: info
+
+  # Telemetry configuration
+  telemetry:
+    metrics:
+      prometheus:
+        disable: false
+      sinks:
+      - type: OpenTelemetry
+        openTelemetry:
+          host: otel-collector.monitoring.svc.cluster.local
+          port: 4317
+          protocol: grpc
+
+---
+# Example: Production configuration with console disabled
+apiVersion: gateway.envoyproxy.io/v1alpha1
+kind: EnvoyGateway
+metadata:
+  name: envoy-gateway-production
+  namespace: envoy-gateway-system
+spec:
+  gateway:
+    controllerName: gateway.envoyproxy.io/gatewayclass-controller
+
+  provider:
+    type: Kubernetes
+    kubernetes:
+      watch:
+        type: Namespaces
+        namespaces:
+        - production
+        - staging
+
+  # Production admin configuration - more restrictive
+  admin:
+    # Disable pprof in production
+    enablePprof: false
+
+    # Disable config dump in logs
+    enableDumpConfig: false
+
+    address:
+      # Localhost only for security
+      host: "127.0.0.1"
+      port: 19000
+
+  logging:
+    level:
+      default: warn
+      # Enable debug logging for specific components if needed
+      # gateway-api: debug
+      # xds-translator: debug
+
+---
+# Example: Development configuration with all debugging features
+apiVersion: gateway.envoyproxy.io/v1alpha1
+kind: EnvoyGateway
+metadata:
+  name: envoy-gateway-development
+  namespace: envoy-gateway-system
+spec:
+  gateway:
+    controllerName: gateway.envoyproxy.io/gatewayclass-controller
+
+  provider:
+    type: Kubernetes
+    kubernetes:
+      watch:
+        type: Namespaces
+        namespaces:
+        - default
+        - development
+        - testing
+
+  # Development admin configuration - all features enabled
+  admin:
+    # Enable pprof for debugging
+    enablePprof: true
+
+    # Enable config dump for troubleshooting
+    enableDumpConfig: true
+
+    address:
+      # Allow external access for development (use with caution)
+      host: "0.0.0.0"
+      port: 19000
+
+  logging:
+    level:
+      default: debug
+      # Detailed logging for all components
+      gateway-api: debug
+      xds-translator: debug
+      xds-server: debug
+      infrastructure: debug
+
+  telemetry:
+    metrics:
+      prometheus:
+        disable: false
+      sinks:
+      - type: OpenTelemetry
+        openTelemetry:
+          host: jaeger-collector.observability.svc.cluster.local
+          port: 4317
+          protocol: grpc
diff --git a/examples/extension-server/go.mod b/examples/extension-server/go.mod
index d2bff4d7a..9b72dae0a 100644
--- a/examples/extension-server/go.mod
+++ b/examples/extension-server/go.mod
@@ -34,7 +34,7 @@ require (
 	github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
 	github.com/russross/blackfriday/v2 v2.1.0 // indirect
 	github.com/spf13/cobra v1.9.1 // indirect
-	github.com/spf13/pflag v1.0.6 // indirect
+	github.com/spf13/pflag v1.0.7 // indirect
 	github.com/x448/float16 v0.8.4 // indirect
 	github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
 	golang.org/x/mod v0.25.0 // indirect
diff --git a/examples/extension-server/go.sum b/examples/extension-server/go.sum
index c76aafe2b..8deddcd82 100644
--- a/examples/extension-server/go.sum
+++ b/examples/extension-server/go.sum
@@ -79,8 +79,9 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf
 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
 github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
-github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
 github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M=
+github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
 github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
diff --git a/examples/preserve-case-backend/go.mod b/examples/preserve-case-backend/go.mod
index 816cdc0a0..2e6b968d8 100644
--- a/examples/preserve-case-backend/go.mod
+++ b/examples/preserve-case-backend/go.mod
@@ -2,7 +2,7 @@ module github.com/envoyproxy/gateway-preserve-case-backend
 
 go 1.24.4
 
-require github.com/valyala/fasthttp v1.63.0
+require github.com/valyala/fasthttp v1.64.0
 
 require (
 	github.com/andybalholm/brotli v1.2.0 // indirect
diff --git a/examples/preserve-case-backend/go.sum b/examples/preserve-case-backend/go.sum
index 277646d01..3d992599b 100644
--- a/examples/preserve-case-backend/go.sum
+++ b/examples/preserve-case-backend/go.sum
@@ -4,7 +4,7 @@ github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zt
 github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
 github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
 github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
-github.com/valyala/fasthttp v1.63.0 h1:DisIL8OjB7ul2d7cBaMRcKTQDYnrGy56R4FCiuDP0Ns=
-github.com/valyala/fasthttp v1.63.0/go.mod h1:REc4IeW+cAEyLrRPa5A81MIjvz0QE1laoTX2EaPHKJM=
+github.com/valyala/fasthttp v1.64.0 h1:QBygLLQmiAyiXuRhthf0tuRkqAFcrC42dckN2S+N3og=
+github.com/valyala/fasthttp v1.64.0/go.mod h1:dGmFxwkWXSK0NbOSJuF7AMVzU+lkHz0wQVvVITv2UQA=
 github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
 github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
diff --git a/go.mod b/go.mod
index 967084f8e..922760859 100644
--- a/go.mod
+++ b/go.mod
@@ -10,10 +10,10 @@ require (
 	github.com/avast/retry-go v3.0.0+incompatible
 	github.com/cenkalti/backoff/v4 v4.3.0
 	github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f
-	github.com/containers/image/v5 v5.35.0
+	github.com/containers/image/v5 v5.36.0
 	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
 	github.com/docker/cli v28.3.2+incompatible
-	github.com/docker/docker v28.2.2+incompatible
+	github.com/docker/docker v28.3.2+incompatible
 	github.com/dominikbraun/graph v0.23.0
 	github.com/envoyproxy/go-control-plane v0.13.5-0.20250622153809-434b6986176d
 	github.com/envoyproxy/go-control-plane/contrib v1.32.5-0.20250430092421-68a532e11403
@@ -43,7 +43,7 @@ require (
 	github.com/replicatedhq/troubleshoot v0.120.3
 	github.com/shopspring/decimal v1.4.0
 	github.com/spf13/cobra v1.9.1
-	github.com/spf13/pflag v1.0.6
+	github.com/spf13/pflag v1.0.7
 	github.com/stretchr/testify v1.10.0
 	github.com/telepresenceio/watchable v0.0.0-20220726211108-9bb86f92afa7
 	github.com/tetratelabs/func-e v1.1.5
@@ -104,7 +104,7 @@ require (
 	cel.dev/expr v0.24.0 // indirect
 	connectrpc.com/connect v1.18.1 // indirect
 	connectrpc.com/otelconnect v0.7.2 // indirect
-	dario.cat/mergo v1.0.1 // indirect
+	dario.cat/mergo v1.0.2 // indirect
 	filippo.io/edwards25519 v1.1.0 // indirect
 	fortio.org/cli v1.10.0 // indirect
 	fortio.org/dflag v1.8.1 // indirect
@@ -130,7 +130,7 @@ require (
 	github.com/Masterminds/sprig/v3 v3.3.0 // indirect
 	github.com/Masterminds/squirrel v1.5.4 // indirect
 	github.com/Microsoft/go-winio v0.6.2 // indirect
-	github.com/Microsoft/hcsshim v0.12.9 // indirect
+	github.com/Microsoft/hcsshim v0.13.0 // indirect
 	github.com/NYTimes/gziphandler v1.1.1 // indirect
 	github.com/OpenPeeDeeP/depguard/v2 v2.2.1 // indirect
 	github.com/alecthomas/go-check-sumtype v0.3.1 // indirect
@@ -183,7 +183,7 @@ require (
 	github.com/containerd/typeurl/v2 v2.2.3 // indirect
 	github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 // indirect
 	github.com/containers/ocicrypt v1.2.1 // indirect
-	github.com/containers/storage v1.58.0 // indirect
+	github.com/containers/storage v1.59.0 // indirect
 	github.com/coreos/go-semver v0.3.1 // indirect
 	github.com/coreos/go-systemd/v22 v22.5.0 // indirect
 	github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
@@ -367,7 +367,6 @@ require (
 	github.com/opencontainers/image-spec v1.1.1 // indirect
 	github.com/opencontainers/runtime-spec v1.2.1 // indirect
 	github.com/opencontainers/selinux v1.12.0 // indirect
-	github.com/ostreedev/ostree-go v0.0.0-20210805093236-719684c64e4f // indirect
 	github.com/pelletier/go-toml v1.9.5 // indirect
 	github.com/pelletier/go-toml/v2 v2.2.3 // indirect
 	github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
@@ -394,7 +393,7 @@ require (
 	github.com/ryanrolds/sqlclosecheck v0.5.1 // indirect
 	github.com/sagikazarmark/locafero v0.7.0 // indirect
 	github.com/sanposhiho/wastedassign/v2 v2.1.0 // indirect
-	github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 // indirect
+	github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect
 	github.com/sashamelentyev/interfacebloat v1.1.0 // indirect
 	github.com/sashamelentyev/usestdlibvars v1.28.0 // indirect
 	github.com/securego/gosec/v2 v2.22.2 // indirect
@@ -419,7 +418,7 @@ require (
 	github.com/stretchr/objx v0.5.2 // indirect
 	github.com/subosito/gotenv v1.6.0 // indirect
 	github.com/sylabs/sif/v2 v2.21.1 // indirect
-	github.com/tchap/go-patricia/v2 v2.3.2 // indirect
+	github.com/tchap/go-patricia/v2 v2.3.3 // indirect
 	github.com/tdakkota/asciicheck v0.4.1 // indirect
 	github.com/tetafro/godot v1.5.0 // indirect
 	github.com/tetratelabs/wazero v1.9.0 // indirect
@@ -482,7 +481,7 @@ require (
 	golang.org/x/sys v0.34.0 // indirect
 	golang.org/x/term v0.33.0 // indirect
 	golang.org/x/text v0.27.0 // indirect
-	golang.org/x/time v0.10.0 // indirect
+	golang.org/x/time v0.11.0 // indirect
 	golang.org/x/tools v0.34.0 // indirect
 	golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
 	google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
diff --git a/go.sum b/go.sum
index 2f93dff1b..9e54f0493 100644
--- a/go.sum
+++ b/go.sum
@@ -37,8 +37,8 @@ connectrpc.com/connect v1.18.1 h1:PAg7CjSAGvscaf6YZKUefjoih5Z/qYkyaTrBW8xvYPw=
 connectrpc.com/connect v1.18.1/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8=
 connectrpc.com/otelconnect v0.7.2 h1:WlnwFzaW64dN06JXU+hREPUGeEzpz3Acz2ACOmN8cMI=
 connectrpc.com/otelconnect v0.7.2/go.mod h1:JS7XUKfuJs2adhCnXhNHPHLz6oAaZniCJdSF00OZSew=
-dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
-dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
+dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
+dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
 filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
 filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
 fortio.org/assert v1.2.1 h1:48I39urpeDj65RP1KguF7akCjILNeu6vICiYMEysR7Q=
@@ -61,8 +61,6 @@ fortio.org/struct2env v0.4.2 h1:Xh7HlS9vf2ZdRvRfmoGIasNDO8t6z36M713utVODRCo=
 fortio.org/struct2env v0.4.2/go.mod h1:lENUe70UwA1zDUCX+8AsO663QCFqYaprk5lnPhjD410=
 fortio.org/version v1.0.4 h1:FWUMpJ+hVTNc4RhvvOJzb0xesrlRmG/a+D6bjbQ4+5U=
 fortio.org/version v1.0.4/go.mod h1:2JQp9Ax+tm6QKiGuzR5nJY63kFeANcgrZ0osoQFDVm0=
-github.com/14rcole/gopopulate v0.0.0-20180821133914-b175b219e774 h1:SCbEWT58NSt7d2mcFdvxC9uyrdcTfvBbPLThhkDmXzg=
-github.com/14rcole/gopopulate v0.0.0-20180821133914-b175b219e774/go.mod h1:6/0dYRLLXyJjbkIPeeGyoJ/eKOSI0eU6eTlCBYibgd0=
 github.com/4meepo/tagalign v1.4.2 h1:0hcLHPGMjDyM1gHG58cS73aQF8J4TdVR96TZViorO9E=
 github.com/4meepo/tagalign v1.4.2/go.mod h1:+p4aMyFM+ra7nb41CnFG6aSDXqRxU/w1VQqScKqDARI=
 github.com/Abirdcfly/dupword v0.1.3 h1:9Pa1NuAsZvpFPi9Pqkd93I7LIYRURj+A//dFd5tgBeE=
@@ -124,8 +122,8 @@ github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8
 github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10=
 github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
 github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
-github.com/Microsoft/hcsshim v0.12.9 h1:2zJy5KA+l0loz1HzEGqyNnjd3fyZA31ZBCGKacp6lLg=
-github.com/Microsoft/hcsshim v0.12.9/go.mod h1:fJ0gkFAna6ukt0bLdKB8djt4XIJhF/vEPuoIWYVvZ8Y=
+github.com/Microsoft/hcsshim v0.13.0 h1:/BcXOiS6Qi7N9XqUcv27vkIuVOkBEcWstd2pMlWSeaA=
+github.com/Microsoft/hcsshim v0.13.0/go.mod h1:9KWJ/8DgU+QzYGupX4tzMhRQE8h6w90lH6HAaclpEok=
 github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
 github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
 github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
@@ -266,14 +264,14 @@ github.com/containerd/stargz-snapshotter/estargz v0.16.3 h1:7evrXtoh1mSbGj/pfRcc
 github.com/containerd/stargz-snapshotter/estargz v0.16.3/go.mod h1:uyr4BfYfOj3G9WBVE8cOlQmXAbPN9VEQpBBeJIuOipU=
 github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40=
 github.com/containerd/typeurl/v2 v2.2.3/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk=
-github.com/containers/image/v5 v5.35.0 h1:T1OeyWp3GjObt47bchwD9cqiaAm/u4O4R9hIWdrdrP8=
-github.com/containers/image/v5 v5.35.0/go.mod h1:8vTsgb+1gKcBL7cnjyNOInhJQfTUQjJoO2WWkKDoebM=
+github.com/containers/image/v5 v5.36.0 h1:Zh+xFcLjRmicnOT5AFPHH/xj+e3s9ojDN/9X2Kx1+Jo=
+github.com/containers/image/v5 v5.36.0/go.mod h1:VZ6cyDHbxZoOt4dklUJ+WNEH9FrgSgfH3qUBYKFlcT0=
 github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 h1:Qzk5C6cYglewc+UyGf6lc8Mj2UaPTHy/iF2De0/77CA=
 github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY=
 github.com/containers/ocicrypt v1.2.1 h1:0qIOTT9DoYwcKmxSt8QJt+VzMY18onl9jUXsxpVhSmM=
 github.com/containers/ocicrypt v1.2.1/go.mod h1:aD0AAqfMp0MtwqWgHM1bUwe1anx0VazI108CRrSKINQ=
-github.com/containers/storage v1.58.0 h1:Q7SyyCCjqgT3wYNgRNIL8o/wUS92heIj2/cc8Sewvcc=
-github.com/containers/storage v1.58.0/go.mod h1:w7Jl6oG+OpeLGLzlLyOZPkmUso40kjpzgrHUk5tyBlo=
+github.com/containers/storage v1.59.0 h1:r2pYSTzQpJTROZbjJQ54Z0GT+rUC6+wHzlSY8yPjsXk=
+github.com/containers/storage v1.59.0/go.mod h1:KoAYHnAjP3/cTsRS+mmWZGkufSY2GACiKQ4V3ZLQnR0=
 github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
 github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
 github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
@@ -330,8 +328,8 @@ github.com/docker/cli v28.3.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvM
 github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
 github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
 github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
-github.com/docker/docker v28.2.2+incompatible h1:CjwRSksz8Yo4+RmQ339Dp/D2tGO5JxwYeqtMOEe0LDw=
-github.com/docker/docker v28.2.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v28.3.2+incompatible h1:wn66NJ6pWB1vBZIilP8G3qQPqHy5XymfYn5vsqeA5oA=
+github.com/docker/docker v28.3.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8=
 github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo=
 github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
@@ -877,8 +875,8 @@ github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m
 github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
 github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
 github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
-github.com/mattn/go-sqlite3 v1.14.27 h1:drZCnuvf37yPfs95E5jd9s3XhdVWLal+6BOK6qrv6IU=
-github.com/mattn/go-sqlite3 v1.14.27/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
+github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A=
+github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
 github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
@@ -993,8 +991,6 @@ github.com/opencontainers/runtime-spec v1.2.1 h1:S4k4ryNgEpxW1dzyqffOmhI1BHYcjzU
 github.com/opencontainers/runtime-spec v1.2.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
 github.com/opencontainers/selinux v1.12.0 h1:6n5JV4Cf+4y0KNXW48TLj5DwfXpvWlxXplUkdTrmPb8=
 github.com/opencontainers/selinux v1.12.0/go.mod h1:BTPX+bjVbWGXw7ZZWUbdENt8w0htPSrlgOOysQaU62U=
-github.com/ostreedev/ostree-go v0.0.0-20210805093236-719684c64e4f h1:/UDgs8FGMqwnHagNDPGOlts35QkhAZ8by3DR7nMih7M=
-github.com/ostreedev/ostree-go v0.0.0-20210805093236-719684c64e4f/go.mod h1:J6OG6YJVEWopen4avK3VNQSnALmmjvniMmni/YFYAwc=
 github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw=
 github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU=
 github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w=
@@ -1102,8 +1098,8 @@ github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsF
 github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
 github.com/sanposhiho/wastedassign/v2 v2.1.0 h1:crurBF7fJKIORrV85u9UUpePDYGWnwvv3+A96WvwXT0=
 github.com/sanposhiho/wastedassign/v2 v2.1.0/go.mod h1:+oSmSC+9bQ+VUAxA66nBb0Z7N8CK7mscKTDYC6aIek4=
-github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 h1:PKK9DyHxif4LZo+uQSgXNqs0jj5+xZwwfKHgph2lxBw=
-github.com/santhosh-tekuri/jsonschema/v6 v6.0.1/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
+github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ=
+github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
 github.com/sashamelentyev/interfacebloat v1.1.0 h1:xdRdJp0irL086OyW1H/RTZTr1h/tMEOsumirXcOJqAw=
 github.com/sashamelentyev/interfacebloat v1.1.0/go.mod h1:+Y9yU5YdTkrNvoX0xHc84dxiN1iBi9+G8zZIhPVoNjQ=
 github.com/sashamelentyev/usestdlibvars v1.28.0 h1:jZnudE2zKCtYlGzLVreNp5pmCdOxXUzwsMDBkR21cyQ=
@@ -1168,8 +1164,9 @@ github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzu
 github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
 github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M=
+github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
 github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
 github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4=
@@ -1205,8 +1202,8 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8
 github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
 github.com/sylabs/sif/v2 v2.21.1 h1:GZ0b5//AFAqJEChd8wHV/uSKx/l1iuGYwjR8nx+4wPI=
 github.com/sylabs/sif/v2 v2.21.1/go.mod h1:YoqEGQnb5x/ItV653bawXHZJOXQaEWpGwHsSD3YePJI=
-github.com/tchap/go-patricia/v2 v2.3.2 h1:xTHFutuitO2zqKAQ5rCROYgUb7Or/+IC3fts9/Yc7nM=
-github.com/tchap/go-patricia/v2 v2.3.2/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k=
+github.com/tchap/go-patricia/v2 v2.3.3 h1:xfNEsODumaEcCcY3gI0hYPZ/PcpVv5ju6RMAhgwZDDc=
+github.com/tchap/go-patricia/v2 v2.3.3/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k=
 github.com/tdakkota/asciicheck v0.4.1 h1:bm0tbcmi0jezRA2b5kg4ozmMuGAFotKI3RZfrhfovg8=
 github.com/tdakkota/asciicheck v0.4.1/go.mod h1:0k7M3rCfRXb0Z6bwgvkEIMleKH3kXNz9UqJ9Xuqopr8=
 github.com/telepresenceio/telepresence/rpc/v2 v2.6.8 h1:q5V85LBT9bA/c4YPa/kMvJGyKZDgBPJTftlAMqJx7j4=
@@ -1319,8 +1316,8 @@ go-simpler.org/sloglint v0.9.0 h1:/40NQtjRx9txvsB/RN022KsUJU+zaaSb/9q9BSefSrE=
 go-simpler.org/sloglint v0.9.0/go.mod h1:G/OrAF6uxj48sHahCzrbarVMptL2kjWTaUeC8+fOGww=
 go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
-go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk=
-go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk=
+go.etcd.io/bbolt v1.4.2 h1:IrUHp260R8c+zYx/Tm8QZr04CX+qWS5PGfPdevhdm1I=
+go.etcd.io/bbolt v1.4.2/go.mod h1:Is8rSHO/b4f3XigBC0lL0+4FwAQv3HXEEIgFMuKHceM=
 go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738 h1:VcrIfasaLFkyjk6KNlXQSzO+B0fZcnECiDrKJsfxka0=
 go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
 go.etcd.io/etcd/api/v3 v3.5.21 h1:A6O2/JDb3tvHhiIz3xf9nJ7REHvtEFJJ3veW3FbCnS8=
@@ -1377,8 +1374,8 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 h1:dNzwXjZKpMpE2JhmO+9
 go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0/go.mod h1:90PoxvaEB5n6AOdZvi+yWJQoE95U8Dhhw2bSyRqnTD0=
 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 h1:5pojmb1U1AogINhN3SurB+zm/nIcusopeBNp42f45QM=
 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0/go.mod h1:57gTHJSE5S1tqg+EKsLPlTWhpHMsWlVmer+LA926XiA=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 h1:wpMfgF8E1rkrT1Z6meFh1NDtownE9Ii3n3X2GJYjsaU=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0/go.mod h1:wAy0T/dUbs468uOlkT31xjvqQgEVXv58BRFWEgn5v/0=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qHD0C1BeYVTLLR9sX12+Qb95kfeD/byKj6Ky1pXg=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk=
 go.opentelemetry.io/otel/exporters/prometheus v0.59.0 h1:HHf+wKS6o5++XZhS98wvILrLVgHxjA/AMjqHKes+uzo=
 go.opentelemetry.io/otel/exporters/prometheus v0.59.0/go.mod h1:R8GpRXTZrqvXHDEGVH5bF6+JqAZcK8PjJcZ5nGhEWiE=
 go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.8.0 h1:CHXNXwfKWfzS65yrlB2PVds1IBZcdsX8Vepy9of0iRU=
@@ -1590,8 +1587,8 @@ golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
 golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
-golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
+golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
 golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
diff --git a/internal/admin/console/api.go b/internal/admin/console/api.go
new file mode 100644
index 000000000..b68409e22
--- /dev/null
+++ b/internal/admin/console/api.go
@@ -0,0 +1,561 @@
+// Copyright Envoy Gateway Authors
+// SPDX-License-Identifier: Apache-2.0
+// The full text of the Apache license is available in the LICENSE file at
+// the root of the repo.
+
+package console
+
+import (
+	"encoding/json"
+	"net/http"
+	"runtime"
+	"time"
+
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
+
+	"github.com/envoyproxy/gateway/internal/cmd/version"
+)
+
+// SystemInfo represents basic system information
+type SystemInfo struct {
+	Version   string    `json:"version"`
+	StartTime time.Time `json:"startTime"`
+	Uptime    string    `json:"uptime"`
+	GoVersion string    `json:"goVersion"`
+	Platform  string    `json:"platform"`
+	Timestamp time.Time `json:"timestamp"`
+}
+
+// ServerInfo represents server status information
+type ServerInfo struct {
+	State              string            `json:"state"`
+	Version            string            `json:"version"`
+	Uptime             string            `json:"uptime"`
+	Components         []ComponentStatus `json:"components"`
+	EnvoyGatewayConfig interface{}       `json:"envoyGatewayConfig"`
+	LastUpdated        time.Time         `json:"lastUpdated"`
+}
+
+// ComponentStatus represents the status of a system component
+type ComponentStatus struct {
+	Name    string `json:"name"`
+	Status  string `json:"status"`
+	Message string `json:"message"`
+}
+
+// ConfigDumpInfo represents configuration dump information
+type ConfigDumpInfo struct {
+	// Gateway API Core Resources
+	Gateways     []ResourceSummary `json:"gateways"`
+	HTTPRoutes   []ResourceSummary `json:"httpRoutes"`
+	GRPCRoutes   []ResourceSummary `json:"grpcRoutes"`
+	TLSRoutes    []ResourceSummary `json:"tlsRoutes"`
+	TCPRoutes    []ResourceSummary `json:"tcpRoutes"`
+	UDPRoutes    []ResourceSummary `json:"udpRoutes"`
+	GatewayClass []ResourceSummary `json:"gatewayClass"`
+
+	// Envoy Gateway Policies
+	ClientTrafficPolicies  []ResourceSummary `json:"clientTrafficPolicies"`
+	BackendTrafficPolicies []ResourceSummary `json:"backendTrafficPolicies"`
+	BackendTLSPolicies     []ResourceSummary `json:"backendTLSPolicies"`
+	SecurityPolicies       []ResourceSummary `json:"securityPolicies"`
+	EnvoyPatchPolicies     []ResourceSummary `json:"envoyPatchPolicies"`
+	EnvoyExtensionPolicies []ResourceSummary `json:"envoyExtensionPolicies"`
+
+	// Kubernetes Resources
+	Services       []ResourceSummary `json:"services"`
+	Secrets        []ResourceSummary `json:"secrets"`
+	ConfigMaps     []ResourceSummary `json:"configMaps"`
+	Namespaces     []ResourceSummary `json:"namespaces"`
+	EndpointSlices []ResourceSummary `json:"endpointSlices"`
+
+	// Other Resources
+	ReferenceGrants  []ResourceSummary `json:"referenceGrants"`
+	HTTPRouteFilters []ResourceSummary `json:"httpRouteFilters"`
+	EnvoyProxies     []ResourceSummary `json:"envoyProxies"`
+	Backends         []ResourceSummary `json:"backends"`
+	ServiceImports   []ResourceSummary `json:"serviceImports"`
+
+	LastUpdated time.Time `json:"lastUpdated"`
+}
+
+// ResourceSummary represents a simplified summary of a Kubernetes resource
+type ResourceSummary struct {
+	Name      string `json:"name"`
+	Namespace string `json:"namespace"`
+}
+
+var startTime = time.Now()
+
+// handleAPIInfo returns basic system information
+func (h *Handler) handleAPIInfo(w http.ResponseWriter, r *http.Request) {
+	if r.Method != http.MethodGet {
+		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
+		return
+	}
+
+	uptime := time.Since(startTime)
+	versionInfo := version.Get()
+	info := SystemInfo{
+		Version:   versionInfo.EnvoyGatewayVersion,
+		StartTime: startTime,
+		Uptime:    uptime.String(),
+		GoVersion: runtime.Version(),
+		Platform:  runtime.GOOS + "/" + runtime.GOARCH,
+		Timestamp: time.Now(),
+	}
+
+	w.Header().Set("Content-Type", "application/json")
+	if err := json.NewEncoder(w).Encode(info); err != nil {
+		http.Error(w, "Failed to encode response", http.StatusInternalServerError)
+		return
+	}
+}
+
+// handleAPIServerInfo returns server status information
+func (h *Handler) handleAPIServerInfo(w http.ResponseWriter, r *http.Request) {
+	if r.Method != http.MethodGet {
+		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
+		return
+	}
+
+	uptime := time.Since(startTime)
+	versionInfo := version.Get()
+
+	// Since Admin Server starts last, if we can respond, all components are healthy
+	// This reflects the actual startup order in server.go
+	components := []ComponentStatus{
+		{
+			Name:    "Provider Service",
+			Status:  "Running",
+			Message: "Fetches resources from configured provider and publishes them",
+		},
+		{
+			Name:    "GatewayAPI Translator",
+			Status:  "Running",
+			Message: "Translates provider resources to xDS IR and infra IR",
+		},
+		{
+			Name:    "XDS Translator",
+			Status:  "Running",
+			Message: "Translates xDS IR into xDS resources and computes policy statuses",
+		},
+		{
+			Name:    "Infrastructure Manager",
+			Status:  "Running",
+			Message: "Translates infra IR into Envoy Proxy infrastructure resources",
+		},
+	}
+
+	info := ServerInfo{
+		State:              "Running",
+		Version:            versionInfo.EnvoyGatewayVersion,
+		Uptime:             uptime.String(),
+		Components:         components,
+		EnvoyGatewayConfig: h.cfg.EnvoyGateway,
+		LastUpdated:        time.Now(),
+	}
+
+	w.Header().Set("Content-Type", "application/json")
+	if err := json.NewEncoder(w).Encode(info); err != nil {
+		http.Error(w, "Failed to encode response", http.StatusInternalServerError)
+		return
+	}
+}
+
+// handleAPIConfigDump returns configuration dump information
+func (h *Handler) handleAPIConfigDump(w http.ResponseWriter, r *http.Request) {
+	if r.Method != http.MethodGet {
+		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
+		return
+	}
+
+	// Load provider resources from watchable
+	configDump := h.loadConfigDump()
+
+	w.Header().Set("Content-Type", "application/json")
+	if err := json.NewEncoder(w).Encode(configDump); err != nil {
+		http.Error(w, "Failed to encode response", http.StatusInternalServerError)
+		return
+	}
+}
+
+// loadConfigDump loads configuration data from provider resources
+func (h *Handler) loadConfigDump() ConfigDumpInfo {
+	configDump := ConfigDumpInfo{
+		// Gateway API Core Resources
+		Gateways:     []ResourceSummary{},
+		HTTPRoutes:   []ResourceSummary{},
+		GRPCRoutes:   []ResourceSummary{},
+		TLSRoutes:    []ResourceSummary{},
+		TCPRoutes:    []ResourceSummary{},
+		UDPRoutes:    []ResourceSummary{},
+		GatewayClass: []ResourceSummary{},
+
+		// Envoy Gateway Policies
+		ClientTrafficPolicies:  []ResourceSummary{},
+		BackendTrafficPolicies: []ResourceSummary{},
+		BackendTLSPolicies:     []ResourceSummary{},
+		SecurityPolicies:       []ResourceSummary{},
+		EnvoyPatchPolicies:     []ResourceSummary{},
+		EnvoyExtensionPolicies: []ResourceSummary{},
+
+		// Kubernetes Resources
+		Services:       []ResourceSummary{},
+		Secrets:        []ResourceSummary{},
+		ConfigMaps:     []ResourceSummary{},
+		Namespaces:     []ResourceSummary{},
+		EndpointSlices: []ResourceSummary{},
+
+		// Other Resources
+		ReferenceGrants:  []ResourceSummary{},
+		HTTPRouteFilters: []ResourceSummary{},
+		EnvoyProxies:     []ResourceSummary{},
+		Backends:         []ResourceSummary{},
+		ServiceImports:   []ResourceSummary{},
+
+		LastUpdated: time.Now(),
+	}
+
+	if h.providerResources != nil {
+		// Load controller resources directly from the provider resources
+		controllerResources := h.providerResources.GatewayAPIResources.LoadAll()
+
+		for _, resources := range controllerResources {
+			if resources == nil {
+				continue
+			}
+
+			for _, res := range *resources {
+				if res == nil {
+					continue
+				}
+
+				// Process GatewayClass
+				if res.GatewayClass != nil {
+					configDump.GatewayClass = append(configDump.GatewayClass, ResourceSummary{
+						Name:      res.GatewayClass.Name,
+						Namespace: "", // GatewayClass is cluster-scoped
+					})
+				}
+
+				// Process Gateways
+				for _, gateway := range res.Gateways {
+					if gateway != nil {
+						configDump.Gateways = append(configDump.Gateways, ResourceSummary{
+							Name:      gateway.Name,
+							Namespace: gateway.Namespace,
+						})
+					}
+				}
+
+				// Process HTTPRoutes
+				for _, httpRoute := range res.HTTPRoutes {
+					if httpRoute != nil {
+						configDump.HTTPRoutes = append(configDump.HTTPRoutes, ResourceSummary{
+							Name:      httpRoute.Name,
+							Namespace: httpRoute.Namespace,
+						})
+					}
+				}
+
+				// Process GRPCRoutes
+				for _, grpcRoute := range res.GRPCRoutes {
+					if grpcRoute != nil {
+						configDump.GRPCRoutes = append(configDump.GRPCRoutes, ResourceSummary{
+							Name:      grpcRoute.Name,
+							Namespace: grpcRoute.Namespace,
+						})
+					}
+				}
+
+				// Process TLSRoutes
+				for _, tlsRoute := range res.TLSRoutes {
+					if tlsRoute != nil {
+						configDump.TLSRoutes = append(configDump.TLSRoutes, ResourceSummary{
+							Name:      tlsRoute.Name,
+							Namespace: tlsRoute.Namespace,
+						})
+					}
+				}
+
+				// Process TCPRoutes
+				for _, tcpRoute := range res.TCPRoutes {
+					if tcpRoute != nil {
+						configDump.TCPRoutes = append(configDump.TCPRoutes, ResourceSummary{
+							Name:      tcpRoute.Name,
+							Namespace: tcpRoute.Namespace,
+						})
+					}
+				}
+
+				// Process UDPRoutes
+				for _, udpRoute := range res.UDPRoutes {
+					if udpRoute != nil {
+						configDump.UDPRoutes = append(configDump.UDPRoutes, ResourceSummary{
+							Name:      udpRoute.Name,
+							Namespace: udpRoute.Namespace,
+						})
+					}
+				}
+
+				// Process Envoy Gateway Policies
+				for _, policy := range res.ClientTrafficPolicies {
+					if policy != nil {
+						configDump.ClientTrafficPolicies = append(configDump.ClientTrafficPolicies, ResourceSummary{
+							Name:      policy.Name,
+							Namespace: policy.Namespace,
+						})
+					}
+				}
+
+				for _, policy := range res.BackendTrafficPolicies {
+					if policy != nil {
+						configDump.BackendTrafficPolicies = append(configDump.BackendTrafficPolicies, ResourceSummary{
+							Name:      policy.Name,
+							Namespace: policy.Namespace,
+						})
+					}
+				}
+
+				for _, policy := range res.BackendTLSPolicies {
+					if policy != nil {
+						configDump.BackendTLSPolicies = append(configDump.BackendTLSPolicies, ResourceSummary{
+							Name:      policy.Name,
+							Namespace: policy.Namespace,
+						})
+					}
+				}
+
+				for _, policy := range res.SecurityPolicies {
+					if policy != nil {
+						configDump.SecurityPolicies = append(configDump.SecurityPolicies, ResourceSummary{
+							Name:      policy.Name,
+							Namespace: policy.Namespace,
+						})
+					}
+				}
+
+				for _, policy := range res.EnvoyPatchPolicies {
+					if policy != nil {
+						configDump.EnvoyPatchPolicies = append(configDump.EnvoyPatchPolicies, ResourceSummary{
+							Name:      policy.Name,
+							Namespace: policy.Namespace,
+						})
+					}
+				}
+
+				for _, policy := range res.EnvoyExtensionPolicies {
+					if policy != nil {
+						configDump.EnvoyExtensionPolicies = append(configDump.EnvoyExtensionPolicies, ResourceSummary{
+							Name:      policy.Name,
+							Namespace: policy.Namespace,
+						})
+					}
+				}
+
+				// Process Kubernetes Resources
+				for _, service := range res.Services {
+					if service != nil {
+						configDump.Services = append(configDump.Services, ResourceSummary{
+							Name:      service.Name,
+							Namespace: service.Namespace,
+						})
+					}
+				}
+
+				for _, secret := range res.Secrets {
+					if secret != nil {
+						configDump.Secrets = append(configDump.Secrets, ResourceSummary{
+							Name:      secret.Name,
+							Namespace: secret.Namespace,
+						})
+					}
+				}
+
+				for _, configMap := range res.ConfigMaps {
+					if configMap != nil {
+						configDump.ConfigMaps = append(configDump.ConfigMaps, ResourceSummary{
+							Name:      configMap.Name,
+							Namespace: configMap.Namespace,
+						})
+					}
+				}
+
+				for _, namespace := range res.Namespaces {
+					if namespace != nil {
+						configDump.Namespaces = append(configDump.Namespaces, ResourceSummary{
+							Name:      namespace.Name,
+							Namespace: "", // Namespaces are cluster-scoped
+						})
+					}
+				}
+
+				for _, endpointSlice := range res.EndpointSlices {
+					if endpointSlice != nil {
+						configDump.EndpointSlices = append(configDump.EndpointSlices, ResourceSummary{
+							Name:      endpointSlice.Name,
+							Namespace: endpointSlice.Namespace,
+						})
+					}
+				}
+
+				// Process Other Resources
+				for _, refGrant := range res.ReferenceGrants {
+					if refGrant != nil {
+						configDump.ReferenceGrants = append(configDump.ReferenceGrants, ResourceSummary{
+							Name:      refGrant.Name,
+							Namespace: refGrant.Namespace,
+						})
+					}
+				}
+
+				for _, filter := range res.HTTPRouteFilters {
+					if filter != nil {
+						configDump.HTTPRouteFilters = append(configDump.HTTPRouteFilters, ResourceSummary{
+							Name:      filter.Name,
+							Namespace: filter.Namespace,
+						})
+					}
+				}
+
+				// Process EnvoyProxy for GatewayClass
+				if res.EnvoyProxyForGatewayClass != nil {
+					configDump.EnvoyProxies = append(configDump.EnvoyProxies, ResourceSummary{
+						Name:      res.EnvoyProxyForGatewayClass.Name,
+						Namespace: res.EnvoyProxyForGatewayClass.Namespace,
+					})
+				}
+
+				// Process EnvoyProxies for Gateways
+				for _, proxy := range res.EnvoyProxiesForGateways {
+					if proxy != nil {
+						configDump.EnvoyProxies = append(configDump.EnvoyProxies, ResourceSummary{
+							Name:      proxy.Name,
+							Namespace: proxy.Namespace,
+						})
+					}
+				}
+
+				for _, backend := range res.Backends {
+					if backend != nil {
+						configDump.Backends = append(configDump.Backends, ResourceSummary{
+							Name:      backend.Name,
+							Namespace: backend.Namespace,
+						})
+					}
+				}
+
+				for _, serviceImport := range res.ServiceImports {
+					if serviceImport != nil {
+						configDump.ServiceImports = append(configDump.ServiceImports, ResourceSummary{
+							Name:      serviceImport.Name,
+							Namespace: serviceImport.Namespace,
+						})
+					}
+				}
+			}
+		}
+	}
+
+	return configDump
+}
+
+// handleAPIConfigDumpAll handles requests for complete provider resources dump
+func (h *Handler) handleAPIConfigDumpAll(w http.ResponseWriter, r *http.Request) {
+	if r.Method != http.MethodGet {
+		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
+		return
+	}
+
+	w.Header().Set("Content-Type", "application/json; charset=utf-8")
+
+	if h.providerResources == nil {
+		// Return empty structure if no provider resources
+		emptyResponse := map[string]interface{}{
+			"message":   "No provider resources available",
+			"timestamp": time.Now(),
+		}
+		if err := json.NewEncoder(w).Encode(emptyResponse); err != nil {
+			http.Error(w, "Failed to encode response", http.StatusInternalServerError)
+		}
+		return
+	}
+
+	// Get the actual resources using GetResources() method
+	resources := h.providerResources.GetResources()
+
+	// Create a structured response with the actual resource data
+	response := map[string]interface{}{
+		"resources":  resources,
+		"timestamp":  time.Now(),
+		"totalCount": len(resources),
+	}
+
+	if err := json.NewEncoder(w).Encode(response); err != nil {
+		http.Error(w, "Failed to encode provider resources", http.StatusInternalServerError)
+		return
+	}
+}
+
+// handleAPIMetrics handles requests for metrics using the Prometheus registry
+func (h *Handler) handleAPIMetrics(w http.ResponseWriter, r *http.Request) {
+	if r.Method != http.MethodGet {
+		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
+		return
+	}
+
+	// Create a Prometheus handler using the controller-runtime registry
+	// This includes all Envoy Gateway control plane metrics
+	handler := createCombinedMetricsHandler()
+
+	// Serve the metrics using the Prometheus handler
+	handler.ServeHTTP(w, r)
+}
+
+// Helper functions to extract status information from Gateway API resources
+
+func getGatewayClassStatus(gc *gwapiv1.GatewayClass) string {
+	if len(gc.Status.Conditions) == 0 {
+		return "Unknown"
+	}
+
+	for _, condition := range gc.Status.Conditions {
+		if condition.Type == string(gwapiv1.GatewayClassConditionStatusAccepted) {
+			if condition.Status == metav1.ConditionTrue {
+				return "Accepted"
+			}
+			return "Not Accepted"
+		}
+	}
+	return "Unknown"
+}
+
+func getGatewayStatus(gw *gwapiv1.Gateway) string {
+	if len(gw.Status.Conditions) == 0 {
+		return "Unknown"
+	}
+
+	// Check for Programmed condition first
+	for _, condition := range gw.Status.Conditions {
+		if condition.Type == string(gwapiv1.GatewayConditionProgrammed) {
+			if condition.Status == metav1.ConditionTrue {
+				return "Programmed"
+			}
+			return "Not Programmed"
+		}
+	}
+
+	// Check for Accepted condition
+	for _, condition := range gw.Status.Conditions {
+		if condition.Type == string(gwapiv1.GatewayConditionAccepted) {
+			if condition.Status == metav1.ConditionTrue {
+				return "Accepted"
+			}
+			return "Not Accepted"
+		}
+	}
+
+	return "Unknown"
+}
diff --git a/internal/admin/console/embed.go b/internal/admin/console/embed.go
new file mode 100644
index 000000000..8323f02d1
--- /dev/null
+++ b/internal/admin/console/embed.go
@@ -0,0 +1,18 @@
+// Copyright Envoy Gateway Authors
+// SPDX-License-Identifier: Apache-2.0
+// The full text of the Apache license is available in the LICENSE file at
+// the root of the repo.
+
+package console
+
+import (
+	"embed"
+)
+
+// Embed static files and templates
+//
+//go:embed static
+var staticFiles embed.FS
+
+//go:embed templates
+var templateFiles embed.FS
diff --git a/internal/admin/console/handlers.go b/internal/admin/console/handlers.go
new file mode 100644
index 000000000..e665f5c4c
--- /dev/null
+++ b/internal/admin/console/handlers.go
@@ -0,0 +1,228 @@
+// Copyright Envoy Gateway Authors
+// SPDX-License-Identifier: Apache-2.0
+// The full text of the Apache license is available in the LICENSE file at
+// the root of the repo.
+
+package console
+
+import (
+	"fmt"
+	"html/template"
+	"io/fs"
+	"net/http"
+	"time"
+
+	egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1"
+	"github.com/envoyproxy/gateway/internal/envoygateway/config"
+	"github.com/envoyproxy/gateway/internal/message"
+)
+
+// Handler manages the console web interface
+type Handler struct {
+	cfg               *config.Server
+	templates         map[string]*template.Template
+	providerResources *message.ProviderResources
+}
+
+// NewHandler creates a new console handler
+func NewHandler(cfg *config.Server, providerResources *message.ProviderResources) *Handler {
+	return &Handler{
+		cfg:               cfg,
+		templates:         make(map[string]*template.Template),
+		providerResources: providerResources,
+	}
+}
+
+// RegisterRoutes registers all console routes with the provided mux
+func (h *Handler) RegisterRoutes(mux *http.ServeMux) {
+	// Web UI endpoints
+	mux.HandleFunc("/", h.handleIndex)
+	mux.HandleFunc("/pprof", h.handlePprof)
+	mux.HandleFunc("/server_info", h.handleServerInfo)
+	mux.HandleFunc("/config_dump", h.handleConfigDump)
+	mux.HandleFunc("/stats", h.handleStats)
+
+	// API endpoints
+	mux.HandleFunc("/api/info", h.handleAPIInfo)
+	mux.HandleFunc("/api/server_info", h.handleAPIServerInfo)
+	mux.HandleFunc("/api/config_dump", h.handleAPIConfigDump)
+	mux.HandleFunc("/api/config_dump_all", h.handleAPIConfigDumpAll)
+	mux.HandleFunc("/api/metrics", h.handleAPIMetrics)
+
+	// Static files
+	staticFS, err := fs.Sub(staticFiles, "static")
+	if err == nil {
+		mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(staticFS))))
+	}
+}
+
+// handleIndex serves the main console page
+func (h *Handler) handleIndex(w http.ResponseWriter, r *http.Request) {
+	if r.Method != http.MethodGet {
+		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
+		return
+	}
+
+	data := struct {
+		Title     string
+		Timestamp time.Time
+	}{
+		Title:     "Envoy Gateway Admin Console",
+		Timestamp: time.Now(),
+	}
+
+	if err := h.renderTemplate(w, "index.html", data); err != nil {
+		// Content-Type is already set by renderTemplate
+		w.WriteHeader(http.StatusInternalServerError)
+		_, _ = w.Write([]byte("Internal server error"))
+		return
+	}
+}
+
+// handlePprof serves the pprof page
+func (h *Handler) handlePprof(w http.ResponseWriter, r *http.Request) {
+	if r.Method != http.MethodGet {
+		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
+		return
+	}
+
+	enablePprof := h.cfg.EnvoyGateway.GetEnvoyGatewayAdmin().EnablePprof
+	data := struct {
+		Title       string
+		EnablePprof bool
+	}{
+		Title:       "Performance Profiling",
+		EnablePprof: enablePprof,
+	}
+
+	if err := h.renderTemplate(w, "pprof.html", data); err != nil {
+		// Content-Type is already set by renderTemplate
+		w.WriteHeader(http.StatusInternalServerError)
+		_, _ = w.Write([]byte("Internal server error"))
+		return
+	}
+}
+
+// handleServerInfo serves the server info page
+func (h *Handler) handleServerInfo(w http.ResponseWriter, r *http.Request) {
+	if r.Method != http.MethodGet {
+		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
+		return
+	}
+
+	data := struct {
+		Title string
+	}{
+		Title: "Server Information",
+	}
+
+	if err := h.renderTemplate(w, "server_info.html", data); err != nil {
+		// Content-Type is already set by renderTemplate
+		w.WriteHeader(http.StatusInternalServerError)
+		_, _ = w.Write([]byte("Internal server error"))
+		return
+	}
+}
+
+// handleConfigDump serves the config dump page
+func (h *Handler) handleConfigDump(w http.ResponseWriter, r *http.Request) {
+	if r.Method != http.MethodGet {
+		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
+		return
+	}
+
+	data := struct {
+		Title string
+	}{
+		Title: "Configuration Dump",
+	}
+
+	if err := h.renderTemplate(w, "config_dump.html", data); err != nil {
+		// Content-Type is already set by renderTemplate
+		w.WriteHeader(http.StatusInternalServerError)
+		_, _ = w.Write([]byte("Internal server error"))
+		return
+	}
+}
+
+// handleStats serves the stats page
+func (h *Handler) handleStats(w http.ResponseWriter, r *http.Request) {
+	if r.Method != http.MethodGet {
+		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
+		return
+	}
+
+	// Get metrics server address
+	metricsAddress := fmt.Sprintf("%s:%d", egv1a1.GatewayMetricsHost, egv1a1.GatewayMetricsPort)
+
+	data := struct {
+		Title          string
+		MetricsAddress string
+	}{
+		Title:          "Statistics",
+		MetricsAddress: metricsAddress,
+	}
+
+	if err := h.renderTemplate(w, "stats.html", data); err != nil {
+		// Content-Type is already set by renderTemplate
+		w.WriteHeader(http.StatusInternalServerError)
+		_, _ = w.Write([]byte("Internal server error"))
+		return
+	}
+}
+
+// renderTemplate renders a template with the given data
+func (h *Handler) renderTemplate(w http.ResponseWriter, tmpl string, data interface{}) error {
+	if len(h.templates) == 0 {
+		if err := h.loadTemplates(); err != nil {
+			// Set Content-Type before returning error
+			w.Header().Set("Content-Type", "text/html; charset=utf-8")
+			return err
+		}
+	}
+
+	w.Header().Set("Content-Type", "text/html; charset=utf-8")
+
+	// Get the specific template
+	t, exists := h.templates[tmpl]
+	if !exists {
+		return fmt.Errorf("template %s not found", tmpl)
+	}
+
+	return t.ExecuteTemplate(w, "base.html", data)
+}
+
+// loadTemplates loads all HTML templates
+func (h *Handler) loadTemplates() error {
+	// Template mappings: template name -> content block name
+	templateMappings := map[string]string{
+		"index.html":       "index-content",
+		"server_info.html": "server-info-content",
+		"config_dump.html": "config-dump-content",
+		"stats.html":       "stats-content",
+		"pprof.html":       "pprof-content",
+	}
+
+	// Create individual templates for each page
+	for templateName, contentBlock := range templateMappings {
+		// Create a new template instance for each page
+		tmpl := template.New(templateName)
+
+		// Parse all templates together
+		tmpl, err := tmpl.ParseFS(templateFiles, "templates/base.html", "templates/"+templateName)
+		if err != nil {
+			return fmt.Errorf("failed to parse templates for %s: %w", templateName, err)
+		}
+
+		// Create a wrapper template that calls the correct content block
+		wrapperTemplate := fmt.Sprintf(`{{define "content"}}{{template "%s" .}}{{end}}`, contentBlock)
+		tmpl, err = tmpl.Parse(wrapperTemplate)
+		if err != nil {
+			return fmt.Errorf("failed to parse wrapper for %s: %w", templateName, err)
+		}
+
+		h.templates[templateName] = tmpl
+	}
+
+	return nil
+}
diff --git a/internal/admin/console/metrics.go b/internal/admin/console/metrics.go
new file mode 100644
index 000000000..8c74fda06
--- /dev/null
+++ b/internal/admin/console/metrics.go
@@ -0,0 +1,33 @@
+// Copyright Envoy Gateway Authors
+// SPDX-License-Identifier: Apache-2.0
+// The full text of the Apache license is available in the LICENSE file at
+// the root of the repo.
+
+package console
+
+import (
+	"net/http"
+
+	"github.com/prometheus/client_golang/prometheus"
+	"github.com/prometheus/client_golang/prometheus/promhttp"
+	metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics"
+)
+
+// createCombinedMetricsHandler creates a handler that uses the controller-runtime registry
+func createCombinedMetricsHandler() http.Handler {
+	// Check if the controller-runtime registry has any metrics
+	// In test environments, it might be empty
+	if hasMetrics(metricsserver.Registry) {
+		// Use the controller-runtime registry which includes all Envoy Gateway metrics
+		return promhttp.HandlerFor(metricsserver.Registry, promhttp.HandlerOpts{})
+	}
+
+	// Fallback to default gatherer for test environments
+	return promhttp.Handler()
+}
+
+// hasMetrics checks if a gatherer has any metrics
+func hasMetrics(gatherer prometheus.Gatherer) bool {
+	families, err := gatherer.Gather()
+	return err == nil && len(families) > 0
+}
diff --git a/internal/admin/console/static/css/admin.css b/internal/admin/console/static/css/admin.css
new file mode 100644
index 000000000..3c6a54c03
--- /dev/null
+++ b/internal/admin/console/static/css/admin.css
@@ -0,0 +1,641 @@
+/* Envoy Gateway Admin Console Styles */
+
+/* Reset and base styles */
+* {
+    margin: 0;
+    padding: 0;
+    box-sizing: border-box;
+}
+
+html, body {
+    height: 100%;
+}
+
+body {
+    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
+    line-height: 1.6;
+    color: #333;
+    background-color: #f5f5f5;
+    display: flex;
+    flex-direction: column;
+    min-height: 100vh;
+}
+
+/* Header */
+.header {
+    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+    color: white;
+    padding: 1rem 0;
+    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+}
+
+.header-content {
+    max-width: 1200px;
+    margin: 0 auto;
+    padding: 0 2rem;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+}
+
+.logo {
+    font-size: 1.5rem;
+    font-weight: bold;
+}
+
+.nav {
+    display: flex;
+    gap: 2rem;
+}
+
+.nav a {
+    color: white;
+    text-decoration: none;
+    padding: 0.5rem 1rem;
+    border-radius: 4px;
+    transition: background-color 0.3s;
+}
+
+.nav a:hover,
+.nav a.active {
+    background-color: rgba(255, 255, 255, 0.2);
+}
+
+/* Main content */
+.container {
+    max-width: 1200px;
+    margin: 2rem auto;
+    padding: 0 2rem;
+    flex: 1; /* This makes the container expand to fill available space */
+}
+
+.card {
+    background: white;
+    border-radius: 8px;
+    box-shadow: 0 2px 8px rgba(0,0,0,0.1);
+    margin-bottom: 2rem;
+    overflow: hidden;
+}
+
+.card-header {
+    background: #f8f9fa;
+    padding: 1rem 1.5rem;
+    border-bottom: 1px solid #e9ecef;
+}
+
+.card-title {
+    font-size: 1.25rem;
+    font-weight: 600;
+    color: #495057;
+}
+
+.card-body {
+    padding: 1.5rem;
+}
+
+/* Grid layout */
+.grid {
+    display: grid;
+    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
+    gap: 2rem;
+    margin-bottom: 2rem;
+}
+
+/* Dashboard 2x2 grid layout */
+.dashboard-grid {
+    display: grid;
+    grid-template-columns: repeat(2, 1fr);
+    gap: 2rem;
+    margin-bottom: 2rem;
+}
+
+/* Config Summary 6x4 grid layout */
+.config-summary-grid {
+    display: grid;
+    grid-template-columns: repeat(6, 1fr);
+    gap: 1.5rem;
+    margin-bottom: 2rem;
+}
+
+/* Config Resources 4x6 grid layout */
+.config-resources-grid {
+    display: grid;
+    grid-template-columns: repeat(4, 1fr);
+    gap: 1.5rem;
+    margin-bottom: 2rem;
+}
+
+/* Quick Links styles */
+.quick-links-grid {
+    display: grid;
+    grid-template-columns: repeat(2, 1fr);
+    gap: 2rem;
+}
+
+.quick-links-section h3 {
+    margin-bottom: 1rem;
+    color: #495057;
+    font-size: 1.1rem;
+    font-weight: 600;
+}
+
+.quick-links-list {
+    display: flex;
+    flex-direction: column;
+    gap: 0.75rem;
+}
+
+.quick-link {
+    display: flex;
+    align-items: center;
+    padding: 0.75rem 1rem;
+    background-color: #f8f9fa;
+    border: 1px solid #e9ecef;
+    border-radius: 0.5rem;
+    text-decoration: none;
+    color: #495057;
+    transition: all 0.2s ease;
+}
+
+.quick-link:hover {
+    background-color: #e9ecef;
+    border-color: #007bff;
+    color: #007bff;
+    text-decoration: none;
+    transform: translateY(-1px);
+    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+}
+
+.quick-link-icon {
+    font-size: 1.2rem;
+    margin-right: 0.75rem;
+    min-width: 1.5rem;
+}
+
+.quick-link-text {
+    font-weight: 500;
+}
+
+/* Resource item styles */
+.resource-item {
+    padding: 1rem;
+    border: 1px solid #e9ecef;
+    border-radius: 0.5rem;
+    background-color: #fff;
+    margin-bottom: 0.5rem;
+}
+
+.resource-header {
+    display: flex;
+    flex-direction: column;
+    gap: 0.5rem;
+}
+
+.resource-name {
+    font-weight: 600;
+    font-size: 1rem;
+    color: #495057;
+}
+
+.resource-namespace {
+    font-size: 0.875rem;
+    color: #6c757d;
+    background-color: #f8f9fa;
+    padding: 0.25rem 0.5rem;
+    border-radius: 0.25rem;
+    display: inline-block;
+    width: fit-content;
+}
+
+.resource-scope {
+    font-size: 0.875rem;
+    color: #6c757d;
+    background-color: #e3f2fd;
+    padding: 0.25rem 0.5rem;
+    border-radius: 0.25rem;
+    display: inline-block;
+    width: fit-content;
+}
+
+/* Status indicators */
+.status {
+    display: inline-block;
+    padding: 0.25rem 0.75rem;
+    border-radius: 12px;
+    font-size: 0.875rem;
+    font-weight: 500;
+}
+
+.status.running {
+    background-color: #d4edda;
+    color: #155724;
+}
+
+.status.warning {
+    background-color: #fff3cd;
+    color: #856404;
+}
+
+.status.error {
+    background-color: #f8d7da;
+    color: #721c24;
+}
+
+.status.accepted {
+    background-color: #d4edda;
+    color: #155724;
+}
+
+.status.programmed {
+    background-color: #d4edda;
+    color: #155724;
+}
+
+.status.pending {
+    background-color: #fff3cd;
+    color: #856404;
+}
+
+.status.invalid {
+    background-color: #f8d7da;
+    color: #721c24;
+}
+
+/* Resource items */
+.resource-item {
+    border: 1px solid #e9ecef;
+    border-radius: 8px;
+    padding: 1rem;
+    margin-bottom: 1rem;
+    background-color: #fff;
+}
+
+.resource-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 0.5rem;
+}
+
+.resource-details {
+    color: #6c757d;
+    font-size: 0.875rem;
+}
+
+/* Config summary */
+.summary-item {
+    text-align: center;
+    padding: 1rem;
+    background-color: #f8f9fa;
+    border-radius: 8px;
+}
+
+.summary-item h3 {
+    font-size: 2rem;
+    margin: 0 0 0.5rem 0;
+    color: #007bff;
+}
+
+.summary-item p {
+    margin: 0;
+    color: #6c757d;
+}
+
+.text-muted {
+    color: #6c757d;
+    font-style: italic;
+}
+
+/* Namespace and cluster badges */
+.namespace-badge {
+    background-color: #e9ecef;
+    color: #495057;
+    padding: 0.25rem 0.5rem;
+    border-radius: 4px;
+    font-size: 0.75rem;
+    font-weight: 500;
+}
+
+.cluster-scoped {
+    background-color: #fff3cd;
+    color: #856404;
+    padding: 0.25rem 0.5rem;
+    border-radius: 4px;
+    font-size: 0.75rem;
+    font-weight: 500;
+}
+
+/* Button styles */
+.btn-primary {
+    background-color: #007bff;
+    border-color: #007bff;
+    color: white;
+    text-decoration: none;
+    padding: 0.375rem 0.75rem;
+    border-radius: 0.25rem;
+    display: inline-block;
+    font-weight: 400;
+    text-align: center;
+    vertical-align: middle;
+    border: 1px solid transparent;
+    font-size: 1rem;
+    line-height: 1.5;
+}
+
+.btn-primary:hover {
+    background-color: #0056b3;
+    border-color: #004085;
+    color: white;
+    text-decoration: none;
+}
+
+.btn-secondary {
+    background-color: #6c757d;
+    border-color: #6c757d;
+    color: white;
+    text-decoration: none;
+    padding: 0.375rem 0.75rem;
+    border-radius: 0.25rem;
+    display: inline-block;
+    font-weight: 400;
+    text-align: center;
+    vertical-align: middle;
+    border: 1px solid transparent;
+    font-size: 1rem;
+    line-height: 1.5;
+    margin-left: 0.5rem;
+}
+
+.btn-secondary:hover {
+    background-color: #545b62;
+    border-color: #4e555b;
+    color: white;
+    text-decoration: none;
+}
+
+/* Tables */
+.table {
+    width: 100%;
+    border-collapse: collapse;
+    margin-top: 1rem;
+}
+
+.table th,
+.table td {
+    padding: 0.75rem;
+    text-align: left;
+    border-bottom: 1px solid #e9ecef;
+}
+
+.table th {
+    background-color: #f8f9fa;
+    font-weight: 600;
+    color: #495057;
+}
+
+.table tbody tr:hover {
+    background-color: #f8f9fa;
+}
+
+/* Buttons */
+.btn {
+    display: inline-block;
+    padding: 0.5rem 1rem;
+    background-color: #007bff;
+    color: white;
+    text-decoration: none;
+    border-radius: 4px;
+    border: none;
+    cursor: pointer;
+    transition: background-color 0.3s;
+}
+
+.btn:hover {
+    background-color: #0056b3;
+}
+
+.btn-secondary {
+    background-color: #6c757d;
+}
+
+.btn-secondary:hover {
+    background-color: #545b62;
+}
+
+/* Info boxes */
+.info-box {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 1rem;
+    background-color: #e7f3ff;
+    border-left: 4px solid #007bff;
+    margin-bottom: 1rem;
+}
+
+.info-box.warning {
+    background-color: #fff8e1;
+    border-left-color: #ff9800;
+}
+
+.info-box.error {
+    background-color: #ffebee;
+    border-left-color: #f44336;
+}
+
+/* Code blocks */
+.code {
+    background-color: #f8f9fa;
+    border: 1px solid #e9ecef;
+    border-radius: 4px;
+    padding: 1rem;
+    font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
+    font-size: 0.875rem;
+    overflow-x: auto;
+    white-space: pre-wrap;
+    word-wrap: break-word;
+    line-height: 1.4;
+}
+
+/* JSON code blocks with syntax highlighting */
+.json-code {
+    background-color: #f8f9fa;
+    border: 1px solid #e9ecef;
+    border-radius: 6px;
+    padding: 1.25rem;
+    font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', monospace;
+    font-size: 0.875rem;
+    overflow-x: auto;
+    white-space: pre;
+    line-height: 1.5;
+    max-height: 500px;
+    overflow-y: auto;
+    margin-top: 0.75rem;
+}
+
+/* JSON syntax highlighting */
+.json-key {
+    color: #0066cc;
+    font-weight: 600;
+}
+
+.json-string {
+    color: #008000;
+}
+
+.json-number {
+    color: #ff6600;
+}
+
+.json-boolean {
+    color: #cc0066;
+    font-weight: 600;
+}
+
+.json-null {
+    color: #999999;
+    font-style: italic;
+}
+
+/* Config details toggle */
+.config-details {
+    margin-top: 1rem;
+    border-top: 1px solid #e9ecef;
+    padding-top: 1rem;
+}
+
+.config-details.collapsed {
+    display: none;
+}
+
+.config-details.expanded {
+    display: block;
+    animation: slideDown 0.3s ease-out;
+}
+
+@keyframes slideDown {
+    from {
+        opacity: 0;
+        max-height: 0;
+    }
+    to {
+        opacity: 1;
+        max-height: 500px;
+    }
+}
+
+/* Success button state */
+.btn-success {
+    background-color: #28a745 !important;
+    border-color: #28a745 !important;
+    color: white !important;
+}
+
+/* Button hover effects */
+.btn:hover {
+    transform: translateY(-1px);
+    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+    transition: all 0.2s ease;
+}
+
+/* Copy button specific styling */
+.btn.copy-btn {
+    transition: all 0.3s ease;
+}
+
+.btn.copy-btn:active {
+    transform: translateY(0);
+}
+
+/* Loading spinner */
+.loading {
+    display: inline-block;
+    width: 20px;
+    height: 20px;
+    border: 3px solid #f3f3f3;
+    border-top: 3px solid #007bff;
+    border-radius: 50%;
+    animation: spin 1s linear infinite;
+}
+
+@keyframes spin {
+    0% { transform: rotate(0deg); }
+    100% { transform: rotate(360deg); }
+}
+
+/* Responsive design */
+@media (max-width: 768px) {
+    .header-content {
+        flex-direction: column;
+        gap: 1rem;
+    }
+    
+    .nav {
+        flex-wrap: wrap;
+        justify-content: center;
+    }
+    
+    .container {
+        padding: 0 1rem;
+    }
+    
+    .grid {
+        grid-template-columns: 1fr;
+    }
+
+    .dashboard-grid {
+        grid-template-columns: 1fr;
+    }
+
+    .config-summary-grid {
+        grid-template-columns: repeat(3, 1fr);
+    }
+
+    .config-resources-grid {
+        grid-template-columns: repeat(2, 1fr);
+    }
+}
+
+/* Medium screens */
+@media (max-width: 1024px) {
+    .config-summary-grid {
+        grid-template-columns: repeat(4, 1fr);
+    }
+
+    .config-resources-grid {
+        grid-template-columns: repeat(3, 1fr);
+    }
+}
+
+/* Small screens */
+@media (max-width: 768px) {
+    .config-summary-grid {
+        grid-template-columns: repeat(2, 1fr);
+    }
+
+    .config-resources-grid {
+        grid-template-columns: repeat(1, 1fr);
+    }
+
+    .quick-links-grid {
+        grid-template-columns: 1fr;
+    }
+}
+
+/* Footer */
+.footer {
+    background-color: #343a40;
+    color: #adb5bd;
+    text-align: center;
+    padding: 2rem 0;
+    margin-top: auto; /* This pushes the footer to the bottom */
+}
+
+.footer a {
+    color: #007bff;
+    text-decoration: none;
+}
+
+.footer a:hover {
+    text-decoration: underline;
+}
diff --git a/internal/admin/console/static/js/admin.js b/internal/admin/console/static/js/admin.js
new file mode 100644
index 000000000..4bce3bb10
--- /dev/null
+++ b/internal/admin/console/static/js/admin.js
@@ -0,0 +1,677 @@
+// Envoy Gateway Admin Console JavaScript
+
+// Global app object
+window.EnvoyGatewayAdmin = {
+    // Configuration
+    config: {
+        refreshInterval: 30000, // 30 seconds
+        apiTimeout: 10000 // 10 seconds
+    },
+    
+    // State
+    state: {
+        autoRefresh: false,
+        currentPage: null
+    },
+    
+    // Initialize the application
+    init: function() {
+        this.setupNavigation();
+        this.setupAutoRefresh();
+        this.detectCurrentPage();
+        this.loadPageData();
+    },
+    
+    // Setup navigation highlighting
+    setupNavigation: function() {
+        const currentPath = window.location.pathname;
+        const navLinks = document.querySelectorAll('.nav a');
+        
+        navLinks.forEach(link => {
+            if (link.getAttribute('href') === currentPath) {
+                link.classList.add('active');
+            }
+        });
+    },
+    
+    // Setup auto-refresh functionality
+    setupAutoRefresh: function() {
+        const refreshToggle = document.getElementById('auto-refresh');
+        if (refreshToggle) {
+            refreshToggle.addEventListener('change', (e) => {
+                this.state.autoRefresh = e.target.checked;
+                if (this.state.autoRefresh) {
+                    this.startAutoRefresh();
+                } else {
+                    this.stopAutoRefresh();
+                }
+            });
+        }
+    },
+    
+    // Detect current page
+    detectCurrentPage: function() {
+        const path = window.location.pathname;
+        if (path === '/' || path === '/index') {
+            this.state.currentPage = 'index';
+        } else if (path === '/server_info') {
+            this.state.currentPage = 'server_info';
+        } else if (path === '/config_dump') {
+            this.state.currentPage = 'config_dump';
+        } else if (path === '/stats') {
+            this.state.currentPage = 'stats';
+        } else if (path === '/pprof') {
+            this.state.currentPage = 'pprof';
+        }
+    },
+    
+    // Load page-specific data
+    loadPageData: function() {
+        switch (this.state.currentPage) {
+            case 'index':
+                this.loadSystemInfo();
+                break;
+            case 'server_info':
+                this.loadServerInfo();
+                break;
+            case 'config_dump':
+                this.loadConfigDump();
+                break;
+        }
+    },
+    
+    // API call helper
+    apiCall: function(endpoint, callback) {
+        const controller = new AbortController();
+        const timeoutId = setTimeout(() => controller.abort(), this.config.apiTimeout);
+        
+        fetch(endpoint, {
+            signal: controller.signal,
+            headers: {
+                'Accept': 'application/json'
+            }
+        })
+        .then(response => {
+            clearTimeout(timeoutId);
+            if (!response.ok) {
+                throw new Error(`HTTP ${response.status}: ${response.statusText}`);
+            }
+            return response.json();
+        })
+        .then(data => callback(null, data))
+        .catch(error => {
+            clearTimeout(timeoutId);
+            callback(error, null);
+        });
+    },
+    
+    // Load system information
+    loadSystemInfo: function() {
+        this.showLoading('system-info');
+        this.apiCall('/api/info', (error, data) => {
+            this.hideLoading('system-info');
+            if (error) {
+                this.showError('system-info', 'Failed to load system information: ' + error.message);
+                return;
+            }
+            this.updateSystemInfo(data);
+        });
+    },
+    
+    // Update system information display
+    updateSystemInfo: function(data) {
+        const container = document.getElementById('system-info');
+        if (!container) return;
+
+        container.innerHTML = `
+            <div class="info-box">
+                <div>
+                    <strong>Version:</strong> ${data.version}<br>
+                    <strong>Uptime:</strong> ${data.uptime}<br>
+                    <strong>Go Version:</strong> ${data.goVersion}<br>
+                    <strong>Platform:</strong> ${data.platform}
+                </div>
+                <div>
+                    <small>Last updated: ${new Date(data.timestamp).toLocaleString()}</small>
+                </div>
+            </div>
+        `;
+    },
+
+
+
+    // Update EnvoyGateway configuration display
+    updateEnvoyGatewayConfig: function(data) {
+        const container = document.getElementById('envoy-gateway-config');
+        if (!container) return;
+
+        container.innerHTML = `
+            <div class="info-box">
+                <div>
+                    <strong>API Version:</strong> ${data.apiVersion || 'N/A'}<br>
+                    <strong>Kind:</strong> ${data.kind || 'N/A'}<br>
+                    <strong>Controller Name:</strong> ${data.gateway?.controllerName || 'N/A'}<br>
+                    <strong>Provider Type:</strong> ${data.provider?.type || 'N/A'}
+                </div>
+                <div>
+                    <button class="btn btn-secondary" onclick="EnvoyGatewayAdmin.toggleConfigDetails()" id="config-toggle-btn">
+                        Show Details
+                    </button>
+                    <button class="btn btn-secondary" onclick="EnvoyGatewayAdmin.copyConfigToClipboard(event)" style="margin-left: 0.5rem;">
+                        Copy JSON
+                    </button>
+                </div>
+            </div>
+            <div id="config-details" class="config-details collapsed">
+                <div class="json-code" id="config-json">${this.formatJSON(data)}</div>
+            </div>
+        `;
+    },
+
+    // Toggle configuration details
+    toggleConfigDetails: function() {
+        const details = document.getElementById('config-details');
+        const button = document.getElementById('config-toggle-btn');
+
+        if (details && button) {
+            if (details.classList.contains('collapsed')) {
+                details.classList.remove('collapsed');
+                details.classList.add('expanded');
+                button.textContent = 'Hide Details';
+            } else {
+                details.classList.remove('expanded');
+                details.classList.add('collapsed');
+                button.textContent = 'Show Details';
+            }
+        }
+    },
+
+    // Copy configuration to clipboard
+    copyConfigToClipboard: function(event) {
+        const configElement = document.getElementById('config-json');
+        if (configElement && event && event.target) {
+            const text = configElement.textContent;
+            const button = event.target;
+
+            if (navigator.clipboard && navigator.clipboard.writeText) {
+                navigator.clipboard.writeText(text).then(() => {
+                    this.showCopySuccess(button);
+                }).catch(err => {
+                    console.error('Failed to copy to clipboard:', err);
+                    this.fallbackCopyToClipboard(text, button);
+                });
+            } else {
+                this.fallbackCopyToClipboard(text, button);
+            }
+        }
+    },
+
+    // Show copy success feedback
+    showCopySuccess: function(button) {
+        const originalText = button.textContent;
+        button.textContent = 'Copied!';
+        button.classList.add('btn-success');
+        setTimeout(() => {
+            button.textContent = originalText;
+            button.classList.remove('btn-success');
+        }, 2000);
+    },
+
+    // Fallback copy method for older browsers
+    fallbackCopyToClipboard: function(text, button) {
+        const textArea = document.createElement('textarea');
+        textArea.value = text;
+        textArea.style.position = 'fixed';
+        textArea.style.left = '-999999px';
+        textArea.style.top = '-999999px';
+        document.body.appendChild(textArea);
+        textArea.focus();
+        textArea.select();
+
+        try {
+            const successful = document.execCommand('copy');
+            if (successful && button) {
+                this.showCopySuccess(button);
+            }
+        } catch (err) {
+            console.error('Fallback copy failed:', err);
+        }
+
+        document.body.removeChild(textArea);
+    },
+
+    // Format JSON with syntax highlighting
+    formatJSON: function(obj) {
+        const jsonString = JSON.stringify(obj, null, 2);
+        return this.syntaxHighlight(jsonString);
+    },
+
+    // Add syntax highlighting to JSON
+    syntaxHighlight: function(json) {
+        json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
+        return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
+            let cls = 'json-number';
+            if (/^"/.test(match)) {
+                if (/:$/.test(match)) {
+                    cls = 'json-key';
+                } else {
+                    cls = 'json-string';
+                }
+            } else if (/true|false/.test(match)) {
+                cls = 'json-boolean';
+            } else if (/null/.test(match)) {
+                cls = 'json-null';
+            }
+            return '<span class="' + cls + '">' + match + '</span>';
+        });
+    },
+    
+    // Load server information
+    loadServerInfo: function() {
+        this.showLoading('server-info');
+        this.apiCall('/api/server_info', (error, data) => {
+            this.hideLoading('server-info');
+            if (error) {
+                this.showError('server-info', 'Failed to load server information: ' + error.message);
+                return;
+            }
+            this.updateServerInfo(data);
+        });
+    },
+    
+    // Update server information display
+    updateServerInfo: function(data) {
+        const container = document.getElementById('server-info');
+        if (!container) return;
+
+        let componentsHtml = '';
+        data.components.forEach(component => {
+            const statusClass = component.status.toLowerCase() === 'running' ? 'running' : 'error';
+            componentsHtml += `
+                <tr>
+                    <td>${component.name}</td>
+                    <td><span class="status ${statusClass}">${component.status}</span></td>
+                    <td>${component.message}</td>
+                </tr>
+            `;
+        });
+
+        container.innerHTML = `
+            <div class="info-box">
+                <div>
+                    <strong>State:</strong> <span class="status running">${data.state}</span><br>
+                    <strong>Version:</strong> ${data.version}<br>
+                    <strong>Uptime:</strong> ${data.uptime}
+                </div>
+                <div>
+                    <small>Last updated: ${new Date(data.lastUpdated).toLocaleString()}</small>
+                </div>
+            </div>
+            <table class="table">
+                <thead>
+                    <tr>
+                        <th>Component</th>
+                        <th>Status</th>
+                        <th>Message</th>
+                    </tr>
+                </thead>
+                <tbody>
+                    ${componentsHtml}
+                </tbody>
+            </table>
+        `;
+
+        // Update EnvoyGateway configuration if we're on server_info page
+        if (data.envoyGatewayConfig) {
+            this.updateEnvoyGatewayConfig(data.envoyGatewayConfig);
+        }
+    },
+    
+    // Load configuration dump
+    loadConfigDump: function() {
+        this.showLoading('config-dump');
+        this.apiCall('/api/config_dump', (error, data) => {
+            this.hideLoading('config-dump');
+            if (error) {
+                this.showError('config-dump', 'Failed to load configuration dump: ' + error.message);
+                return;
+            }
+            this.updateConfigDump(data);
+        });
+    },
+    
+    // Update configuration dump display
+    updateConfigDump: function(data) {
+        const container = document.getElementById('config-dump');
+        if (container) {
+            container.innerHTML = `
+                <div class="info-box">
+                    <div>
+                        <strong>Gateways:</strong> ${data.gateways ? data.gateways.length : 0}<br>
+                        <strong>HTTP Routes:</strong> ${data.httpRoutes ? data.httpRoutes.length : 0}<br>
+                        <strong>Gateway Classes:</strong> ${data.gatewayClass ? data.gatewayClass.length : 0}
+                    </div>
+                    <div>
+                        <small>Last updated: ${new Date(data.lastUpdated).toLocaleString()}</small>
+                    </div>
+                </div>
+                <div class="code">
+                    ${JSON.stringify(data, null, 2)}
+                </div>
+            `;
+        }
+
+        // Update individual resource lists
+        this.updateGatewaysList(data.gateways || []);
+        this.updateHTTPRoutesList(data.httpRoutes || []);
+        this.updateGRPCRoutesList(data.grpcRoutes || []);
+        this.updateTLSRoutesList(data.tlsRoutes || []);
+        this.updateTCPRoutesList(data.tcpRoutes || []);
+        this.updateUDPRoutesList(data.udpRoutes || []);
+        this.updateGatewayClassesList(data.gatewayClass || []);
+
+        // Update Envoy Gateway Policies
+        this.updateResourcesList('clienttrafficpolicies-list', data.clientTrafficPolicies || [], 'Client Traffic Policies');
+        this.updateResourcesList('backendtrafficpolicies-list', data.backendTrafficPolicies || [], 'Backend Traffic Policies');
+        this.updateResourcesList('backendtlspolicies-list', data.backendTLSPolicies || [], 'Backend TLS Policies');
+        this.updateResourcesList('securitypolicies-list', data.securityPolicies || [], 'Security Policies');
+        this.updateResourcesList('envoypatchpolicies-list', data.envoyPatchPolicies || [], 'Envoy Patch Policies');
+        this.updateResourcesList('envoyextensionpolicies-list', data.envoyExtensionPolicies || [], 'Envoy Extension Policies');
+
+        // Update Kubernetes Resources
+        this.updateResourcesList('services-list', data.services || [], 'Services');
+        this.updateResourcesList('secrets-list', data.secrets || [], 'Secrets');
+        this.updateResourcesList('configmaps-list', data.configMaps || [], 'ConfigMaps');
+        this.updateResourcesList('namespaces-list', data.namespaces || [], 'Namespaces');
+        this.updateResourcesList('endpointslices-list', data.endpointSlices || [], 'Endpoint Slices');
+
+        // Update Other Resources
+        this.updateResourcesList('referencegrants-list', data.referenceGrants || [], 'Reference Grants');
+        this.updateResourcesList('httproutefilters-list', data.httpRouteFilters || [], 'HTTP Route Filters');
+        this.updateResourcesList('envoyproxies-list', data.envoyProxies || [], 'Envoy Proxies');
+        this.updateResourcesList('backends-list', data.backends || [], 'Backends');
+        this.updateResourcesList('serviceimports-list', data.serviceImports || [], 'Service Imports');
+
+        // Update config summary
+        this.updateConfigSummary(data);
+    },
+
+    // Update gateways list
+    updateGatewaysList: function(gateways) {
+        const container = document.getElementById('gateways-list');
+        if (!container) return;
+
+        if (gateways.length === 0) {
+            container.innerHTML = '<p class="text-muted">No gateways found</p>';
+            return;
+        }
+
+        const gatewaysHtml = gateways.map(gw => `
+            <div class="resource-item">
+                <div class="resource-header">
+                    <div class="resource-name">${gw.name}</div>
+                    <div class="resource-namespace">${gw.namespace}</div>
+                </div>
+            </div>
+        `).join('');
+
+        container.innerHTML = gatewaysHtml;
+    },
+
+    // Update HTTP routes list
+    updateHTTPRoutesList: function(httpRoutes) {
+        this.updateRoutesList('httproutes-list', httpRoutes, 'HTTP routes');
+    },
+
+    // Update GRPC routes list
+    updateGRPCRoutesList: function(grpcRoutes) {
+        this.updateRoutesList('grpcroutes-list', grpcRoutes, 'GRPC routes');
+    },
+
+    // Update TLS routes list
+    updateTLSRoutesList: function(tlsRoutes) {
+        this.updateRoutesList('tlsroutes-list', tlsRoutes, 'TLS routes');
+    },
+
+    // Update TCP routes list
+    updateTCPRoutesList: function(tcpRoutes) {
+        this.updateRoutesList('tcproutes-list', tcpRoutes, 'TCP routes');
+    },
+
+    // Update UDP routes list
+    updateUDPRoutesList: function(udpRoutes) {
+        this.updateRoutesList('udproutes-list', udpRoutes, 'UDP routes');
+    },
+
+    // Generic method to update any routes list
+    updateRoutesList: function(containerId, routes, routeType) {
+        const container = document.getElementById(containerId);
+        if (!container) return;
+
+        if (routes.length === 0) {
+            container.innerHTML = `<p class="text-muted">No ${routeType} found</p>`;
+            return;
+        }
+
+        const routesHtml = routes.map(route => `
+            <div class="resource-item">
+                <div class="resource-header">
+                    <div class="resource-name">${route.name}</div>
+                    <div class="resource-namespace">${route.namespace}</div>
+                </div>
+            </div>
+        `).join('');
+
+        container.innerHTML = routesHtml;
+    },
+
+    // Update gateway classes list
+    updateGatewayClassesList: function(gatewayClasses) {
+        const container = document.getElementById('gatewayclasses-list');
+        if (!container) return;
+
+        if (gatewayClasses.length === 0) {
+            container.innerHTML = '<p class="text-muted">No gateway classes found</p>';
+            return;
+        }
+
+        const classesHtml = gatewayClasses.map(gc => `
+            <div class="resource-item">
+                <div class="resource-header">
+                    <div class="resource-name">${gc.name}</div>
+                    <div class="resource-scope">Cluster-scoped</div>
+                </div>
+            </div>
+        `).join('');
+
+        container.innerHTML = classesHtml;
+    },
+
+    // Generic method to update any resource list
+    updateResourcesList: function(containerId, resources, resourceType) {
+        const container = document.getElementById(containerId);
+        if (!container) return;
+
+        if (resources.length === 0) {
+            container.innerHTML = `<p class="text-muted">No ${resourceType.toLowerCase()} found</p>`;
+            return;
+        }
+
+        const resourcesHtml = resources.map(resource => `
+            <div class="resource-item">
+                <div class="resource-header">
+                    <div class="resource-name">${resource.name}</div>
+                    <div class="resource-namespace">${resource.namespace || 'Cluster-scoped'}</div>
+                </div>
+            </div>
+        `).join('');
+
+        container.innerHTML = resourcesHtml;
+    },
+
+    // Update config summary
+    updateConfigSummary: function(data) {
+        const container = document.getElementById('config-summary');
+        if (!container) return;
+
+        const summary = `
+            <div class="config-summary-grid">
+                <!-- Gateway API Core Resources -->
+                <div class="summary-item">
+                    <h3>${data.gateways ? data.gateways.length : 0}</h3>
+                    <p>Gateways</p>
+                </div>
+                <div class="summary-item">
+                    <h3>${data.httpRoutes ? data.httpRoutes.length : 0}</h3>
+                    <p>HTTP Routes</p>
+                </div>
+                <div class="summary-item">
+                    <h3>${data.grpcRoutes ? data.grpcRoutes.length : 0}</h3>
+                    <p>GRPC Routes</p>
+                </div>
+                <div class="summary-item">
+                    <h3>${data.tlsRoutes ? data.tlsRoutes.length : 0}</h3>
+                    <p>TLS Routes</p>
+                </div>
+                <div class="summary-item">
+                    <h3>${data.tcpRoutes ? data.tcpRoutes.length : 0}</h3>
+                    <p>TCP Routes</p>
+                </div>
+                <div class="summary-item">
+                    <h3>${data.udpRoutes ? data.udpRoutes.length : 0}</h3>
+                    <p>UDP Routes</p>
+                </div>
+                <div class="summary-item">
+                    <h3>${data.gatewayClass ? data.gatewayClass.length : 0}</h3>
+                    <p>Gateway Classes</p>
+                </div>
+
+                <!-- Envoy Gateway Policies -->
+                <div class="summary-item">
+                    <h3>${data.clientTrafficPolicies ? data.clientTrafficPolicies.length : 0}</h3>
+                    <p>Client Traffic Policies</p>
+                </div>
+                <div class="summary-item">
+                    <h3>${data.backendTrafficPolicies ? data.backendTrafficPolicies.length : 0}</h3>
+                    <p>Backend Traffic Policies</p>
+                </div>
+                <div class="summary-item">
+                    <h3>${data.backendTLSPolicies ? data.backendTLSPolicies.length : 0}</h3>
+                    <p>Backend TLS Policies</p>
+                </div>
+                <div class="summary-item">
+                    <h3>${data.securityPolicies ? data.securityPolicies.length : 0}</h3>
+                    <p>Security Policies</p>
+                </div>
+                <div class="summary-item">
+                    <h3>${data.envoyPatchPolicies ? data.envoyPatchPolicies.length : 0}</h3>
+                    <p>Envoy Patch Policies</p>
+                </div>
+                <div class="summary-item">
+                    <h3>${data.envoyExtensionPolicies ? data.envoyExtensionPolicies.length : 0}</h3>
+                    <p>Envoy Extension Policies</p>
+                </div>
+
+                <!-- Kubernetes Resources -->
+                <div class="summary-item">
+                    <h3>${data.services ? data.services.length : 0}</h3>
+                    <p>Services</p>
+                </div>
+                <div class="summary-item">
+                    <h3>${data.secrets ? data.secrets.length : 0}</h3>
+                    <p>Secrets</p>
+                </div>
+                <div class="summary-item">
+                    <h3>${data.configMaps ? data.configMaps.length : 0}</h3>
+                    <p>ConfigMaps</p>
+                </div>
+                <div class="summary-item">
+                    <h3>${data.namespaces ? data.namespaces.length : 0}</h3>
+                    <p>Namespaces</p>
+                </div>
+                <div class="summary-item">
+                    <h3>${data.endpointSlices ? data.endpointSlices.length : 0}</h3>
+                    <p>Endpoint Slices</p>
+                </div>
+
+                <!-- Other Resources -->
+                <div class="summary-item">
+                    <h3>${data.referenceGrants ? data.referenceGrants.length : 0}</h3>
+                    <p>Reference Grants</p>
+                </div>
+                <div class="summary-item">
+                    <h3>${data.httpRouteFilters ? data.httpRouteFilters.length : 0}</h3>
+                    <p>HTTP Route Filters</p>
+                </div>
+                <div class="summary-item">
+                    <h3>${data.envoyProxies ? data.envoyProxies.length : 0}</h3>
+                    <p>Envoy Proxies</p>
+                </div>
+                <div class="summary-item">
+                    <h3>${data.backends ? data.backends.length : 0}</h3>
+                    <p>Backends</p>
+                </div>
+                <div class="summary-item">
+                    <h3>${data.serviceImports ? data.serviceImports.length : 0}</h3>
+                    <p>Service Imports</p>
+                </div>
+            </div>
+        `;
+
+        container.innerHTML = summary;
+    },
+    
+    // Show loading indicator
+    showLoading: function(containerId) {
+        const container = document.getElementById(containerId);
+        if (container) {
+            container.innerHTML = '<div class="loading"></div> Loading...';
+        }
+    },
+    
+    // Hide loading indicator
+    hideLoading: function(containerId) {
+        // Loading will be replaced by content
+    },
+    
+    // Show error message
+    showError: function(containerId, message) {
+        const container = document.getElementById(containerId);
+        if (container) {
+            container.innerHTML = `<div class="info-box error">${message}</div>`;
+        }
+    },
+    
+    // Start auto-refresh
+    startAutoRefresh: function() {
+        if (this.refreshTimer) {
+            clearInterval(this.refreshTimer);
+        }
+        this.refreshTimer = setInterval(() => {
+            this.loadPageData();
+        }, this.config.refreshInterval);
+    },
+    
+    // Stop auto-refresh
+    stopAutoRefresh: function() {
+        if (this.refreshTimer) {
+            clearInterval(this.refreshTimer);
+            this.refreshTimer = null;
+        }
+    },
+    
+    // Manual refresh
+    refresh: function() {
+        this.loadPageData();
+    }
+};
+
+// Initialize when DOM is loaded
+document.addEventListener('DOMContentLoaded', function() {
+    window.EnvoyGatewayAdmin.init();
+});
+
+// Cleanup on page unload
+window.addEventListener('beforeunload', function() {
+    window.EnvoyGatewayAdmin.stopAutoRefresh();
+});
diff --git a/internal/admin/console/templates/base.html b/internal/admin/console/templates/base.html
new file mode 100644
index 000000000..ce3487ced
--- /dev/null
+++ b/internal/admin/console/templates/base.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>{{.Title}} - Envoy Gateway Admin</title>
+    <link rel="stylesheet" href="/static/css/admin.css">
+    <link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🚀</text></svg>">
+</head>
+<body>
+    <header class="header">
+        <div class="header-content">
+            <div class="logo">
+                🚀 Envoy Gateway Admin
+            </div>
+            <nav class="nav">
+                <a href="/">Dashboard</a>
+                <a href="/server_info">Server Info</a>
+                <a href="/config_dump">Config Dump</a>
+                <a href="/stats">Stats</a>
+                <a href="/pprof">Profiling</a>
+            </nav>
+        </div>
+    </header>
+
+    <main class="container">
+        {{template "content" .}}
+    </main>
+
+    <footer class="footer">
+        <p>&copy; 2025 Envoy Gateway. Built with ❤️ by the Envoy Gateway community.</p>
+        <p>
+            <a href="https://gateway.envoyproxy.io/" target="_blank">Documentation</a> |
+            <a href="https://github.com/envoyproxy/gateway" target="_blank">GitHub</a>
+        </p>
+    </footer>
+
+    <script src="/static/js/admin.js"></script>
+</body>
+</html>
diff --git a/internal/admin/console/templates/config_dump.html b/internal/admin/console/templates/config_dump.html
new file mode 100644
index 000000000..1895b012d
--- /dev/null
+++ b/internal/admin/console/templates/config_dump.html
@@ -0,0 +1,323 @@
+{{template "base.html" .}}
+
+{{define "config-dump-content"}}
+<div class="card">
+    <div class="card-header">
+        <h1 class="card-title">{{.Title}}</h1>
+        <div style="display: flex; align-items: center; gap: 1rem;">
+            <a href="/api/config_dump_all" class="btn btn-primary" target="_blank">
+                🔍 View Complete Config (JSON)
+            </a>
+            <label>
+                <input type="checkbox" id="auto-refresh"> Auto-refresh (30s)
+            </label>
+            <button class="btn btn-secondary" onclick="EnvoyGatewayAdmin.refresh()">
+                Refresh
+            </button>
+        </div>
+    </div>
+    <div class="card-body">
+        <p>This page displays the current configuration state of Envoy Gateway, including all Gateway API resources and their status.</p>
+    </div>
+</div>
+
+<div class="card">
+    <div class="card-header">
+        <h2 class="card-title">📊 Configuration Summary</h2>
+    </div>
+    <div class="card-body">
+        <p>This section provides a high-level overview of the current configuration state:</p>
+        <div id="config-summary">
+            <div class="loading"></div> Loading configuration summary...
+        </div>
+    </div>
+</div>
+
+<div class="config-resources-grid">
+    <div class="card">
+        <div class="card-header">
+            <h2 class="card-title">🚪 Gateways</h2>
+        </div>
+        <div class="card-body">
+            <p>Gateway resources define the entry points for traffic into the cluster. They specify which ports and protocols are exposed.</p>
+            <div id="gateways-list">
+                <div class="loading"></div> Loading gateways...
+            </div>
+        </div>
+    </div>
+
+    <div class="card">
+        <div class="card-header">
+            <h2 class="card-title">🛣️ HTTP Routes</h2>
+        </div>
+        <div class="card-body">
+            <p>HTTPRoute resources define how HTTP requests are routed to backend services.</p>
+            <div id="httproutes-list">
+                <div class="loading"></div> Loading HTTP routes...
+            </div>
+        </div>
+    </div>
+
+    <div class="card">
+        <div class="card-header">
+            <h2 class="card-title">🔧 GRPC Routes</h2>
+        </div>
+        <div class="card-body">
+            <p>GRPCRoute resources define how gRPC requests are routed to backend services.</p>
+            <div id="grpcroutes-list">
+                <div class="loading"></div> Loading GRPC routes...
+            </div>
+        </div>
+    </div>
+
+    <div class="card">
+        <div class="card-header">
+            <h2 class="card-title">🔒 TLS Routes</h2>
+        </div>
+        <div class="card-body">
+            <p>TLSRoute resources define how TLS connections are routed to backend services.</p>
+            <div id="tlsroutes-list">
+                <div class="loading"></div> Loading TLS routes...
+            </div>
+        </div>
+    </div>
+
+    <div class="card">
+        <div class="card-header">
+            <h2 class="card-title">🔌 TCP Routes</h2>
+        </div>
+        <div class="card-body">
+            <p>TCPRoute resources define how TCP connections are routed to backend services.</p>
+            <div id="tcproutes-list">
+                <div class="loading"></div> Loading TCP routes...
+            </div>
+        </div>
+    </div>
+
+    <div class="card">
+        <div class="card-header">
+            <h2 class="card-title">📡 UDP Routes</h2>
+        </div>
+        <div class="card-body">
+            <p>UDPRoute resources define how UDP traffic is routed to backend services.</p>
+            <div id="udproutes-list">
+                <div class="loading"></div> Loading UDP routes...
+            </div>
+        </div>
+    </div>
+
+    <div class="card">
+        <div class="card-header">
+            <h2 class="card-title">🏷️ Gateway Classes</h2>
+        </div>
+        <div class="card-body">
+            <p>GatewayClass resources define the type of Gateway that can be deployed.</p>
+            <div id="gatewayclasses-list">
+                <div class="loading"></div> Loading gateway classes...
+            </div>
+        </div>
+    </div>
+
+    <!-- Envoy Gateway Policies -->
+    <div class="card">
+        <div class="card-header">
+            <h2 class="card-title">🚦 Client Traffic Policies</h2>
+        </div>
+        <div class="card-body">
+            <p>ClientTrafficPolicy resources define traffic management policies for client connections.</p>
+            <div id="clienttrafficpolicies-list">
+                <div class="loading"></div> Loading client traffic policies...
+            </div>
+        </div>
+    </div>
+
+    <div class="card">
+        <div class="card-header">
+            <h2 class="card-title">🔄 Backend Traffic Policies</h2>
+        </div>
+        <div class="card-body">
+            <p>BackendTrafficPolicy resources define traffic management policies for backend connections.</p>
+            <div id="backendtrafficpolicies-list">
+                <div class="loading"></div> Loading backend traffic policies...
+            </div>
+        </div>
+    </div>
+
+    <div class="card">
+        <div class="card-header">
+            <h2 class="card-title">🔐 Backend TLS Policies</h2>
+        </div>
+        <div class="card-body">
+            <p>BackendTLSPolicy resources define TLS configuration for backend connections.</p>
+            <div id="backendtlspolicies-list">
+                <div class="loading"></div> Loading backend TLS policies...
+            </div>
+        </div>
+    </div>
+
+    <div class="card">
+        <div class="card-header">
+            <h2 class="card-title">🛡️ Security Policies</h2>
+        </div>
+        <div class="card-body">
+            <p>SecurityPolicy resources define security configurations and authentication policies.</p>
+            <div id="securitypolicies-list">
+                <div class="loading"></div> Loading security policies...
+            </div>
+        </div>
+    </div>
+
+    <div class="card">
+        <div class="card-header">
+            <h2 class="card-title">🔧 Envoy Patch Policies</h2>
+        </div>
+        <div class="card-body">
+            <p>EnvoyPatchPolicy resources define custom patches to Envoy configuration.</p>
+            <div id="envoypatchpolicies-list">
+                <div class="loading"></div> Loading Envoy patch policies...
+            </div>
+        </div>
+    </div>
+
+    <div class="card">
+        <div class="card-header">
+            <h2 class="card-title">🔌 Envoy Extension Policies</h2>
+        </div>
+        <div class="card-body">
+            <p>EnvoyExtensionPolicy resources define external processing and extension configurations.</p>
+            <div id="envoyextensionpolicies-list">
+                <div class="loading"></div> Loading Envoy extension policies...
+            </div>
+        </div>
+    </div>
+
+    <!-- Kubernetes Resources -->
+    <div class="card">
+        <div class="card-header">
+            <h2 class="card-title">🌐 Services</h2>
+        </div>
+        <div class="card-body">
+            <p>Service resources define network services and their endpoints.</p>
+            <div id="services-list">
+                <div class="loading"></div> Loading services...
+            </div>
+        </div>
+    </div>
+
+    <div class="card">
+        <div class="card-header">
+            <h2 class="card-title">🔑 Secrets</h2>
+        </div>
+        <div class="card-body">
+            <p>Secret resources contain sensitive data such as certificates and keys.</p>
+            <div id="secrets-list">
+                <div class="loading"></div> Loading secrets...
+            </div>
+        </div>
+    </div>
+
+    <div class="card">
+        <div class="card-header">
+            <h2 class="card-title">📋 ConfigMaps</h2>
+        </div>
+        <div class="card-body">
+            <p>ConfigMap resources contain configuration data for applications.</p>
+            <div id="configmaps-list">
+                <div class="loading"></div> Loading config maps...
+            </div>
+        </div>
+    </div>
+
+    <div class="card">
+        <div class="card-header">
+            <h2 class="card-title">📁 Namespaces</h2>
+        </div>
+        <div class="card-body">
+            <p>Namespace resources provide scope for names and resource isolation.</p>
+            <div id="namespaces-list">
+                <div class="loading"></div> Loading namespaces...
+            </div>
+        </div>
+    </div>
+
+    <div class="card">
+        <div class="card-header">
+            <h2 class="card-title">🎯 Endpoint Slices</h2>
+        </div>
+        <div class="card-body">
+            <p>EndpointSlice resources define network endpoints for services.</p>
+            <div id="endpointslices-list">
+                <div class="loading"></div> Loading endpoint slices...
+            </div>
+        </div>
+    </div>
+
+    <!-- Other Resources -->
+    <div class="card">
+        <div class="card-header">
+            <h2 class="card-title">🔗 Reference Grants</h2>
+        </div>
+        <div class="card-body">
+            <p>ReferenceGrant resources allow cross-namespace references in Gateway API.</p>
+            <div id="referencegrants-list">
+                <div class="loading"></div> Loading reference grants...
+            </div>
+        </div>
+    </div>
+
+    <div class="card">
+        <div class="card-header">
+            <h2 class="card-title">🔍 HTTP Route Filters</h2>
+        </div>
+        <div class="card-body">
+            <p>HTTPRouteFilter resources define custom filters for HTTP routes.</p>
+            <div id="httproutefilters-list">
+                <div class="loading"></div> Loading HTTP route filters...
+            </div>
+        </div>
+    </div>
+
+    <div class="card">
+        <div class="card-header">
+            <h2 class="card-title">⚙️ Envoy Proxies</h2>
+        </div>
+        <div class="card-body">
+            <p>EnvoyProxy resources define Envoy proxy configuration and deployment settings.</p>
+            <div id="envoyproxies-list">
+                <div class="loading"></div> Loading Envoy proxies...
+            </div>
+        </div>
+    </div>
+
+    <div class="card">
+        <div class="card-header">
+            <h2 class="card-title">🔙 Backends</h2>
+        </div>
+        <div class="card-body">
+            <p>Backend resources define backend service configurations.</p>
+            <div id="backends-list">
+                <div class="loading"></div> Loading backends...
+            </div>
+        </div>
+    </div>
+
+    <div class="card">
+        <div class="card-header">
+            <h2 class="card-title">📤 Service Imports</h2>
+        </div>
+        <div class="card-body">
+            <p>ServiceImport resources define imported services from other clusters.</p>
+            <div id="serviceimports-list">
+                <div class="loading"></div> Loading service imports...
+            </div>
+        </div>
+    </div>
+
+</div>
+
+
+
+<script>
+
+</script>
+{{end}}
diff --git a/internal/admin/console/templates/index.html b/internal/admin/console/templates/index.html
new file mode 100644
index 000000000..24ed4ff48
--- /dev/null
+++ b/internal/admin/console/templates/index.html
@@ -0,0 +1,110 @@
+{{template "base.html" .}}
+
+{{define "index-content"}}
+<div class="card">
+    <div class="card-header">
+        <h1 class="card-title">{{.Title}}</h1>
+        <div style="display: flex; align-items: center; gap: 1rem;">
+            <label>
+                <input type="checkbox" id="auto-refresh"> Auto-refresh (30s)
+            </label>
+            <button class="btn btn-secondary" onclick="EnvoyGatewayAdmin.refresh()">
+                Refresh
+            </button>
+        </div>
+    </div>
+    <div class="card-body">
+        <p>Welcome to the Envoy Gateway Admin Console. This interface provides access to various administrative functions and monitoring capabilities.</p>
+        
+        <div id="system-info">
+            <div class="loading"></div> Loading system information...
+        </div>
+    </div>
+</div>
+
+<div class="dashboard-grid">
+    <div class="card">
+        <div class="card-header">
+            <h2 class="card-title">🖥️ Server Information</h2>
+        </div>
+        <div class="card-body">
+            <p>View detailed information about the Envoy Gateway server status, components, and runtime information.</p>
+            <a href="/server_info" class="btn">View Server Info</a>
+        </div>
+    </div>
+
+    <div class="card">
+        <div class="card-header">
+            <h2 class="card-title">⚙️ Configuration Dump</h2>
+        </div>
+        <div class="card-body">
+            <p>Access the current configuration state including Gateways, HTTPRoutes, and other resources.</p>
+            <a href="/config_dump" class="btn">View Config Dump</a>
+        </div>
+    </div>
+
+    <div class="card">
+        <div class="card-header">
+            <h2 class="card-title">📊 Statistics</h2>
+        </div>
+        <div class="card-body">
+            <p>Monitor performance metrics and statistics from the Envoy Gateway control plane.</p>
+            <a href="/stats" class="btn">View Statistics</a>
+        </div>
+    </div>
+
+    <div class="card">
+        <div class="card-header">
+            <h2 class="card-title">🔍 Performance Profiling</h2>
+        </div>
+        <div class="card-body">
+            <p>Access pprof endpoints for performance analysis and debugging.</p>
+            <a href="/pprof" class="btn">View Profiling</a>
+        </div>
+    </div>
+</div>
+
+<div class="card">
+    <div class="card-header">
+        <h2 class="card-title">📚 Quick Links</h2>
+    </div>
+    <div class="card-body">
+        <div class="quick-links-grid">
+            <div class="quick-links-section">
+                <h3>📖 Documentation</h3>
+                <div class="quick-links-list">
+                    <a href="https://gateway.envoyproxy.io/" target="_blank" class="quick-link">
+                        <span class="quick-link-icon">📚</span>
+                        <span class="quick-link-text">Envoy Gateway Docs</span>
+                    </a>
+                    <a href="https://gateway.envoyproxy.io/latest/api/" target="_blank" class="quick-link">
+                        <span class="quick-link-icon">🔧</span>
+                        <span class="quick-link-text">API Reference</span>
+                    </a>
+                    <a href="https://gateway.envoyproxy.io/latest/tasks/" target="_blank" class="quick-link">
+                        <span class="quick-link-icon">📝</span>
+                        <span class="quick-link-text">Tasks & Tutorials</span>
+                    </a>
+                </div>
+            </div>
+            <div class="quick-links-section">
+                <h3>👥 Community</h3>
+                <div class="quick-links-list">
+                    <a href="https://github.com/envoyproxy/gateway" target="_blank" class="quick-link">
+                        <span class="quick-link-icon">🐙</span>
+                        <span class="quick-link-text">GitHub Repository</span>
+                    </a>
+                    <a href="https://github.com/envoyproxy/gateway/issues" target="_blank" class="quick-link">
+                        <span class="quick-link-icon">🐛</span>
+                        <span class="quick-link-text">Issue Tracker</span>
+                    </a>
+                    <a href="https://envoyproxy.slack.com/" target="_blank" class="quick-link">
+                        <span class="quick-link-icon">💬</span>
+                        <span class="quick-link-text">Slack Community</span>
+                    </a>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+{{end}}
diff --git a/internal/admin/console/templates/pprof.html b/internal/admin/console/templates/pprof.html
new file mode 100644
index 000000000..66c919066
--- /dev/null
+++ b/internal/admin/console/templates/pprof.html
@@ -0,0 +1,118 @@
+{{template "base.html" .}}
+
+{{define "pprof-content"}}
+<div class="card">
+    <div class="card-header">
+        <h1 class="card-title">{{.Title}}</h1>
+    </div>
+    <div class="card-body">
+        {{if .EnablePprof}}
+        <p>Performance profiling endpoints are available for debugging and performance analysis.</p>
+        
+        <div class="info-box">
+            <div>
+                <strong>Status:</strong> <span class="status running">Enabled</span>
+            </div>
+        </div>
+
+        <div class="dashboard-grid">
+            <div class="card">
+                <div class="card-header">
+                    <h2 class="card-title">🔍 CPU Profile</h2>
+                </div>
+                <div class="card-body">
+                    <p>CPU profiling shows where the program spends its time while actively consuming CPU cycles.</p>
+                    <a href="/debug/pprof/profile" class="btn" target="_blank">Download CPU Profile</a>
+                </div>
+            </div>
+
+            <div class="card">
+                <div class="card-header">
+                    <h2 class="card-title">🧠 Memory Heap</h2>
+                </div>
+                <div class="card-body">
+                    <p>Heap profiling reports memory allocation samples for monitoring current and historical memory usage.</p>
+                    <a href="/debug/pprof/heap" class="btn" target="_blank">Download Heap Profile</a>
+                </div>
+            </div>
+
+            <div class="card">
+                <div class="card-header">
+                    <h2 class="card-title">🔄 Goroutines</h2>
+                </div>
+                <div class="card-body">
+                    <p>Stack traces of all current goroutines.</p>
+                    <a href="/debug/pprof/goroutine" class="btn" target="_blank">View Goroutines</a>
+                </div>
+            </div>
+
+            <div class="card">
+                <div class="card-header">
+                    <h2 class="card-title">🔒 Mutex</h2>
+                </div>
+                <div class="card-body">
+                    <p>Stack traces of holders of contended mutexes.</p>
+                    <a href="/debug/pprof/mutex" class="btn" target="_blank">View Mutex Profile</a>
+                </div>
+            </div>
+
+            <div class="card">
+                <div class="card-header">
+                    <h2 class="card-title">🚫 Block</h2>
+                </div>
+                <div class="card-body">
+                    <p>Stack traces that led to blocking on synchronization primitives.</p>
+                    <a href="/debug/pprof/block" class="btn" target="_blank">View Block Profile</a>
+                </div>
+            </div>
+
+            <div class="card">
+                <div class="card-header">
+                    <h2 class="card-title">📊 All Profiles</h2>
+                </div>
+                <div class="card-body">
+                    <p>Index page with links to all available profiles.</p>
+                    <a href="/debug/pprof/" class="btn" target="_blank">View All Profiles</a>
+                </div>
+            </div>
+        </div>
+
+        <div class="card">
+            <div class="card-header">
+                <h2 class="card-title">💡 Usage Tips</h2>
+            </div>
+            <div class="card-body">
+                <ul>
+                    <li><strong>CPU Profile:</strong> Use <code>go tool pprof &lt;profile-url&gt;</code> to analyze CPU usage</li>
+                    <li><strong>Memory Heap:</strong> Identify memory leaks and high memory usage patterns</li>
+                    <li><strong>Goroutines:</strong> Debug goroutine leaks and concurrency issues</li>
+                    <li><strong>Mutex/Block:</strong> Find contention points in your application</li>
+                </ul>
+                
+                <div class="code">
+# Example: Analyze CPU profile
+go tool pprof http://localhost:19000/debug/pprof/profile
+
+# Example: Analyze heap profile  
+go tool pprof http://localhost:19000/debug/pprof/heap
+
+# Example: View goroutines in browser
+go tool pprof -http=:8080 http://localhost:19000/debug/pprof/goroutine
+                </div>
+            </div>
+        </div>
+        {{else}}
+        <div class="info-box warning">
+            <div>
+                <strong>Status:</strong> <span class="status warning">Disabled</span>
+            </div>
+            <div>
+                pprof endpoints are currently disabled. Enable them in the Envoy Gateway configuration.
+            </div>
+        </div>
+
+
+        {{end}}
+    </div>
+</div>
+{{end}}
diff --git a/internal/admin/console/templates/server_info.html b/internal/admin/console/templates/server_info.html
new file mode 100644
index 000000000..fbaccd9ad
--- /dev/null
+++ b/internal/admin/console/templates/server_info.html
@@ -0,0 +1,43 @@
+{{template "base.html" .}}
+
+{{define "server-info-content"}}
+<div class="card">
+    <div class="card-header">
+        <h1 class="card-title">{{.Title}}</h1>
+        <div style="display: flex; align-items: center; gap: 1rem;">
+            <label>
+                <input type="checkbox" id="auto-refresh"> Auto-refresh (30s)
+            </label>
+            <button class="btn btn-secondary" onclick="EnvoyGatewayAdmin.refresh()">
+                Refresh
+            </button>
+        </div>
+    </div>
+    <div class="card-body">
+        <p>This page displays detailed information about the Envoy Gateway server status and components.</p>
+
+        <div id="server-info">
+            <div class="loading"></div> Loading server information...
+        </div>
+    </div>
+</div>
+
+
+
+
+
+
+<div class="card">
+    <div class="card-header">
+        <h2 class="card-title">⚙️ EnvoyGateway Configuration</h2>
+    </div>
+    <div class="card-body">
+        <p>Current EnvoyGateway configuration loaded from the config file or defaults.</p>
+
+        <div id="envoy-gateway-config">
+            <div class="loading"></div> Loading EnvoyGateway configuration...
+        </div>
+    </div>
+</div>
+
+{{end}}
diff --git a/internal/admin/console/templates/stats.html b/internal/admin/console/templates/stats.html
new file mode 100644
index 000000000..42e1ea336
--- /dev/null
+++ b/internal/admin/console/templates/stats.html
@@ -0,0 +1,102 @@
+{{template "base.html" .}}
+
+{{define "stats-content"}}
+<div class="card">
+    <div class="card-header">
+        <h1 class="card-title">{{.Title}}</h1>
+    </div>
+    <div class="card-body">
+        <p>This page provides access to Envoy Gateway statistics and metrics. Metrics are exposed in Prometheus format for monitoring and alerting.</p>
+
+        <div class="info-box">
+            <div>
+                <strong>Metrics Endpoint:</strong><br>
+                <a href="/api/metrics" target="_blank">/api/metrics</a> (Available on Admin Port)
+            </div>
+            <div>
+                <a href="/api/metrics" class="btn btn-primary" target="_blank">
+                    📊 View Metrics
+                </a>
+            </div>
+        </div>
+    </div>
+</div>
+
+<div class="card">
+    <div class="card-header">
+        <h2 class="card-title">📈 Metrics Categories</h2>
+    </div>
+    <div class="card-body">
+        <p>Envoy Gateway exports metrics in six main categories to monitor different aspects of the control plane:</p>
+    </div>
+</div>
+
+<div class="dashboard-grid">
+    <div class="card">
+        <div class="card-header">
+            <h3 class="card-title">🔄 Watching Components</h3>
+        </div>
+        <div class="card-body">
+            <p><strong>Core Function:</strong> Monitor the internal event-driven architecture of Envoy Gateway components.</p>
+
+            <p><em>Components: Gateway API Translator, Infrastructure Manager, xDS Server, xDS Translator, Global RateLimit</em></p>
+        </div>
+    </div>
+
+    <div class="card">
+        <div class="card-header">
+            <h3 class="card-title">📝 Status Updater</h3>
+        </div>
+        <div class="card-body">
+            <p><strong>Core Function:</strong> Track status updates for Gateway API resources to ensure proper resource state management.</p>
+
+            <p><em>Resources: GatewayClass, Gateway, HTTPRoute, GRPCRoute, TLSRoute, etc.</em></p>
+        </div>
+    </div>
+
+    <div class="card">
+        <div class="card-header">
+            <h3 class="card-title">🌐 xDS Server</h3>
+        </div>
+        <div class="card-body">
+            <p><strong>Core Function:</strong> Monitor the xDS (Discovery Service) server that provides configuration to Envoy proxies.</p>
+
+            <p><em>Critical for proxy configuration delivery and connection health</em></p>
+        </div>
+    </div>
+
+    <div class="card">
+        <div class="card-header">
+            <h3 class="card-title">🏗️ Infrastructure Manager</h3>
+        </div>
+        <div class="card-body">
+            <p><strong>Core Function:</strong> Monitor Kubernetes resource operations performed by Envoy Gateway.</p>
+
+            <p><em>Tracks Deployments, Services, ConfigMaps, and other Kubernetes resources</em></p>
+        </div>
+    </div>
+
+    <div class="card">
+        <div class="card-header">
+            <h3 class="card-title">🔧 Wasm</h3>
+        </div>
+        <div class="card-body">
+            <p><strong>Core Function:</strong> Monitor WebAssembly extension management and remote fetch operations.</p>
+
+            <p><em>Essential for custom extension performance and availability</em></p>
+        </div>
+    </div>
+
+    <div class="card">
+        <div class="card-header">
+            <h3 class="card-title">🗺️ Topology Injector</h3>
+        </div>
+        <div class="card-body">
+            <p><strong>Core Function:</strong> Monitor the webhook that injects node topology information into EnvoyProxy pods.</p>
+
+            <p><em>Critical for multi-zone deployments and traffic locality optimization</em></p>
+        </div>
+    </div>
+</div>
+
+{{end}}
diff --git a/internal/admin/server.go b/internal/admin/server.go
index 3bb301acc..9da741596 100644
--- a/internal/admin/server.go
+++ b/internal/admin/server.go
@@ -13,17 +13,26 @@ import (
 
 	"github.com/davecgh/go-spew/spew"
 
+	"github.com/envoyproxy/gateway/internal/admin/console"
 	"github.com/envoyproxy/gateway/internal/envoygateway/config"
+	"github.com/envoyproxy/gateway/internal/message"
 )
 
+type Config struct {
+	Server            config.Server
+	ProviderResources *message.ProviderResources
+}
+
 type Runner struct {
-	cfg    *config.Server
-	server *http.Server
+	cfg               *config.Server
+	server            *http.Server
+	providerResources *message.ProviderResources
 }
 
-func New(cfg *config.Server) *Runner {
+func New(cfg *Config) *Runner {
 	return &Runner{
-		cfg: cfg,
+		cfg:               &cfg.Server,
+		providerResources: cfg.ProviderResources,
 	}
 }
 
@@ -53,10 +62,15 @@ func (r *Runner) Close() error {
 func (r *Runner) start() error {
 	handlers := http.NewServeMux()
 	address := r.cfg.EnvoyGateway.GetEnvoyGatewayAdminAddress()
-	enablePprof := r.cfg.EnvoyGateway.GetEnvoyGatewayAdmin().EnablePprof
+	adminConfig := r.cfg.EnvoyGateway.GetEnvoyGatewayAdmin()
+	enablePprof := adminConfig.EnablePprof
 
 	adminLogger := r.cfg.Logger.WithName("admin")
-	adminLogger.Info("starting admin server", "address", address, "enablePprof", enablePprof)
+	adminLogger.Info("starting admin server", "address", address, "enablePprof", enablePprof, "enableConsole", true)
+
+	// Register console handlers (always enabled)
+	consoleHandler := console.NewHandler(r.cfg, r.providerResources)
+	consoleHandler.RegisterRoutes(handlers)
 
 	if enablePprof {
 		// Serve pprof endpoints to aid in live debugging.
diff --git a/internal/cmd/egctl/dashboard.go b/internal/cmd/egctl/dashboard.go
index 349662dbe..67994c449 100644
--- a/internal/cmd/egctl/dashboard.go
+++ b/internal/cmd/egctl/dashboard.go
@@ -18,6 +18,7 @@ func newDashboardCommand() *cobra.Command {
 	}
 
 	c.AddCommand(newEnvoyDashboardCmd())
+	c.AddCommand(newEnvoyGatewayDashboardCmd())
 
 	return c
 }
diff --git a/internal/cmd/egctl/dashboard_envoy_gateway.go b/internal/cmd/egctl/dashboard_envoy_gateway.go
new file mode 100644
index 000000000..be6bb6ece
--- /dev/null
+++ b/internal/cmd/egctl/dashboard_envoy_gateway.go
@@ -0,0 +1,105 @@
+// Copyright Envoy Gateway Authors
+// SPDX-License-Identifier: Apache-2.0
+// The full text of the Apache license is available in the LICENSE file at
+// the root of the repo.
+
+package egctl
+
+import (
+	"fmt"
+	"io"
+
+	"github.com/spf13/cobra"
+	"k8s.io/apimachinery/pkg/types"
+
+	kube "github.com/envoyproxy/gateway/internal/kubernetes"
+)
+
+const (
+	// envoyGatewayAdminPort is the port for the Envoy Gateway admin server
+	envoyGatewayAdminPort = 19000
+)
+
+func newEnvoyGatewayDashboardCmd() *cobra.Command {
+	var namespace string
+	var listenPort int
+
+	dashboardCmd := &cobra.Command{
+		Use:   "envoy-gateway",
+		Short: "Open Envoy Gateway admin console in your browser",
+		Long:  `Open Envoy Gateway admin console in your browser.`,
+		Example: `  # Open Envoy Gateway admin console in your browser.
+  egctl x dashboard envoy-gateway
+
+  # Open Envoy Gateway admin console with a specific namespace
+  egctl x dashboard envoy-gateway -n custom-namespace
+
+  # Open Envoy Gateway admin console with a specific port
+  egctl x dashboard envoy-gateway --port 8080
+`,
+		Aliases: []string{"eg"},
+		RunE: func(c *cobra.Command, args []string) error {
+			if listenPort > 65535 || listenPort < 0 {
+				return fmt.Errorf("invalid port number range")
+			}
+
+			kubeClient, err := getCLIClient()
+			if err != nil {
+				return err
+			}
+
+			// Find the Envoy Gateway deployment pod
+			labelSelectors := []string{"control-plane=envoy-gateway"}
+			pl, err := kubeClient.PodsForSelector(namespace, labelSelectors...)
+			if err != nil {
+				return fmt.Errorf("not able to locate Envoy Gateway pod: %w", err)
+			}
+			if len(pl.Items) < 1 {
+				return fmt.Errorf("no Envoy Gateway pods found in namespace %s", namespace)
+			}
+
+			podName := pl.Items[0].Name
+			fmt.Fprintf(c.OutOrStdout(), "Found Envoy Gateway pod: %s\n", podName)
+
+			return forwardToEnvoyGatewayAdmin(podName, namespace, listenPort, kubeClient, c.OutOrStdout())
+		},
+	}
+
+	dashboardCmd.PersistentFlags().StringVarP(&namespace, "namespace", "n", "envoy-gateway-system", "Namespace where Envoy Gateway is installed.")
+	dashboardCmd.PersistentFlags().IntVarP(&listenPort, "port", "p", 0, "Local port to listen to.")
+
+	return dashboardCmd
+}
+
+// forwardToEnvoyGatewayAdmin forwards a local port to the Envoy Gateway admin port
+func forwardToEnvoyGatewayAdmin(podName, namespace string, listenPort int, client kube.CLIClient, writer io.Writer) error {
+	meta := types.NamespacedName{
+		Namespace: namespace,
+		Name:      podName,
+	}
+
+	fmt.Fprintf(writer, "Setting up port-forward to Envoy Gateway admin server...\n")
+
+	fw, err := kube.NewLocalPortForwarder(client, meta, listenPort, envoyGatewayAdminPort)
+	if err != nil {
+		return fmt.Errorf("could not build port forwarder for Envoy Gateway: %w", err)
+	}
+
+	if err = fw.Start(); err != nil {
+		fw.Stop()
+		return fmt.Errorf("could not start port forwarder for Envoy Gateway: %w", err)
+	}
+
+	ClosePortForwarderOnInterrupt(fw)
+
+	url := fmt.Sprintf("http://%s", fw.Address())
+	fmt.Fprintf(writer, "Envoy Gateway admin console URL: %s\n", url)
+	fmt.Fprintf(writer, "Opening browser to access the Envoy Gateway admin console...\n")
+	fmt.Fprintf(writer, "Press Ctrl+C to quit\n")
+
+	openBrowser(url, writer)
+
+	fw.WaitForStop()
+
+	return nil
+}
diff --git a/internal/cmd/server.go b/internal/cmd/server.go
index 618b221ff..6ba9f77b9 100644
--- a/internal/cmd/server.go
+++ b/internal/cmd/server.go
@@ -215,7 +215,10 @@ func startRunners(ctx context.Context, cfg *config.Server) (err error) {
 		{
 			// Start the Admin Server
 			// It provides admin endpoints including pprof for debugging.
-			runner: admin.New(cfg),
+			runner: admin.New(&admin.Config{
+				Server:            *cfg,
+				ProviderResources: channels.pResources,
+			}),
 		},
 		{
 			// Start the Metrics Server
diff --git a/internal/gatewayapi/clustersettings.go b/internal/gatewayapi/clustersettings.go
index 1b2b806d9..8fe0d4178 100644
--- a/internal/gatewayapi/clustersettings.go
+++ b/internal/gatewayapi/clustersettings.go
@@ -400,6 +400,7 @@ func buildActiveHealthCheck(policy egv1a1.HealthCheck) *ir.ActiveHealthCheck {
 	irHC := &ir.ActiveHealthCheck{
 		Timeout:            hc.Timeout,
 		Interval:           hc.Interval,
+		InitialJitter:      hc.InitialJitter,
 		UnhealthyThreshold: hc.UnhealthyThreshold,
 		HealthyThreshold:   hc.HealthyThreshold,
 	}
diff --git a/internal/gatewayapi/filters.go b/internal/gatewayapi/filters.go
index c2e52bb98..631de3247 100644
--- a/internal/gatewayapi/filters.go
+++ b/internal/gatewayapi/filters.go
@@ -212,20 +212,28 @@ func (t *Translator) processURLRewriteFilter(
 		return
 	}
 
-	newURLRewrite := &ir.URLRewrite{}
-
 	if rewrite.Hostname != nil {
 		if err := t.validateHostname(string(*rewrite.Hostname)); err != nil {
 			updateRouteStatusForFilter(filterContext, err.Error())
 			return
 		}
 		redirectHost := string(*rewrite.Hostname)
-		newURLRewrite.Host = &ir.HTTPHostModifier{
-			Name: &redirectHost,
+		if filterContext.URLRewrite == nil {
+			filterContext.URLRewrite = &ir.URLRewrite{
+				Host: &ir.HTTPHostModifier{
+					Name: &redirectHost,
+				},
+			}
+		} else if filterContext.URLRewrite.Host == nil {
+			filterContext.URLRewrite.Host = &ir.HTTPHostModifier{
+				Name: &redirectHost,
+			}
 		}
 	}
 
 	if rewrite.Path != nil {
+		var pathModifier *ir.ExtendedHTTPPathModifier
+
 		switch rewrite.Path.Type {
 		case gwapiv1.FullPathHTTPPathModifier:
 			if rewrite.Path.ReplacePrefixMatch != nil {
@@ -241,7 +249,7 @@ func (t *Translator) processURLRewriteFilter(
 				return
 			}
 			if rewrite.Path.ReplaceFullPath != nil {
-				newURLRewrite.Path = &ir.ExtendedHTTPPathModifier{
+				pathModifier = &ir.ExtendedHTTPPathModifier{
 					HTTPPathModifier: ir.HTTPPathModifier{
 						FullReplace: rewrite.Path.ReplaceFullPath,
 					},
@@ -261,7 +269,7 @@ func (t *Translator) processURLRewriteFilter(
 				return
 			}
 			if rewrite.Path.ReplacePrefixMatch != nil {
-				newURLRewrite.Path = &ir.ExtendedHTTPPathModifier{
+				pathModifier = &ir.ExtendedHTTPPathModifier{
 					HTTPPathModifier: ir.HTTPPathModifier{
 						PrefixMatchReplace: rewrite.Path.ReplacePrefixMatch,
 					},
@@ -275,9 +283,14 @@ func (t *Translator) processURLRewriteFilter(
 					rewrite.Path.Type))
 			return
 		}
+		if filterContext.URLRewrite == nil {
+			filterContext.URLRewrite = &ir.URLRewrite{
+				Path: pathModifier,
+			}
+		} else if filterContext.URLRewrite.Path == nil {
+			filterContext.URLRewrite.Path = pathModifier
+		}
 	}
-
-	filterContext.URLRewrite = newURLRewrite
 }
 
 func (t *Translator) processRedirectFilter(
diff --git a/internal/gatewayapi/securitypolicy.go b/internal/gatewayapi/securitypolicy.go
index 5067e3df8..67a8a5c6c 100644
--- a/internal/gatewayapi/securitypolicy.go
+++ b/internal/gatewayapi/securitypolicy.go
@@ -1342,8 +1342,10 @@ func (t *Translator) buildAPIKeyAuth(
 	}
 
 	return &ir.APIKeyAuth{
-		Credentials: credentials,
-		ExtractFrom: extractFrom,
+		Credentials:           credentials,
+		ExtractFrom:           extractFrom,
+		ForwardClientIDHeader: policy.Spec.APIKeyAuth.ForwardClientIDHeader,
+		Sanitize:              policy.Spec.APIKeyAuth.Sanitize,
 	}, nil
 }
 
diff --git a/internal/ir/xds.go b/internal/ir/xds.go
index e5ffefc02..695fa6a9d 100644
--- a/internal/ir/xds.go
+++ b/internal/ir/xds.go
@@ -16,6 +16,7 @@ import (
 	"net/http"
 	"net/netip"
 	"reflect"
+	"time"
 
 	"golang.org/x/exp/slices"
 	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
@@ -24,6 +25,7 @@ import (
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 	"k8s.io/apimachinery/pkg/util/sets"
 	"k8s.io/apimachinery/pkg/util/validation"
+	gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
 	gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
 	"sigs.k8s.io/yaml"
 
@@ -65,6 +67,7 @@ var (
 	ErrLoadBalancerInvalid                      = errors.New("loadBalancer setting is invalid, only one setting can be set")
 	ErrHealthCheckTimeoutInvalid                = errors.New("field HealthCheck.Timeout must be specified")
 	ErrHealthCheckIntervalInvalid               = errors.New("field HealthCheck.Interval must be specified")
+	ErrHealthCheckInitialJitterInvalid          = errors.New("field HealthCheck.InitialJitter should be greater than or equal to 0")
 	ErrHealthCheckUnhealthyThresholdInvalid     = errors.New("field HealthCheck.UnhealthyThreshold should be greater than 0")
 	ErrHealthCheckHealthyThresholdInvalid       = errors.New("field HealthCheck.HealthyThreshold should be greater than 0")
 	ErrHealthCheckerInvalid                     = errors.New("health checker setting is invalid, only one health checker can be set")
@@ -1205,6 +1208,17 @@ type APIKeyAuth struct {
 	// ExtractFrom is where to fetch the key from the coming request.
 	// The value from the first source that has a key will be used.
 	ExtractFrom []*ExtractFrom `json:"extractFrom"`
+
+	// ForwardClientIDHeader is the name of the header to forward the client identity to the backend
+	// service. The header will be added to the request with the client id as the value.
+	//
+	// +optional
+	ForwardClientIDHeader *string `json:"forwardClientIDHeader,omitempty"`
+
+	// Sanitize indicates whether to remove the API key from the request before forwarding it to the backend service.
+	//
+	// +optional
+	Sanitize *bool `json:"sanitize,omitempty"`
 }
 
 // ExtractFrom defines the source of the key.
@@ -2647,6 +2661,8 @@ type ActiveHealthCheck struct {
 	Timeout *metav1.Duration `json:"timeout"`
 	// Interval defines the time between active health checks.
 	Interval *metav1.Duration `json:"interval"`
+	// InitialJitter defines the initial jitter to apply to the health check interval.
+	InitialJitter *gwapiv1.Duration `json:"initialJitter,omitempty"`
 	// UnhealthyThreshold defines the number of unhealthy health checks required before a backend host is marked unhealthy.
 	UnhealthyThreshold *uint32 `json:"unhealthyThreshold"`
 	// HealthyThreshold defines the number of healthy health checks required before a backend host is marked healthy.
@@ -2675,6 +2691,11 @@ func (h *HealthCheck) Validate() error {
 		if h.Active.Interval != nil && h.Active.Interval.Duration == 0 {
 			errs = errors.Join(errs, ErrHealthCheckIntervalInvalid)
 		}
+		if h.Active.InitialJitter != nil {
+			if d, err := time.ParseDuration(string(*h.Active.InitialJitter)); err != nil || d < 0 {
+				errs = errors.Join(errs, ErrHealthCheckInitialJitterInvalid)
+			}
+		}
 		if h.Active.UnhealthyThreshold != nil && *h.Active.UnhealthyThreshold == 0 {
 			errs = errors.Join(errs, ErrHealthCheckUnhealthyThresholdInvalid)
 		}
@@ -2901,6 +2922,7 @@ const (
 	Error5XX             = TriggerEnum(egv1a1.Error5XX)
 	GatewayError         = TriggerEnum(egv1a1.GatewayError)
 	Reset                = TriggerEnum(egv1a1.Reset)
+	ResetBeforeRequest   = TriggerEnum(egv1a1.ResetBeforeRequest)
 	ConnectFailure       = TriggerEnum(egv1a1.ConnectFailure)
 	Retriable4XX         = TriggerEnum(egv1a1.Retriable4XX)
 	RefusedStream        = TriggerEnum(egv1a1.RefusedStream)
diff --git a/internal/ir/zz_generated.deepcopy.go b/internal/ir/zz_generated.deepcopy.go
index b7a3a4486..c45b5b868 100644
--- a/internal/ir/zz_generated.deepcopy.go
+++ b/internal/ir/zz_generated.deepcopy.go
@@ -13,6 +13,7 @@ import (
 	"github.com/envoyproxy/gateway/api/v1alpha1"
 	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
 	"k8s.io/apimachinery/pkg/apis/meta/v1"
+	apisv1 "sigs.k8s.io/gateway-api/apis/v1"
 	"sigs.k8s.io/gateway-api/apis/v1alpha2"
 )
 
@@ -124,6 +125,16 @@ func (in *APIKeyAuth) DeepCopyInto(out *APIKeyAuth) {
 			}
 		}
 	}
+	if in.ForwardClientIDHeader != nil {
+		in, out := &in.ForwardClientIDHeader, &out.ForwardClientIDHeader
+		*out = new(string)
+		**out = **in
+	}
+	if in.Sanitize != nil {
+		in, out := &in.Sanitize, &out.Sanitize
+		*out = new(bool)
+		**out = **in
+	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new APIKeyAuth.
@@ -208,6 +219,11 @@ func (in *ActiveHealthCheck) DeepCopyInto(out *ActiveHealthCheck) {
 		*out = new(v1.Duration)
 		**out = **in
 	}
+	if in.InitialJitter != nil {
+		in, out := &in.InitialJitter, &out.InitialJitter
+		*out = new(apisv1.Duration)
+		**out = **in
+	}
 	if in.UnhealthyThreshold != nil {
 		in, out := &in.UnhealthyThreshold, &out.UnhealthyThreshold
 		*out = new(uint32)
diff --git a/internal/xds/translator/api_key_auth.go b/internal/xds/translator/api_key_auth.go
index 226098053..5470537e7 100644
--- a/internal/xds/translator/api_key_auth.go
+++ b/internal/xds/translator/api_key_auth.go
@@ -13,6 +13,7 @@ import (
 	apikeyauthv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/api_key_auth/v3"
 	hcmv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
 	"google.golang.org/protobuf/types/known/anypb"
+	"k8s.io/utils/ptr"
 
 	egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1"
 	"github.com/envoyproxy/gateway/internal/ir"
@@ -155,6 +156,15 @@ func buildAPIKeyAuthFilterConfig(apiKeyAuth *ir.APIKeyAuth) *apikeyauthv3.ApiKey
 		}
 	}
 
+	clientIDHeader := ptr.Deref(apiKeyAuth.ForwardClientIDHeader, "")
+	sanitize := ptr.Deref(apiKeyAuth.Sanitize, false)
+	if clientIDHeader != "" || sanitize {
+		apiKeyAuthProto.Forwarding = &apikeyauthv3.Forwarding{
+			Header:          *apiKeyAuth.ForwardClientIDHeader,
+			HideCredentials: ptr.Deref(apiKeyAuth.Sanitize, false),
+		}
+	}
+
 	return apiKeyAuthProto
 }
 
@@ -163,5 +173,6 @@ func buildAPIKeyAuthFilterPerRouteConfig(apiKeyAuth *ir.APIKeyAuth) *apikeyauthv
 	return &apikeyauthv3.ApiKeyAuthPerRoute{
 		Credentials: apiKeyAuthProto.Credentials,
 		KeySources:  apiKeyAuthProto.KeySources,
+		Forwarding:  apiKeyAuthProto.Forwarding,
 	}
 }
diff --git a/internal/xds/translator/cluster.go b/internal/xds/translator/cluster.go
index 1166229e8..78378ea08 100644
--- a/internal/xds/translator/cluster.go
+++ b/internal/xds/translator/cluster.go
@@ -444,6 +444,11 @@ func buildXdsHealthCheck(healthcheck *ir.ActiveHealthCheck) []*corev3.HealthChec
 		Timeout:  durationpb.New(healthcheck.Timeout.Duration),
 		Interval: durationpb.New(healthcheck.Interval.Duration),
 	}
+	if healthcheck.InitialJitter != nil {
+		if d, err := time.ParseDuration(string(*healthcheck.InitialJitter)); err == nil {
+			hc.InitialJitter = durationpb.New(d)
+		}
+	}
 	if healthcheck.UnhealthyThreshold != nil {
 		hc.UnhealthyThreshold = wrapperspb.UInt32(*healthcheck.UnhealthyThreshold)
 	}
diff --git a/internal/xds/translator/route.go b/internal/xds/translator/route.go
index 7458145c2..9f2a5ca48 100644
--- a/internal/xds/translator/route.go
+++ b/internal/xds/translator/route.go
@@ -771,6 +771,7 @@ func buildRetryOn(triggers []ir.TriggerEnum) (string, error) {
 		ir.Error5XX:             "5xx",
 		ir.GatewayError:         "gateway-error",
 		ir.Reset:                "reset",
+		ir.ResetBeforeRequest:   "reset-before-request",
 		ir.ConnectFailure:       "connect-failure",
 		ir.Retriable4XX:         "retriable-4xx",
 		ir.RefusedStream:        "refused-stream",
diff --git a/release-notes/current.yaml b/release-notes/current.yaml
index 4aaba48f1..12155091c 100644
--- a/release-notes/current.yaml
+++ b/release-notes/current.yaml
@@ -12,6 +12,7 @@ security updates: |
 # New features or capabilities added in this release.
 new features: |
   Added support for percentage-based request mirroring
+  Added support for setting `initialJitter` in the BackendTrafficPolicy active health check.
   Add an option to OIDC authentication to bypass it and defer to JWT when the request contains an "Authorization: Bearer ..." header.
   Added support for configuring Subject Alternative Names (SANs) for upstream TLS validation via `BackendTLSPolicy.validation.subjectAltNames`.
   Added support for local rate limit header.
@@ -36,6 +37,7 @@ new features: |
   Added support for SecurityPolicy and EnvoyExtensionPolicy to target ServiceImport via BackendRefs.
   Introduce validation strictness levels for Lua scripts in EnvoyExtensionPolicies.
   Added metric `watchable_publish_total` counting store events in watchable message queues.
+  Added support for forwarding client ID header and sanitizing API keys for API Key authentication in SecurityPolicy.
   Accessloggers of type ALS now have http2 enabled on the cluster by default.
 
 bug fixes: |
diff --git a/site/content/en/v1.4/tasks/operations/airgap-deployment.md b/site/content/en/v1.4/tasks/operations/airgap-deployment.md
new file mode 100644
index 000000000..8b72c776d
--- /dev/null
+++ b/site/content/en/v1.4/tasks/operations/airgap-deployment.md
@@ -0,0 +1,160 @@
+---
+title: 'Deploy Envoy Gateway in Air-Gapped Environments'
+---
+
+Deploying the Envoy Gateway in an air-gapped environment using a Helm chart
+requires careful configuration of the `values.yaml` file as well as adjustments
+when deploying a Gateway resource.
+
+You will need to specify custom image repositories for the following components
+in the Helm chart. This can be done on a global level or image level.
+
+- Gateway
+- Ratelimit
+
+## Gateway – `values.yaml` Configuration
+
+Example done in image level:
+
+```yaml
+deployment:
+  envoyGateway:
+    image:
+      repository: custom-cr.internal.io/envoyproxy/gateway
+      tag: v1.4.1
+```
+
+It's also possible to define the registry on a global level:
+
+```yaml
+# Global settings
+global:
+  # If set, these take highest precedence and change both envoyGateway and ratelimit's container registry and pull secrets.
+  # -- Global override for image registry
+  imageRegistry: 'custom-cr.internal.io'
+```
+
+## Ratelimit - `values.yaml` Configuration
+
+Example done on global level:
+
+```yaml
+global:
+  images:
+    ratelimit:
+      image: custom-cr.internal.io/envoyproxy/ratelimit:master
+```
+
+Furthermore for private registries you might need to define imagePullSecrets.
+
+On global level:
+
+```yaml
+global:
+  imagePullSecrets:
+    - my-private-registry-secret
+```
+
+or per image
+
+```yaml
+global:
+  images:
+    ratelimit:
+      pullSecrets:
+        - name: my-private-registry-secret
+```
+
+## Gateway Requires a Custom EnvoyProxy Reference
+
+Either the Gateway or GatewayClass must reference a custom EnvoyProxy resource
+that explicitly specifies the location of the distroless Envoy container image.
+Without this, the image will be pulled implicitly from Docker Hub.
+
+```yaml
+apiVersion: gateway.envoyproxy.io/v1alpha1
+kind: EnvoyProxy
+metadata:
+  name: custom-envoy-proxy
+  namespace: default
+spec:
+  provider:
+    type: Kubernetes
+    kubernetes:
+      envoyDeployment:
+        container:
+          image: custom-cr.internal.io/envoyproxy/envoy:distroless-v1.34.1
+```
+
+Then, reference the `custom-envoy-proxy` in your Gateway manifest
+
+```yaml
+apiVersion: gateway.networking.k8s.io/v1
+kind: Gateway
+metadata:
+  name: envoy-gateway
+  namespace: default
+  annotations:
+    cert-manager.io/cluster-issuer: cluster-wide-ca-issuer
+    cert-manager.io/duration: 8760h
+    cert-manager.io/renew-before: 360h
+    cert-manager.io/usages: server auth, client auth
+spec:
+  gatewayClassName: envoy-gateway-class
+  infrastructure:
+    parametersRef:
+      group: gateway.envoyproxy.io
+      kind: EnvoyProxy
+      name: custom-envoy-proxy
+  listeners:
+    - hostname: example.com
+      name: https
+      port: 443
+      protocol: HTTPS
+      tls:
+        certificateRefs:
+          - name: example-tls
+        mode: Terminate
+```
+
+or reference it in your GatewayClass, so that each new Gateway uses the
+EnvoyProxy automatically:
+
+```yaml
+apiVersion: gateway.networking.k8s.io/v1
+kind: GatewayClass
+metadata:
+  name: envoy-gateway-class
+spec:
+  controllerName: gateway.envoyproxy.io/gatewayclass-controller
+  parametersRef:
+    group: gateway.envoyproxy.io
+    kind: EnvoyProxy
+    name: custom-envoy-proxy
+    namespace: default
+```
+
+## Default LoadBalancer Service Type
+
+By default, Envoy uses a Service of type `LoadBalancer`. Depending on your
+Kubernetes environment, you might need to add custom annotations. For example,
+when deploying in Azure, you can configure the service as follows:
+
+```yaml
+apiVersion: gateway.envoyproxy.io/v1alpha1
+kind: EnvoyProxy
+metadata:
+  name: custom-envoy-proxy
+  namespace: default
+spec:
+  provider:
+    type: Kubernetes
+    kubernetes:
+      envoyService:
+        type: LoadBalancer
+        annotations:
+          service.beta.kubernetes.io/azure-load-balancer-internal: 'true'
+      envoyDeployment:
+        container:
+          image: custom-cr.internal.io/envoyproxy/envoy:distroless-v1.34.1
+```
diff --git a/site/data/ecosystem.yaml b/site/data/ecosystem.yaml
index 0a7e04b09..018273e58 100644
--- a/site/data/ecosystem.yaml
+++ b/site/data/ecosystem.yaml
@@ -23,3 +23,7 @@ adopters:
     url: "https://k0rdent.io/"
     logo: /logos/k0rdent.svg
     description: "k0rdent integrates with Envoy Gateway to offer advanced, high-performance traffic management within Kubernetes. k0rdent simplifies Envoy configuration, enabling users to easily define routing, security, and observability policies. This integration provides a robust, scalable, and secure ingress solution for applications deployed within the k0rdent-managed cluster."
+  - name: "KEDA"
+    logo: "/logos/keda.svg"
+    url: "https://keda.sh"
+    description: "KEDA integrates with Envoy Gateway through http-add-on for autoscaling workloads based on real-time HTTP traffic metrics. This integration allows users to leverage Envoy Gateway's capabilities for scaling behavior shaping desired traffic patterns."
diff --git a/site/static/logos/keda.svg b/site/static/logos/keda.svg
new file mode 100644
index 000000000..2dba5f8fd
--- /dev/null
+++ b/site/static/logos/keda.svg
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Warstwa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 906.2 906.2" style="enable-background:new 0 0 906.2 906.2;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:#326DE6;}
+	.st1{fill:#FFFFFF;}
+</style>
+<title>keda-word</title>
+<g id="Warstwa_1-2">
+	<g id="Wordmark">
+		<path id="KEDA" class="st0" d="M207.5,492.8l-47.2-84.5L146,422.8v70h-37V308h37v59.7l54.1-59.7H250l-64.7,72.3l66.3,112.5H207.5z
+			 M412.9,308v37H289.7v-37H412.9z M289.7,416.2v-37h102.1v37H289.7z M289.7,493.1v-37h122.5v37H289.7z M450.9,308h52.3
+			c24.5-0.2,48,9.6,65.2,26.9c8.5,8.4,15.3,18.5,19.8,29.6c9.9,23,9.9,49.1,0,72.1c-4.5,11.1-11.2,21-19.8,29.3
+			c-17.3,17.3-40.7,27.1-65.2,27.2H466v-0.3h-15.1V308z M487.9,456.1h15.3c30.7-0.4,55.2-25.6,54.8-56.3
+			c-0.4-30.1-24.7-54.4-54.8-54.8h-15.3V456.1z M698.9,308h10.8l87.4,185.1h-41l-52.8-112.4l-57.3,112.4h-41.5L698.9,308z"/>
+
+			<image style="overflow:visible;opacity:0.95;enable-background:new    ;" width="126" height="231" xlink:href="A04350A68EF731DF.png"  transform="matrix(0.9921 0 0 0.9921 625.9449 369.0308)">
+		</image>
+		<polygon class="st1" points="723.6,485.3 733.6,467.4 694.8,467.4 706.4,387.1 705.8,386 644.6,497 644.6,497 642.7,500.3
+			682.1,499.9 671.6,578.9 671.9,580 719.3,493 719.3,493 719.3,493 		"/>
+		<path id="Combined-Shape" class="st0" d="M719.3,493.1l14.1-25.6h-38.7l12.1-83.3l-60.1,108.9h-13.3l69.8-131l69.8,131H719.3z"/>
+	</g>
+</g>
+</svg>



Test patch:
diff --git a/internal/admin/console/api_test.go b/internal/admin/console/api_test.go
new file mode 100644
index 000000000..e677184b0
--- /dev/null
+++ b/internal/admin/console/api_test.go
@@ -0,0 +1,416 @@
+// Copyright Envoy Gateway Authors
+// SPDX-License-Identifier: Apache-2.0
+// The full text of the Apache license is available in the LICENSE file at
+// the root of the repo.
+
+package console
+
+import (
+	"encoding/json"
+	"net/http"
+	"net/http/httptest"
+	"os"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+	"github.com/telepresenceio/watchable"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
+
+	egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1"
+	"github.com/envoyproxy/gateway/internal/envoygateway/config"
+	"github.com/envoyproxy/gateway/internal/gatewayapi/resource"
+	"github.com/envoyproxy/gateway/internal/logging"
+	"github.com/envoyproxy/gateway/internal/message"
+)
+
+func TestHandleAPIInfo(t *testing.T) {
+	cfg := &config.Server{
+		EnvoyGateway: egv1a1.DefaultEnvoyGateway(),
+		Logger:       logging.DefaultLogger(nil, egv1a1.LogLevelInfo),
+	}
+
+	handler := NewHandler(cfg, (*message.ProviderResources)(nil))
+
+	req := httptest.NewRequest(http.MethodGet, "/api/info", nil)
+	w := httptest.NewRecorder()
+
+	handler.handleAPIInfo(w, req)
+
+	assert.Equal(t, http.StatusOK, w.Code)
+	assert.Equal(t, "application/json", w.Header().Get("Content-Type"))
+
+	var info SystemInfo
+	err := json.Unmarshal(w.Body.Bytes(), &info)
+	require.NoError(t, err)
+
+	// Version might be empty in test environment
+	assert.NotEmpty(t, info.GoVersion)
+	assert.NotEmpty(t, info.Platform)
+	assert.NotEmpty(t, info.Uptime)
+	assert.False(t, info.StartTime.IsZero())
+	assert.False(t, info.Timestamp.IsZero())
+}
+
+func TestHandleAPIInfoMethodNotAllowed(t *testing.T) {
+	cfg := &config.Server{
+		EnvoyGateway: egv1a1.DefaultEnvoyGateway(),
+		Logger:       logging.DefaultLogger(nil, egv1a1.LogLevelInfo),
+	}
+
+	handler := NewHandler(cfg, (*message.ProviderResources)(nil))
+
+	req := httptest.NewRequest(http.MethodPost, "/api/info", nil)
+	w := httptest.NewRecorder()
+
+	handler.handleAPIInfo(w, req)
+
+	assert.Equal(t, http.StatusMethodNotAllowed, w.Code)
+}
+
+func TestHandleAPIServerInfo(t *testing.T) {
+	cfg := &config.Server{
+		EnvoyGateway: egv1a1.DefaultEnvoyGateway(),
+		Logger:       logging.DefaultLogger(nil, egv1a1.LogLevelInfo),
+	}
+
+	handler := NewHandler(cfg, (*message.ProviderResources)(nil))
+
+	req := httptest.NewRequest(http.MethodGet, "/api/server_info", nil)
+	w := httptest.NewRecorder()
+
+	handler.handleAPIServerInfo(w, req)
+
+	assert.Equal(t, http.StatusOK, w.Code)
+	assert.Equal(t, "application/json", w.Header().Get("Content-Type"))
+
+	var info ServerInfo
+	err := json.Unmarshal(w.Body.Bytes(), &info)
+	require.NoError(t, err)
+
+	assert.Equal(t, "Running", info.State)
+	// Version might be empty in test environment
+	assert.NotEmpty(t, info.Uptime)
+	assert.Len(t, info.Components, 4) // Core components only
+	assert.False(t, info.LastUpdated.IsZero())
+	assert.NotNil(t, info.EnvoyGatewayConfig) // Verify EnvoyGateway config is included
+
+	// Check component details
+	for _, component := range info.Components {
+		assert.NotEmpty(t, component.Name)
+		assert.Equal(t, "Running", component.Status)
+		assert.NotEmpty(t, component.Message)
+	}
+
+	// Verify EnvoyGateway configuration structure
+	configData, ok := info.EnvoyGatewayConfig.(map[string]interface{})
+	if ok {
+		// Check if it has the expected structure (this will be a map when JSON unmarshaled)
+		assert.Contains(t, configData, "kind")
+		assert.Contains(t, configData, "apiVersion")
+	}
+}
+
+func TestHandleAPIConfigDump(t *testing.T) {
+	cfg := &config.Server{
+		EnvoyGateway: egv1a1.DefaultEnvoyGateway(),
+		Logger:       logging.DefaultLogger(nil, egv1a1.LogLevelInfo),
+	}
+
+	// Create a mock provider resources
+	providerResources := &message.ProviderResources{}
+
+	handler := NewHandler(cfg, providerResources)
+
+	req := httptest.NewRequest(http.MethodGet, "/api/config_dump", nil)
+	w := httptest.NewRecorder()
+
+	handler.handleAPIConfigDump(w, req)
+
+	assert.Equal(t, http.StatusOK, w.Code)
+	assert.Equal(t, "application/json", w.Header().Get("Content-Type"))
+
+	var configDump ConfigDumpInfo
+	err := json.Unmarshal(w.Body.Bytes(), &configDump)
+	require.NoError(t, err)
+
+	assert.NotNil(t, configDump.Gateways)
+	assert.NotNil(t, configDump.HTTPRoutes)
+	assert.NotNil(t, configDump.GatewayClass)
+	assert.False(t, configDump.LastUpdated.IsZero())
+}
+
+func TestLoadConfigDumpWithData(t *testing.T) {
+	cfg := &config.Server{
+		EnvoyGateway: egv1a1.DefaultEnvoyGateway(),
+		Logger:       logging.DefaultLogger(nil, egv1a1.LogLevelInfo),
+	}
+
+	// Create a simple test without the complex watchable structure
+	// This test focuses on the basic functionality
+	providerRes := &message.ProviderResources{}
+	// Initialize empty watchable map
+	providerRes.GatewayAPIResources = watchable.Map[string, *resource.ControllerResources]{}
+
+	// Skip storing to avoid watchable copy issues
+	// providerResources.Store("test", providerRes)
+
+	handler := NewHandler(cfg, providerRes)
+
+	configDump := handler.loadConfigDump()
+
+	// Verify the response structure with empty data
+	// Gateway API Core Resources
+	assert.Empty(t, configDump.GatewayClass)
+	assert.Empty(t, configDump.Gateways)
+	assert.Empty(t, configDump.HTTPRoutes)
+	assert.Empty(t, configDump.GRPCRoutes)
+	assert.Empty(t, configDump.TLSRoutes)
+	assert.Empty(t, configDump.TCPRoutes)
+	assert.Empty(t, configDump.UDPRoutes)
+
+	// Envoy Gateway Policies
+	assert.Empty(t, configDump.ClientTrafficPolicies)
+	assert.Empty(t, configDump.BackendTrafficPolicies)
+	assert.Empty(t, configDump.BackendTLSPolicies)
+	assert.Empty(t, configDump.SecurityPolicies)
+	assert.Empty(t, configDump.EnvoyPatchPolicies)
+	assert.Empty(t, configDump.EnvoyExtensionPolicies)
+
+	// Kubernetes Resources
+	assert.Empty(t, configDump.Services)
+	assert.Empty(t, configDump.Secrets)
+	assert.Empty(t, configDump.ConfigMaps)
+	assert.Empty(t, configDump.Namespaces)
+	assert.Empty(t, configDump.EndpointSlices)
+
+	// Other Resources
+	assert.Empty(t, configDump.ReferenceGrants)
+	assert.Empty(t, configDump.HTTPRouteFilters)
+	assert.Empty(t, configDump.EnvoyProxies)
+	assert.Empty(t, configDump.Backends)
+	assert.Empty(t, configDump.ServiceImports)
+
+	// Verify all fields are properly initialized (not nil)
+	assert.NotNil(t, configDump.GatewayClass)
+	assert.NotNil(t, configDump.Gateways)
+	assert.NotNil(t, configDump.HTTPRoutes)
+	assert.NotNil(t, configDump.GRPCRoutes)
+	assert.NotNil(t, configDump.TLSRoutes)
+	assert.NotNil(t, configDump.TCPRoutes)
+	assert.NotNil(t, configDump.UDPRoutes)
+	assert.NotNil(t, configDump.ClientTrafficPolicies)
+	assert.NotNil(t, configDump.BackendTrafficPolicies)
+	assert.NotNil(t, configDump.BackendTLSPolicies)
+	assert.NotNil(t, configDump.SecurityPolicies)
+	assert.NotNil(t, configDump.EnvoyPatchPolicies)
+	assert.NotNil(t, configDump.EnvoyExtensionPolicies)
+	assert.NotNil(t, configDump.Services)
+	assert.NotNil(t, configDump.Secrets)
+	assert.NotNil(t, configDump.ConfigMaps)
+	assert.NotNil(t, configDump.Namespaces)
+	assert.NotNil(t, configDump.EndpointSlices)
+	assert.NotNil(t, configDump.ReferenceGrants)
+	assert.NotNil(t, configDump.HTTPRouteFilters)
+	assert.NotNil(t, configDump.EnvoyProxies)
+	assert.NotNil(t, configDump.Backends)
+	assert.NotNil(t, configDump.ServiceImports)
+
+	assert.False(t, configDump.LastUpdated.IsZero())
+}
+
+func TestGetGatewayClassStatus(t *testing.T) {
+	tests := []struct {
+		name       string
+		conditions []metav1.Condition
+		expected   string
+	}{
+		{
+			name:       "no conditions",
+			conditions: nil,
+			expected:   "Unknown",
+		},
+		{
+			name: "accepted",
+			conditions: []metav1.Condition{
+				{
+					Type:   string(gwapiv1.GatewayClassConditionStatusAccepted),
+					Status: metav1.ConditionTrue,
+				},
+			},
+			expected: "Accepted",
+		},
+		{
+			name: "not accepted",
+			conditions: []metav1.Condition{
+				{
+					Type:   string(gwapiv1.GatewayClassConditionStatusAccepted),
+					Status: metav1.ConditionFalse,
+				},
+			},
+			expected: "Not Accepted",
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			gc := &gwapiv1.GatewayClass{
+				Status: gwapiv1.GatewayClassStatus{
+					Conditions: tt.conditions,
+				},
+			}
+
+			result := getGatewayClassStatus(gc)
+			assert.Equal(t, tt.expected, result)
+		})
+	}
+}
+
+func TestGetGatewayStatus(t *testing.T) {
+	tests := []struct {
+		name       string
+		conditions []metav1.Condition
+		expected   string
+	}{
+		{
+			name:       "no conditions",
+			conditions: nil,
+			expected:   "Unknown",
+		},
+		{
+			name: "programmed",
+			conditions: []metav1.Condition{
+				{
+					Type:   string(gwapiv1.GatewayConditionProgrammed),
+					Status: metav1.ConditionTrue,
+				},
+			},
+			expected: "Programmed",
+		},
+		{
+			name: "accepted",
+			conditions: []metav1.Condition{
+				{
+					Type:   string(gwapiv1.GatewayConditionAccepted),
+					Status: metav1.ConditionTrue,
+				},
+			},
+			expected: "Accepted",
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			gw := &gwapiv1.Gateway{
+				Status: gwapiv1.GatewayStatus{
+					Conditions: tt.conditions,
+				},
+			}
+
+			result := getGatewayStatus(gw)
+			assert.Equal(t, tt.expected, result)
+		})
+	}
+}
+
+func TestHandleAPIConfigDumpAll(t *testing.T) {
+	cfg := &config.Server{
+		Logger: logging.NewLogger(os.Stdout, egv1a1.DefaultEnvoyGatewayLogging()),
+	}
+
+	providerRes := &message.ProviderResources{}
+	handler := NewHandler(cfg, providerRes)
+
+	req := httptest.NewRequest(http.MethodGet, "/api/config_dump_all", nil)
+	resp := httptest.NewRecorder()
+
+	handler.handleAPIConfigDumpAll(resp, req)
+
+	assert.Equal(t, http.StatusOK, resp.Code)
+	assert.Equal(t, "application/json; charset=utf-8", resp.Header().Get("Content-Type"))
+
+	// Should not have download headers
+	assert.Empty(t, resp.Header().Get("Content-Disposition"))
+
+	// Should return JSON response with structured format
+	var result map[string]interface{}
+	err := json.Unmarshal(resp.Body.Bytes(), &result)
+	require.NoError(t, err)
+
+	// Verify response structure
+	assert.Contains(t, result, "resources")
+	assert.Contains(t, result, "timestamp")
+	assert.Contains(t, result, "totalCount")
+
+	// Verify resources field exists (could be nil for empty case)
+	resources := result["resources"]
+	if resources != nil {
+		resourcesArray, ok := resources.([]interface{})
+		assert.True(t, ok)
+		assert.Empty(t, resourcesArray) // Empty for test case
+	} else {
+		// resources is nil, which is expected for empty provider resources
+		assert.Nil(t, resources)
+	}
+
+	// Verify totalCount matches resources length
+	totalCount, ok := result["totalCount"].(float64)
+	assert.True(t, ok)
+	assert.Equal(t, float64(0), totalCount)
+}
+
+func TestHandleAPIConfigDumpAllMethodNotAllowed(t *testing.T) {
+	cfg := &config.Server{
+		Logger: logging.NewLogger(os.Stdout, egv1a1.DefaultEnvoyGatewayLogging()),
+	}
+
+	handler := NewHandler(cfg, (*message.ProviderResources)(nil))
+
+	req := httptest.NewRequest(http.MethodPost, "/api/config_dump_all", nil)
+	resp := httptest.NewRecorder()
+
+	handler.handleAPIConfigDumpAll(resp, req)
+
+	assert.Equal(t, http.StatusMethodNotAllowed, resp.Code)
+}
+
+func TestHandleAPIMetrics(t *testing.T) {
+	cfg := &config.Server{
+		Logger: logging.NewLogger(os.Stdout, egv1a1.DefaultEnvoyGatewayLogging()),
+	}
+
+	handler := NewHandler(cfg, (*message.ProviderResources)(nil))
+
+	req := httptest.NewRequest(http.MethodGet, "/api/metrics", nil)
+	resp := httptest.NewRecorder()
+
+	handler.handleAPIMetrics(resp, req)
+
+	// Now we're using the Prometheus registry directly, so we should get a successful response
+	assert.Equal(t, http.StatusOK, resp.Code)
+	assert.Contains(t, resp.Header().Get("Content-Type"), "text/plain; version=0.0.4; charset=utf-8")
+
+	// Verify we get actual Prometheus metrics
+	body := resp.Body.String()
+	assert.Contains(t, body, "# HELP")
+	assert.Contains(t, body, "# TYPE")
+	// Should contain Go runtime metrics
+	assert.Contains(t, body, "go_goroutines")
+	assert.Contains(t, body, "process_cpu_seconds_total")
+}
+
+func TestHandleAPIMetricsMethodNotAllowed(t *testing.T) {
+	cfg := &config.Server{
+		Logger: logging.NewLogger(os.Stdout, egv1a1.DefaultEnvoyGatewayLogging()),
+	}
+
+	handler := NewHandler(cfg, (*message.ProviderResources)(nil))
+
+	req := httptest.NewRequest(http.MethodPost, "/api/metrics", nil)
+	resp := httptest.NewRecorder()
+
+	handler.handleAPIMetrics(resp, req)
+
+	assert.Equal(t, http.StatusMethodNotAllowed, resp.Code)
+}
diff --git a/internal/admin/console/handlers_test.go b/internal/admin/console/handlers_test.go
new file mode 100644
index 000000000..9d1b8f1af
--- /dev/null
+++ b/internal/admin/console/handlers_test.go
@@ -0,0 +1,211 @@
+// Copyright Envoy Gateway Authors
+// SPDX-License-Identifier: Apache-2.0
+// The full text of the Apache license is available in the LICENSE file at
+// the root of the repo.
+
+package console
+
+import (
+	"net/http"
+	"net/http/httptest"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+
+	egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1"
+	"github.com/envoyproxy/gateway/internal/envoygateway/config"
+	"github.com/envoyproxy/gateway/internal/logging"
+	"github.com/envoyproxy/gateway/internal/message"
+)
+
+func TestNewHandler(t *testing.T) {
+	cfg := &config.Server{
+		EnvoyGateway: egv1a1.DefaultEnvoyGateway(),
+		Logger:       logging.DefaultLogger(nil, egv1a1.LogLevelInfo),
+	}
+
+	providerResources := &message.ProviderResources{}
+
+	handler := NewHandler(cfg, providerResources)
+
+	assert.NotNil(t, handler)
+	assert.Equal(t, cfg, handler.cfg)
+	assert.Equal(t, providerResources, handler.providerResources)
+}
+
+func TestHandleIndex(t *testing.T) {
+	cfg := &config.Server{
+		EnvoyGateway: egv1a1.DefaultEnvoyGateway(),
+		Logger:       logging.DefaultLogger(nil, egv1a1.LogLevelInfo),
+	}
+
+	handler := NewHandler(cfg, (*message.ProviderResources)(nil))
+
+	req := httptest.NewRequest(http.MethodGet, "/", nil)
+	w := httptest.NewRecorder()
+
+	handler.handleIndex(w, req)
+
+	assert.Equal(t, http.StatusOK, w.Code)
+	assert.Contains(t, w.Header().Get("Content-Type"), "text/html")
+	assert.Contains(t, w.Body.String(), "Envoy Gateway Admin Console")
+}
+
+func TestHandleIndexMethodNotAllowed(t *testing.T) {
+	cfg := &config.Server{
+		EnvoyGateway: egv1a1.DefaultEnvoyGateway(),
+		Logger:       logging.DefaultLogger(nil, egv1a1.LogLevelInfo),
+	}
+
+	handler := NewHandler(cfg, nil)
+
+	req := httptest.NewRequest(http.MethodPost, "/", nil)
+	w := httptest.NewRecorder()
+
+	handler.handleIndex(w, req)
+
+	assert.Equal(t, http.StatusMethodNotAllowed, w.Code)
+}
+
+func TestHandlePprof(t *testing.T) {
+	tests := []struct {
+		name        string
+		enablePprof bool
+		expectBody  string
+	}{
+		{
+			name:        "pprof enabled",
+			enablePprof: true,
+			expectBody:  "Performance Profiling",
+		},
+		{
+			name:        "pprof disabled",
+			enablePprof: false,
+			expectBody:  "Performance Profiling",
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			cfg := &config.Server{
+				EnvoyGateway: &egv1a1.EnvoyGateway{
+					EnvoyGatewaySpec: egv1a1.EnvoyGatewaySpec{
+						Admin: &egv1a1.EnvoyGatewayAdmin{
+							EnablePprof: tt.enablePprof,
+						},
+					},
+				},
+				Logger: logging.DefaultLogger(nil, egv1a1.LogLevelInfo),
+			}
+
+			handler := NewHandler(cfg, (*message.ProviderResources)(nil))
+
+			req := httptest.NewRequest(http.MethodGet, "/pprof", nil)
+			w := httptest.NewRecorder()
+
+			handler.handlePprof(w, req)
+
+			assert.Equal(t, http.StatusOK, w.Code)
+			assert.Contains(t, w.Header().Get("Content-Type"), "text/html")
+			assert.Contains(t, w.Body.String(), tt.expectBody)
+		})
+	}
+}
+
+func TestHandleServerInfo(t *testing.T) {
+	cfg := &config.Server{
+		EnvoyGateway: egv1a1.DefaultEnvoyGateway(),
+		Logger:       logging.DefaultLogger(nil, egv1a1.LogLevelInfo),
+	}
+
+	handler := NewHandler(cfg, (*message.ProviderResources)(nil))
+
+	req := httptest.NewRequest(http.MethodGet, "/server_info", nil)
+	w := httptest.NewRecorder()
+
+	handler.handleServerInfo(w, req)
+
+	assert.Equal(t, http.StatusOK, w.Code)
+	assert.Contains(t, w.Header().Get("Content-Type"), "text/html")
+	assert.Contains(t, w.Body.String(), "Server Information")
+}
+
+func TestHandleConfigDump(t *testing.T) {
+	cfg := &config.Server{
+		EnvoyGateway: egv1a1.DefaultEnvoyGateway(),
+		Logger:       logging.DefaultLogger(nil, egv1a1.LogLevelInfo),
+	}
+
+	handler := NewHandler(cfg, (*message.ProviderResources)(nil))
+
+	req := httptest.NewRequest(http.MethodGet, "/config_dump", nil)
+	w := httptest.NewRecorder()
+
+	handler.handleConfigDump(w, req)
+
+	assert.Equal(t, http.StatusOK, w.Code)
+	assert.Contains(t, w.Header().Get("Content-Type"), "text/html")
+	assert.Contains(t, w.Body.String(), "Configuration Dump")
+}
+
+func TestHandleStats(t *testing.T) {
+	cfg := &config.Server{
+		EnvoyGateway: egv1a1.DefaultEnvoyGateway(),
+		Logger:       logging.DefaultLogger(nil, egv1a1.LogLevelInfo),
+	}
+
+	handler := NewHandler(cfg, (*message.ProviderResources)(nil))
+
+	req := httptest.NewRequest(http.MethodGet, "/stats", nil)
+	w := httptest.NewRecorder()
+
+	handler.handleStats(w, req)
+
+	assert.Equal(t, http.StatusOK, w.Code)
+	assert.Contains(t, w.Header().Get("Content-Type"), "text/html")
+	assert.Contains(t, w.Body.String(), "Statistics")
+}
+
+func TestRegisterRoutes(t *testing.T) {
+	cfg := &config.Server{
+		EnvoyGateway: egv1a1.DefaultEnvoyGateway(),
+		Logger:       logging.DefaultLogger(nil, egv1a1.LogLevelInfo),
+	}
+
+	handler := NewHandler(cfg, (*message.ProviderResources)(nil))
+	mux := http.NewServeMux()
+
+	// This should not panic
+	require.NotPanics(t, func() {
+		handler.RegisterRoutes(mux)
+	})
+
+	// Test that routes are registered by making requests
+	testCases := []struct {
+		path           string
+		expectedStatus int
+	}{
+		{"/", http.StatusOK},
+		{"/pprof", http.StatusOK},
+		{"/server_info", http.StatusOK},
+		{"/config_dump", http.StatusOK},
+		{"/stats", http.StatusOK},
+		{"/api/info", http.StatusOK},
+		{"/api/server_info", http.StatusOK},
+		{"/api/config_dump", http.StatusOK},
+		{"/api/config_dump_all", http.StatusOK},
+		{"/api/metrics", http.StatusOK}, // Now uses Prometheus registry directly
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.path, func(t *testing.T) {
+			req := httptest.NewRequest(http.MethodGet, tc.path, nil)
+			w := httptest.NewRecorder()
+
+			mux.ServeHTTP(w, req)
+
+			assert.Equal(t, tc.expectedStatus, w.Code)
+		})
+	}
+}
diff --git a/internal/admin/console/static_test.go b/internal/admin/console/static_test.go
new file mode 100644
index 000000000..958851f2f
--- /dev/null
+++ b/internal/admin/console/static_test.go
@@ -0,0 +1,64 @@
+// Copyright Envoy Gateway Authors
+// SPDX-License-Identifier: Apache-2.0
+// The full text of the Apache license is available in the LICENSE file at
+// the root of the repo.
+
+package console
+
+import (
+	"net/http"
+	"net/http/httptest"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+
+	egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1"
+	"github.com/envoyproxy/gateway/internal/envoygateway/config"
+	"github.com/envoyproxy/gateway/internal/logging"
+	"github.com/envoyproxy/gateway/internal/message"
+)
+
+func TestStaticFiles(t *testing.T) {
+	cfg := &config.Server{
+		EnvoyGateway: egv1a1.DefaultEnvoyGateway(),
+		Logger:       logging.DefaultLogger(nil, egv1a1.LogLevelInfo),
+	}
+
+	handler := NewHandler(cfg, (*message.ProviderResources)(nil))
+	mux := http.NewServeMux()
+	handler.RegisterRoutes(mux)
+
+	// Test CSS file
+	t.Run("CSS file", func(t *testing.T) {
+		req := httptest.NewRequest(http.MethodGet, "/static/css/admin.css", nil)
+		w := httptest.NewRecorder()
+
+		mux.ServeHTTP(w, req)
+
+		assert.Equal(t, http.StatusOK, w.Code)
+		assert.Contains(t, w.Header().Get("Content-Type"), "text/css")
+		assert.Contains(t, w.Body.String(), "Envoy Gateway Admin Console Styles")
+	})
+
+	// Test JS file
+	t.Run("JS file", func(t *testing.T) {
+		req := httptest.NewRequest(http.MethodGet, "/static/js/admin.js", nil)
+		w := httptest.NewRecorder()
+
+		mux.ServeHTTP(w, req)
+
+		assert.Equal(t, http.StatusOK, w.Code)
+		assert.Contains(t, w.Header().Get("Content-Type"), "javascript")
+		assert.Contains(t, w.Body.String(), "EnvoyGatewayAdmin")
+	})
+
+	// Test non-existent file
+	t.Run("Non-existent file", func(t *testing.T) {
+		req := httptest.NewRequest(http.MethodGet, "/static/nonexistent.txt", nil)
+		w := httptest.NewRecorder()
+
+		mux.ServeHTTP(w, req)
+
+		assert.Equal(t, http.StatusNotFound, w.Code)
+	})
+}
diff --git a/internal/admin/console/template_test.go b/internal/admin/console/template_test.go
new file mode 100644
index 000000000..46a2ee2ad
--- /dev/null
+++ b/internal/admin/console/template_test.go
@@ -0,0 +1,70 @@
+// Copyright Envoy Gateway Authors
+// SPDX-License-Identifier: Apache-2.0
+// The full text of the Apache license is available in the LICENSE file at
+// the root of the repo.
+
+package console
+
+import (
+	"bytes"
+	"fmt"
+	"html/template"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+func TestTemplateLoading(t *testing.T) {
+	// Test each template using the same method as the handler
+	testCases := []struct {
+		templateName string
+		contentBlock string
+		expectedText string
+	}{
+		{"index.html", "index-content", "Welcome to the Envoy Gateway Admin Console"},
+		{"server_info.html", "server-info-content", "displays detailed information about the Envoy Gateway server"},
+		{"config_dump.html", "config-dump-content", "current configuration state"},
+		{"stats.html", "stats-content", "Envoy Gateway statistics"},
+		{"pprof.html", "pprof-content", "Performance profiling"},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.templateName, func(t *testing.T) {
+			// Create a new template instance for each page (same as handler)
+			tmpl := template.New(tc.templateName)
+
+			// Parse base template and specific page template
+			tmpl, err := tmpl.ParseFS(templateFiles, "templates/base.html", "templates/"+tc.templateName)
+			require.NoError(t, err)
+
+			// Create a wrapper template that calls the correct content block
+			wrapperTemplate := fmt.Sprintf(`{{define "content"}}{{template "%s" .}}{{end}}`, tc.contentBlock)
+			tmpl, err = tmpl.Parse(wrapperTemplate)
+			require.NoError(t, err)
+
+			var buf bytes.Buffer
+			data := struct {
+				Title          string
+				MetricsAddress string
+				EnablePprof    bool
+			}{
+				Title:          "Test",
+				MetricsAddress: "localhost:19001",
+				EnablePprof:    true,
+			}
+
+			// Execute the base template (same as handler)
+			err = tmpl.ExecuteTemplate(&buf, "base.html", data)
+			require.NoError(t, err)
+
+			content := buf.String()
+			assert.Contains(t, content, tc.expectedText, "Template %s should contain expected text", tc.templateName)
+
+			// Verify the template produces valid HTML structure
+			assert.Contains(t, content, "<html", "Template should contain HTML structure")
+			assert.Contains(t, content, "</html>", "Template should be properly closed")
+			assert.Contains(t, content, "Envoy Gateway", "Template should contain Envoy Gateway branding")
+		})
+	}
+}
diff --git a/internal/admin/server_test.go b/internal/admin/server_test.go
index 325af7e7c..7bf13e4cc 100644
--- a/internal/admin/server_test.go
+++ b/internal/admin/server_test.go
@@ -25,7 +25,9 @@ func TestInitAdminServer(t *testing.T) {
 	}
 
 	svrConfig.Logger = logging.NewLogger(os.Stdout, egv1a1.DefaultEnvoyGatewayLogging())
-	runner := New(svrConfig)
+	runner := New(&Config{
+		Server: *svrConfig,
+	})
 	err := runner.Start(context.Background())
 	require.NoError(t, err)
 
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-healthcheck.in.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-healthcheck.in.yaml
index e2c0a82d9..75f9dbd40 100644
--- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-healthcheck.in.yaml
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-healthcheck.in.yaml
@@ -147,6 +147,25 @@ httpRoutes:
       backendRefs:
       - name: service-2
         port: 8080
+- apiVersion: gateway.networking.k8s.io/v1
+  kind: HTTPRoute
+  metadata:
+    namespace: default
+    name: httproute-5
+  spec:
+    hostnames:
+    - gateway.envoyproxy.io
+    parentRefs:
+    - namespace: envoy-gateway
+      name: gateway-2
+      sectionName: http
+    rules:
+    - matches:
+      - path:
+          value: "/v5"
+      backendRefs:
+      - name: service-4
+        port: 8080
 backendTrafficPolicies:
 - apiVersion: gateway.envoyproxy.io/v1alpha1
   kind: BackendTrafficPolicy
@@ -304,6 +323,25 @@ backendTrafficPolicies:
         consecutiveGatewayErrors: 0
         consecutiveLocalOriginFailures: 5
         splitExternalLocalOriginErrors: false
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+  kind: BackendTrafficPolicy
+  metadata:
+    namespace: default
+    name: policy-for-route-5
+  spec:
+    targetRef:
+      group: gateway.networking.k8s.io
+      kind: HTTPRoute
+      name: httproute-5
+    healthCheck:
+      active:
+        timeout: "10s"
+        interval: "30s"
+        initialJitter: "15s"
+        unhealthyThreshold: 2
+        healthyThreshold: 2
+        type: TCP
+        tcp: {}
 - apiVersion: gateway.envoyproxy.io/v1alpha1
   kind: BackendTrafficPolicy
   metadata:
@@ -340,4 +378,3 @@ backendTrafficPolicies:
         type: GRPC
         grpc:
           service: foo-service
-
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-healthcheck.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-healthcheck.out.yaml
index e7433b7da..08c012243 100644
--- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-healthcheck.out.yaml
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-healthcheck.out.yaml
@@ -185,6 +185,41 @@ backendTrafficPolicies:
         status: "True"
         type: Accepted
       controllerName: gateway.envoyproxy.io/gatewayclass-controller
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+  kind: BackendTrafficPolicy
+  metadata:
+    creationTimestamp: null
+    name: policy-for-route-5
+    namespace: default
+  spec:
+    healthCheck:
+      active:
+        healthyThreshold: 2
+        initialJitter: 15s
+        interval: 30s
+        tcp: {}
+        timeout: 10s
+        type: TCP
+        unhealthyThreshold: 2
+    targetRef:
+      group: gateway.networking.k8s.io
+      kind: HTTPRoute
+      name: httproute-5
+  status:
+    ancestors:
+    - ancestorRef:
+        group: gateway.networking.k8s.io
+        kind: Gateway
+        name: gateway-2
+        namespace: envoy-gateway
+        sectionName: http
+      conditions:
+      - lastTransitionTime: null
+        message: Policy has been accepted.
+        reason: Accepted
+        status: "True"
+        type: Accepted
+      controllerName: gateway.envoyproxy.io/gatewayclass-controller
 - apiVersion: gateway.envoyproxy.io/v1alpha1
   kind: BackendTrafficPolicy
   metadata:
@@ -366,7 +401,7 @@ gateways:
       protocol: HTTP
   status:
     listeners:
-    - attachedRoutes: 4
+    - attachedRoutes: 5
       conditions:
       - lastTransitionTime: null
         message: Sending translated listener configuration to the data plane
@@ -642,6 +677,44 @@ httpRoutes:
         name: gateway-2
         namespace: envoy-gateway
         sectionName: http
+- apiVersion: gateway.networking.k8s.io/v1
+  kind: HTTPRoute
+  metadata:
+    creationTimestamp: null
+    name: httproute-5
+    namespace: default
+  spec:
+    hostnames:
+    - gateway.envoyproxy.io
+    parentRefs:
+    - name: gateway-2
+      namespace: envoy-gateway
+      sectionName: http
+    rules:
+    - backendRefs:
+      - name: service-4
+        port: 8080
+      matches:
+      - path:
+          value: /v5
+  status:
+    parents:
+    - conditions:
+      - lastTransitionTime: null
+        message: Route is accepted
+        reason: Accepted
+        status: "True"
+        type: Accepted
+      - lastTransitionTime: null
+        message: Resolved all the Object references for the Route
+        reason: ResolvedRefs
+        status: "True"
+        type: ResolvedRefs
+      controllerName: gateway.envoyproxy.io/gatewayclass-controller
+      parentRef:
+        name: gateway-2
+        namespace: envoy-gateway
+        sectionName: http
 infraIR:
   envoy-gateway/gateway-1:
     proxy:
@@ -985,6 +1058,44 @@ xdsIR:
               interval: 5s
               timeout: 1s
               unhealthyThreshold: 3
+      - destination:
+          metadata:
+            kind: HTTPRoute
+            name: httproute-5
+            namespace: default
+          name: httproute/default/httproute-5/rule/0
+          settings:
+          - addressType: IP
+            endpoints:
+            - host: 7.7.7.7
+              port: 8080
+            metadata:
+              name: service-4
+              namespace: default
+              sectionName: "8080"
+            name: httproute/default/httproute-5/rule/0/backend/0
+            protocol: HTTP
+            weight: 1
+        hostname: gateway.envoyproxy.io
+        isHTTP2: false
+        metadata:
+          kind: HTTPRoute
+          name: httproute-5
+          namespace: default
+        name: httproute/default/httproute-5/rule/0/match/0/gateway_envoyproxy_io
+        pathMatch:
+          distinct: false
+          name: ""
+          prefix: /v5
+        traffic:
+          healthCheck:
+            active:
+              healthyThreshold: 2
+              initialJitter: 15s
+              interval: 30s
+              tcp: {}
+              timeout: 10s
+              unhealthyThreshold: 2
       - destination:
           metadata:
             kind: HTTPRoute
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-retries.in.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-retries.in.yaml
index 70a004ae6..5ecc975e1 100644
--- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-retries.in.yaml
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-retries.in.yaml
@@ -115,6 +115,7 @@ backendTrafficPolicies:
       retryOn:
         triggers:
         - cancelled
+        - reset-before-request
       perRetry:
         timeout: 250ms
         backoff:
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-retries.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-retries.out.yaml
index 49f939ba5..a3f97660f 100644
--- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-retries.out.yaml
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-retries.out.yaml
@@ -118,6 +118,7 @@ backendTrafficPolicies:
       retryOn:
         triggers:
         - cancelled
+        - reset-before-request
     targetRef:
       group: gateway.networking.k8s.io
       kind: Gateway
@@ -461,6 +462,7 @@ xdsIR:
             retryOn:
               triggers:
               - cancelled
+              - reset-before-request
     readyListener:
       address: 0.0.0.0
       ipFamily: IPv4
diff --git a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-regex-match-replace-http.out.yaml b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-regex-match-replace-http.out.yaml
index 70d124e47..ca3284339 100644
--- a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-regex-match-replace-http.out.yaml
+++ b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-regex-match-replace-http.out.yaml
@@ -370,6 +370,12 @@ xdsIR:
         urlRewrite:
           host:
             name: rewrite.com
+          path:
+            fullReplace: null
+            prefixMatchReplace: null
+            regexMatchReplace:
+              pattern: .*
+              substitution: foo
       - destination:
           metadata:
             kind: HTTPRoute
diff --git a/internal/gatewayapi/testdata/httproute-with-urlrewrite-hostname-filter.out.yaml b/internal/gatewayapi/testdata/httproute-with-urlrewrite-hostname-filter.out.yaml
index 15a17b99c..d28bfb180 100644
--- a/internal/gatewayapi/testdata/httproute-with-urlrewrite-hostname-filter.out.yaml
+++ b/internal/gatewayapi/testdata/httproute-with-urlrewrite-hostname-filter.out.yaml
@@ -333,6 +333,8 @@ xdsIR:
           name: ""
           prefix: /header-host-and-path
         urlRewrite:
+          host:
+            header: my-host
           path:
             fullReplace: null
             prefixMatchReplace: /rewrite
diff --git a/internal/ir/xds_test.go b/internal/ir/xds_test.go
index b97dd0d94..9ad48cbcf 100644
--- a/internal/ir/xds_test.go
+++ b/internal/ir/xds_test.go
@@ -17,6 +17,7 @@ import (
 	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/utils/ptr"
+	gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
 
 	egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1"
 )
@@ -1580,6 +1581,27 @@ func TestValidateHealthCheck(t *testing.T) {
 			},
 			want: ErrHealthCheckIntervalInvalid,
 		},
+		{
+			name: "invalid initial jitter",
+			input: HealthCheck{
+				&ActiveHealthCheck{
+					Timeout:            &metav1.Duration{Duration: time.Second},
+					Interval:           &metav1.Duration{Duration: time.Second},
+					InitialJitter:      ptr.To(gwapiv1.Duration("-1s")),
+					UnhealthyThreshold: ptr.To[uint32](3),
+					HealthyThreshold:   ptr.To[uint32](3),
+					HTTP: &HTTPHealthChecker{
+						Host:             "*",
+						Path:             "/healthz",
+						Method:           ptr.To(http.MethodGet),
+						ExpectedStatuses: []HTTPStatus{200, 400},
+					},
+				},
+				&OutlierDetection{},
+				ptr.To[uint32](10),
+			},
+			want: ErrHealthCheckInitialJitterInvalid,
+		},
 		{
 			name: "invalid unhealthy threshold",
 			input: HealthCheck{
diff --git a/internal/xds/translator/testdata/in/xds-ir/api-key-auth.yaml b/internal/xds/translator/testdata/in/xds-ir/api-key-auth.yaml
index da6c833ea..30c51a35d 100644
--- a/internal/xds/translator/testdata/in/xds-ir/api-key-auth.yaml
+++ b/internal/xds/translator/testdata/in/xds-ir/api-key-auth.yaml
@@ -36,6 +36,8 @@ http:
               client-1: "a2V5MQ=="
             extractFrom:
               - headers: ["X-API-KEY", "X-API-KEY-2"]
+            forwardClientIDHeader: "X-API-KEY-CLIENT-ID"
+            sanitize: true
       - name: httproute/default/httproute-1/rule/1/match/0/www_foo_com
         backendWeights:
         hostname: www.foo.com
diff --git a/internal/xds/translator/testdata/in/xds-ir/retry.yaml b/internal/xds/translator/testdata/in/xds-ir/retry.yaml
index 7b15593ef..5ca639cb6 100644
--- a/internal/xds/translator/testdata/in/xds-ir/retry.yaml
+++ b/internal/xds/translator/testdata/in/xds-ir/retry.yaml
@@ -62,6 +62,7 @@ http:
         retryOn:
           triggers:
           - reset
+          - reset-before-request
           - connect-failure
     destination:
       name: "first-route-dest"
diff --git a/internal/xds/translator/testdata/out/xds-ir/api-key-auth.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/api-key-auth.listeners.yaml
index 7e4465d14..2ed967858 100644
--- a/internal/xds/translator/testdata/out/xds-ir/api-key-auth.listeners.yaml
+++ b/internal/xds/translator/testdata/out/xds-ir/api-key-auth.listeners.yaml
@@ -21,6 +21,9 @@
             credentials:
             - client: client-1
               key: key1
+            forwarding:
+              header: X-API-KEY-CLIENT-ID
+              hideCredentials: true
             keySources:
             - header: X-API-KEY
             - header: X-API-KEY-2
diff --git a/internal/xds/translator/testdata/out/xds-ir/api-key-auth.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/api-key-auth.routes.yaml
index 6d1a7561c..46a516977 100644
--- a/internal/xds/translator/testdata/out/xds-ir/api-key-auth.routes.yaml
+++ b/internal/xds/translator/testdata/out/xds-ir/api-key-auth.routes.yaml
@@ -18,6 +18,9 @@
           credentials:
           - client: client-1
             key: key1
+          forwarding:
+            header: X-API-KEY-CLIENT-ID
+            hideCredentials: true
           keySources:
           - header: X-API-KEY
           - header: X-API-KEY-2
diff --git a/internal/xds/translator/testdata/out/xds-ir/retry-reset-before-request.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/retry-reset-before-request.clusters.yaml
new file mode 100644
index 000000000..054f90bb7
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/retry-reset-before-request.clusters.yaml
@@ -0,0 +1,24 @@
+- circuitBreakers:
+    thresholds:
+    - maxRetries: 1024
+  commonLbConfig: {}
+  connectTimeout: 10s
+  dnsLookupFamily: V4_PREFERRED
+  edsClusterConfig:
+    edsConfig:
+      ads: {}
+      resourceApiVersion: V3
+    serviceName: first-route-dest
+  ignoreHealthOnHostRemoval: true
+  lbPolicy: LEAST_REQUEST
+  loadBalancingPolicy:
+    policies:
+    - typedExtensionConfig:
+        name: envoy.load_balancing_policies.least_request
+        typedConfig:
+          '@type': type.googleapis.com/envoy.extensions.load_balancing_policies.least_request.v3.LeastRequest
+          localityLbConfig:
+            localityWeightedLbConfig: {}
+  name: first-route-dest
+  perConnectionBufferLimitBytes: 32768
+  type: EDS
diff --git a/internal/xds/translator/testdata/out/xds-ir/retry-reset-before-request.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/retry-reset-before-request.endpoints.yaml
new file mode 100644
index 000000000..3b3f2d090
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/retry-reset-before-request.endpoints.yaml
@@ -0,0 +1,12 @@
+- clusterName: first-route-dest
+  endpoints:
+  - lbEndpoints:
+    - endpoint:
+        address:
+          socketAddress:
+            address: 1.2.3.4
+            portValue: 50000
+      loadBalancingWeight: 1
+    loadBalancingWeight: 1
+    locality:
+      region: first-route-dest/backend/0
diff --git a/internal/xds/translator/testdata/out/xds-ir/retry-reset-before-request.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/retry-reset-before-request.listeners.yaml
new file mode 100644
index 000000000..5dd5e46e3
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/retry-reset-before-request.listeners.yaml
@@ -0,0 +1,35 @@
+- address:
+    socketAddress:
+      address: '::'
+      portValue: 10080
+  defaultFilterChain:
+    filters:
+    - name: envoy.filters.network.http_connection_manager
+      typedConfig:
+        '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
+        commonHttpProtocolOptions:
+          headersWithUnderscoresAction: REJECT_REQUEST
+        http2ProtocolOptions:
+          initialConnectionWindowSize: 1048576
+          initialStreamWindowSize: 65536
+          maxConcurrentStreams: 100
+        httpFilters:
+        - name: envoy.filters.http.router
+          typedConfig:
+            '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
+            suppressEnvoyHeaders: true
+        mergeSlashes: true
+        normalizePath: true
+        pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT
+        rds:
+          configSource:
+            ads: {}
+            resourceApiVersion: V3
+          routeConfigName: first-listener
+        serverHeaderTransformation: PASS_THROUGH
+        statPrefix: http-10080
+        useRemoteAddress: true
+    name: first-listener
+  maxConnectionsToAcceptPerSocketEvent: 1
+  name: first-listener
+  perConnectionBufferLimitBytes: 32768
diff --git a/internal/xds/translator/testdata/out/xds-ir/retry-reset-before-request.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/retry-reset-before-request.routes.yaml
new file mode 100644
index 000000000..1eabe7a1b
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/retry-reset-before-request.routes.yaml
@@ -0,0 +1,45 @@
+- ignorePortInHostMatching: true
+  name: first-listener
+  virtualHosts:
+  - domains:
+    - '*'
+    name: first-listener/*
+    routes:
+    - match:
+        prefix: /
+      name: reset-before-request-route
+      route:
+        cluster: first-route-dest
+        retryPolicy:
+          hostSelectionRetryMaxAttempts: "5"
+          numRetries: 3
+          perTryTimeout: 0.250s
+          retryBackOff:
+            baseInterval: 0.100s
+            maxInterval: 10s
+          retryHostPredicate:
+          - name: envoy.retry_host_predicates.previous_hosts
+            typedConfig:
+              '@type': type.googleapis.com/envoy.extensions.retry.host.previous_hosts.v3.PreviousHostsPredicate
+          retryOn: reset-before-request
+        upgradeConfigs:
+        - upgradeType: websocket
+  - domains:
+    - foo
+    name: first-listener/foo
+    routes:
+    - match:
+        prefix: /
+      name: mixed-triggers-route
+      route:
+        cluster: first-route-dest
+        retryPolicy:
+          hostSelectionRetryMaxAttempts: "5"
+          numRetries: 5
+          retryHostPredicate:
+          - name: envoy.retry_host_predicates.previous_hosts
+            typedConfig:
+              '@type': type.googleapis.com/envoy.extensions.retry.host.previous_hosts.v3.PreviousHostsPredicate
+          retryOn: reset,reset-before-request,connect-failure
+        upgradeConfigs:
+        - upgradeType: websocket
diff --git a/internal/xds/translator/testdata/out/xds-ir/retry.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/retry.routes.yaml
index 456d6c203..d07883d92 100644
--- a/internal/xds/translator/testdata/out/xds-ir/retry.routes.yaml
+++ b/internal/xds/translator/testdata/out/xds-ir/retry.routes.yaml
@@ -57,7 +57,7 @@
           - name: envoy.retry_host_predicates.previous_hosts
             typedConfig:
               '@type': type.googleapis.com/envoy.extensions.retry.host.previous_hosts.v3.PreviousHostsPredicate
-          retryOn: reset,connect-failure
+          retryOn: reset,reset-before-request,connect-failure
           retryPriority:
             name: envoy.retry_priorities.previous_priorities
             typedConfig:
diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md
index 85a5bddb6..79eab9ba0 100644
--- a/site/content/en/latest/api/extension_types.md
+++ b/site/content/en/latest/api/extension_types.md
@@ -113,6 +113,8 @@ _Appears in:_
 | ---   | ---  | ---      | ---     | ---         |
 | `credentialRefs` | _[SecretObjectReference](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1.SecretObjectReference) array_ |  true  |  | CredentialRefs is the Kubernetes secret which contains the API keys.<br />This is an Opaque secret.<br />Each API key is stored in the key representing the client id.<br />If the secrets have a key for a duplicated client, the first one will be used. |
 | `extractFrom` | _[ExtractFrom](#extractfrom) array_ |  true  |  | ExtractFrom is where to fetch the key from the coming request.<br />The value from the first source that has a key will be used. |
+| `forwardClientIDHeader` | _string_ |  false  |  | ForwardClientIDHeader is the name of the header to forward the client identity to the backend<br />service. The header will be added to the request with the client id as the value. |
+| `sanitize` | _boolean_ |  false  |  | Sanitize indicates whether to remove the API key from the request before forwarding it to the backend service. |
 
 
 #### ActiveHealthCheck
@@ -129,6 +131,7 @@ _Appears in:_
 | ---   | ---  | ---      | ---     | ---         |
 | `timeout` | _[Duration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#duration-v1-meta)_ |  false  | 1s | Timeout defines the time to wait for a health check response. |
 | `interval` | _[Duration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#duration-v1-meta)_ |  false  | 3s | Interval defines the time between active health checks. |
+| `initialJitter` | _[Duration](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.Duration)_ |  false  |  | InitialJitter defines the maximum time Envoy will wait before the first health check.<br />Envoy will randomly select a value between 0 and the initial jitter value. |
 | `unhealthyThreshold` | _integer_ |  false  | 3 | UnhealthyThreshold defines the number of unhealthy health checks required before a backend host is marked unhealthy. |
 | `healthyThreshold` | _integer_ |  false  | 1 | HealthyThreshold defines the number of healthy health checks required before a backend host is marked healthy. |
 | `type` | _[ActiveHealthCheckerType](#activehealthcheckertype)_ |  true  |  | Type defines the type of health checker. |
@@ -4974,6 +4977,7 @@ _Appears in:_
 | `5xx` | The upstream server responds with any 5xx response code, or does not respond at all (disconnect/reset/read timeout).<br />Includes connect-failure and refused-stream.<br /> | 
 | `gateway-error` | The response is a gateway error (502,503 or 504).<br /> | 
 | `reset` | The upstream server does not respond at all (disconnect/reset/read timeout.)<br /> | 
+| `reset-before-request` | Like reset, but only retry if the request headers have not been sent to the upstream server.<br /> | 
 | `connect-failure` | Connection failure to the upstream server (connect timeout, etc.). (Included in *5xx*)<br /> | 
 | `retriable-4xx` | The upstream server responds with a retriable 4xx response code.<br />Currently, the only response code in this category is 409.<br /> | 
 | `refused-stream` | The upstream server resets the stream with a REFUSED_STREAM error code.<br /> | 
diff --git a/test/cel-validation/envoyextensionpolicy_test.go b/test/cel-validation/envoyextensionpolicy_test.go
index 977dc3684..7202634be 100644
--- a/test/cel-validation/envoyextensionpolicy_test.go
+++ b/test/cel-validation/envoyextensionpolicy_test.go
@@ -447,6 +447,112 @@ func TestEnvoyExtensionPolicyTarget(t *testing.T) {
 				"spec.extProc[0].processingMode.request.body: Unsupported value: \"not-a-body-mode\": supported values: \"Streamed\", \"Buffered\", \"BufferedPartial\"",
 			},
 		},
+		{
+			desc: "valid ExtProc with request attributes and failOpen true",
+			mutate: func(sp *egv1a1.EnvoyExtensionPolicy) {
+				sp.Spec = egv1a1.EnvoyExtensionPolicySpec{
+					ExtProc: []egv1a1.ExtProc{
+						{
+							BackendCluster: egv1a1.BackendCluster{
+								BackendRefs: []egv1a1.BackendRef{
+									{
+										BackendObjectReference: gwapiv1.BackendObjectReference{
+											Name: "grpc-proc-service",
+											Port: ptr.To(gwapiv1.PortNumber(80)),
+										},
+									},
+								},
+							},
+							ProcessingMode: &egv1a1.ExtProcProcessingMode{
+								Request: &egv1a1.ProcessingModeOptions{
+									Attributes: []string{"request.headers"},
+								},
+							},
+							FailOpen: ptr.To(true),
+						},
+					},
+					PolicyTargetReferences: egv1a1.PolicyTargetReferences{
+						TargetRef: &gwapiv1a2.LocalPolicyTargetReferenceWithSectionName{
+							LocalPolicyTargetReference: gwapiv1a2.LocalPolicyTargetReference{
+								Group: "gateway.networking.k8s.io",
+								Kind:  "Gateway",
+								Name:  "eg",
+							},
+						},
+					},
+				}
+			},
+			wantErrors: []string{},
+		},
+		{
+			desc: "valid ExtProc with response attributes and failOpen true",
+			mutate: func(sp *egv1a1.EnvoyExtensionPolicy) {
+				sp.Spec = egv1a1.EnvoyExtensionPolicySpec{
+					ExtProc: []egv1a1.ExtProc{
+						{
+							BackendCluster: egv1a1.BackendCluster{
+								BackendRefs: []egv1a1.BackendRef{
+									{
+										BackendObjectReference: gwapiv1.BackendObjectReference{
+											Name: "grpc-proc-service",
+											Port: ptr.To(gwapiv1.PortNumber(80)),
+										},
+									},
+								},
+							},
+							ProcessingMode: &egv1a1.ExtProcProcessingMode{
+								Response: &egv1a1.ProcessingModeOptions{
+									Attributes: []string{"response.headers"},
+								},
+							},
+							FailOpen: ptr.To(true),
+						},
+					},
+					PolicyTargetReferences: egv1a1.PolicyTargetReferences{
+						TargetRef: &gwapiv1a2.LocalPolicyTargetReferenceWithSectionName{
+							LocalPolicyTargetReference: gwapiv1a2.LocalPolicyTargetReference{
+								Group: "gateway.networking.k8s.io",
+								Kind:  "Gateway",
+								Name:  "eg",
+							},
+						},
+					},
+				}
+			},
+			wantErrors: []string{},
+		},
+		{
+			desc: "valid ExtProc with failOpen true",
+			mutate: func(sp *egv1a1.EnvoyExtensionPolicy) {
+				sp.Spec = egv1a1.EnvoyExtensionPolicySpec{
+					ExtProc: []egv1a1.ExtProc{
+						{
+							BackendCluster: egv1a1.BackendCluster{
+								BackendRefs: []egv1a1.BackendRef{
+									{
+										BackendObjectReference: gwapiv1.BackendObjectReference{
+											Name: "grpc-proc-service",
+											Port: ptr.To(gwapiv1.PortNumber(80)),
+										},
+									},
+								},
+							},
+							FailOpen: ptr.To(true),
+						},
+					},
+					PolicyTargetReferences: egv1a1.PolicyTargetReferences{
+						TargetRef: &gwapiv1a2.LocalPolicyTargetReferenceWithSectionName{
+							LocalPolicyTargetReference: gwapiv1a2.LocalPolicyTargetReference{
+								Group: "gateway.networking.k8s.io",
+								Kind:  "Gateway",
+								Name:  "eg",
+							},
+						},
+					},
+				}
+			},
+			wantErrors: []string{},
+		},
 		{
 			desc: "valid ExtProc without FullDuplexStreamed body and failOpen true",
 			mutate: func(sp *egv1a1.EnvoyExtensionPolicy) {
diff --git a/test/e2e/testdata/api-key-auth.yaml b/test/e2e/testdata/api-key-auth.yaml
index 34b75980d..022618b6e 100644
--- a/test/e2e/testdata/api-key-auth.yaml
+++ b/test/e2e/testdata/api-key-auth.yaml
@@ -86,6 +86,8 @@ spec:
     credentialRefs:
       - name: "api-key-auth-users-secret-1"
       - name: "api-key-auth-users-secret-2"
+    forwardClientIDHeader: "X-API-KEY-CLIENT-ID"
+    sanitize: true
 ---
 apiVersion: gateway.envoyproxy.io/v1alpha1
 kind: SecurityPolicy
diff --git a/test/e2e/tests/api_key_auth.go b/test/e2e/tests/api_key_auth.go
index 2f9566ed6..59f110828 100644
--- a/test/e2e/tests/api_key_auth.go
+++ b/test/e2e/tests/api_key_auth.go
@@ -51,6 +51,15 @@ var APIKeyAuthTest = suite.ConformanceTest{
 						"X-API-KEY": "key1",
 					},
 				},
+				ExpectedRequest: &http.ExpectedRequest{
+					Request: http.Request{
+						Path: "/api-key-auth-header",
+						Headers: map[string]string{
+							"X-API-KEY-CLIENT-ID": "client1",
+						},
+					},
+					AbsentHeaders: []string{"X-API-KEY"},
+				},
 				Response: http.Response{
 					StatusCode: 200,
 				},
diff --git a/test/helm/gateway-crds-helm/all.out.yaml b/test/helm/gateway-crds-helm/all.out.yaml
index cf3656837..93456fe8d 100644
--- a/test/helm/gateway-crds-helm/all.out.yaml
+++ b/test/helm/gateway-crds-helm/all.out.yaml
@@ -17990,6 +17990,13 @@ spec:
                         required:
                         - path
                         type: object
+                      initialJitter:
+                        description: |-
+                          InitialJitter defines the maximum time Envoy will wait before the first health check.
+                          Envoy will randomly select a value between 0 and the initial jitter value.
+                        format: duration
+                        pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+                        type: string
                       interval:
                         default: 3s
                         description: Interval defines the time between active health
@@ -19293,6 +19300,7 @@ spec:
                           - 5xx
                           - gateway-error
                           - reset
+                          - reset-before-request
                           - connect-failure
                           - retriable-4xx
                           - refused-stream
@@ -21829,6 +21837,13 @@ spec:
                                   required:
                                   - path
                                   type: object
+                                initialJitter:
+                                  description: |-
+                                    InitialJitter defines the maximum time Envoy will wait before the first health check.
+                                    Envoy will randomly select a value between 0 and the initial jitter value.
+                                  format: duration
+                                  pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+                                  type: string
                                 interval:
                                   default: 3s
                                   description: Interval defines the time between active
@@ -22291,6 +22306,7 @@ spec:
                                     - 5xx
                                     - gateway-error
                                     - reset
+                                    - reset-before-request
                                     - connect-failure
                                     - retriable-4xx
                                     - refused-stream
@@ -22487,10 +22503,11 @@ spec:
                       ''gateway.envoyproxy.io'')) : true'
                   - message: If FullDuplexStreamed body processing mode is used, FailOpen
                       must be false.
-                    rule: '!(has(self.failOpen) && self.failOpen == true && ((has(self.processingMode.request.body)
+                    rule: '!(has(self.failOpen) && self.failOpen == true && has(self.processingMode)
+                      && ((has(self.processingMode.request) && has(self.processingMode.request.body)
                       && self.processingMode.request.body == ''FullDuplexStreamed'')
-                      || (has(self.processingMode.response.body) && self.processingMode.response.body
-                      == ''FullDuplexStreamed'')))'
+                      || (has(self.processingMode.response) && has(self.processingMode.response.body)
+                      && self.processingMode.response.body == ''FullDuplexStreamed'')))'
                 maxItems: 16
                 type: array
               lua:
@@ -34835,6 +34852,13 @@ spec:
                                                     required:
                                                     - path
                                                     type: object
+                                                  initialJitter:
+                                                    description: |-
+                                                      InitialJitter defines the maximum time Envoy will wait before the first health check.
+                                                      Envoy will randomly select a value between 0 and the initial jitter value.
+                                                    format: duration
+                                                    pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+                                                    type: string
                                                   interval:
                                                     default: 3s
                                                     description: Interval defines
@@ -35343,6 +35367,7 @@ spec:
                                                       - 5xx
                                                       - gateway-error
                                                       - reset
+                                                      - reset-before-request
                                                       - connect-failure
                                                       - retriable-4xx
                                                       - refused-stream
@@ -35907,6 +35932,13 @@ spec:
                                                     required:
                                                     - path
                                                     type: object
+                                                  initialJitter:
+                                                    description: |-
+                                                      InitialJitter defines the maximum time Envoy will wait before the first health check.
+                                                      Envoy will randomly select a value between 0 and the initial jitter value.
+                                                    format: duration
+                                                    pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+                                                    type: string
                                                   interval:
                                                     default: 3s
                                                     description: Interval defines
@@ -36415,6 +36447,7 @@ spec:
                                                       - 5xx
                                                       - gateway-error
                                                       - reset
+                                                      - reset-before-request
                                                       - connect-failure
                                                       - retriable-4xx
                                                       - refused-stream
@@ -37082,6 +37115,13 @@ spec:
                                               required:
                                               - path
                                               type: object
+                                            initialJitter:
+                                              description: |-
+                                                InitialJitter defines the maximum time Envoy will wait before the first health check.
+                                                Envoy will randomly select a value between 0 and the initial jitter value.
+                                              format: duration
+                                              pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+                                              type: string
                                             interval:
                                               default: 3s
                                               description: Interval defines the time
@@ -37566,6 +37606,7 @@ spec:
                                                 - 5xx
                                                 - gateway-error
                                                 - reset
+                                                - reset-before-request
                                                 - connect-failure
                                                 - retriable-4xx
                                                 - refused-stream
@@ -38159,6 +38200,13 @@ spec:
                                         required:
                                         - path
                                         type: object
+                                      initialJitter:
+                                        description: |-
+                                          InitialJitter defines the maximum time Envoy will wait before the first health check.
+                                          Envoy will randomly select a value between 0 and the initial jitter value.
+                                        format: duration
+                                        pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+                                        type: string
                                       interval:
                                         default: 3s
                                         description: Interval defines the time between
@@ -38639,6 +38687,7 @@ spec:
                                           - 5xx
                                           - gateway-error
                                           - reset
+                                          - reset-before-request
                                           - connect-failure
                                           - retriable-4xx
                                           - refused-stream
@@ -39264,6 +39313,15 @@ spec:
                           type: array
                       type: object
                     type: array
+                  forwardClientIDHeader:
+                    description: |-
+                      ForwardClientIDHeader is the name of the header to forward the client identity to the backend
+                      service. The header will be added to the request with the client id as the value.
+                    type: string
+                  sanitize:
+                    description: Sanitize indicates whether to remove the API key
+                      from the request before forwarding it to the backend service.
+                    type: boolean
                 required:
                 - credentialRefs
                 - extractFrom
@@ -40056,6 +40114,13 @@ spec:
                                     required:
                                     - path
                                     type: object
+                                  initialJitter:
+                                    description: |-
+                                      InitialJitter defines the maximum time Envoy will wait before the first health check.
+                                      Envoy will randomly select a value between 0 and the initial jitter value.
+                                    format: duration
+                                    pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+                                    type: string
                                   interval:
                                     default: 3s
                                     description: Interval defines the time between
@@ -40527,6 +40592,7 @@ spec:
                                       - 5xx
                                       - gateway-error
                                       - reset
+                                      - reset-before-request
                                       - connect-failure
                                       - retriable-4xx
                                       - refused-stream
@@ -41026,6 +41092,13 @@ spec:
                                     required:
                                     - path
                                     type: object
+                                  initialJitter:
+                                    description: |-
+                                      InitialJitter defines the maximum time Envoy will wait before the first health check.
+                                      Envoy will randomly select a value between 0 and the initial jitter value.
+                                    format: duration
+                                    pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+                                    type: string
                                   interval:
                                     default: 3s
                                     description: Interval defines the time between
@@ -41497,6 +41570,7 @@ spec:
                                       - 5xx
                                       - gateway-error
                                       - reset
+                                      - reset-before-request
                                       - connect-failure
                                       - retriable-4xx
                                       - refused-stream
@@ -42192,6 +42266,13 @@ spec:
                                           required:
                                           - path
                                           type: object
+                                        initialJitter:
+                                          description: |-
+                                            InitialJitter defines the maximum time Envoy will wait before the first health check.
+                                            Envoy will randomly select a value between 0 and the initial jitter value.
+                                          format: duration
+                                          pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+                                          type: string
                                         interval:
                                           default: 3s
                                           description: Interval defines the time between
@@ -42674,6 +42755,7 @@ spec:
                                             - 5xx
                                             - gateway-error
                                             - reset
+                                            - reset-before-request
                                             - connect-failure
                                             - retriable-4xx
                                             - refused-stream
@@ -43409,6 +43491,13 @@ spec:
                                     required:
                                     - path
                                     type: object
+                                  initialJitter:
+                                    description: |-
+                                      InitialJitter defines the maximum time Envoy will wait before the first health check.
+                                      Envoy will randomly select a value between 0 and the initial jitter value.
+                                    format: duration
+                                    pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+                                    type: string
                                   interval:
                                     default: 3s
                                     description: Interval defines the time between
@@ -43880,6 +43969,7 @@ spec:
                                       - 5xx
                                       - gateway-error
                                       - reset
+                                      - reset-before-request
                                       - connect-failure
                                       - retriable-4xx
                                       - refused-stream
diff --git a/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml b/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml
index 6dff81bf3..7b4ecb3a4 100644
--- a/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml
+++ b/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml
@@ -678,6 +678,13 @@ spec:
                         required:
                         - path
                         type: object
+                      initialJitter:
+                        description: |-
+                          InitialJitter defines the maximum time Envoy will wait before the first health check.
+                          Envoy will randomly select a value between 0 and the initial jitter value.
+                        format: duration
+                        pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+                        type: string
                       interval:
                         default: 3s
                         description: Interval defines the time between active health
@@ -1981,6 +1988,7 @@ spec:
                           - 5xx
                           - gateway-error
                           - reset
+                          - reset-before-request
                           - connect-failure
                           - retriable-4xx
                           - refused-stream
@@ -4517,6 +4525,13 @@ spec:
                                   required:
                                   - path
                                   type: object
+                                initialJitter:
+                                  description: |-
+                                    InitialJitter defines the maximum time Envoy will wait before the first health check.
+                                    Envoy will randomly select a value between 0 and the initial jitter value.
+                                  format: duration
+                                  pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+                                  type: string
                                 interval:
                                   default: 3s
                                   description: Interval defines the time between active
@@ -4979,6 +4994,7 @@ spec:
                                     - 5xx
                                     - gateway-error
                                     - reset
+                                    - reset-before-request
                                     - connect-failure
                                     - retriable-4xx
                                     - refused-stream
@@ -5175,10 +5191,11 @@ spec:
                       ''gateway.envoyproxy.io'')) : true'
                   - message: If FullDuplexStreamed body processing mode is used, FailOpen
                       must be false.
-                    rule: '!(has(self.failOpen) && self.failOpen == true && ((has(self.processingMode.request.body)
+                    rule: '!(has(self.failOpen) && self.failOpen == true && has(self.processingMode)
+                      && ((has(self.processingMode.request) && has(self.processingMode.request.body)
                       && self.processingMode.request.body == ''FullDuplexStreamed'')
-                      || (has(self.processingMode.response.body) && self.processingMode.response.body
-                      == ''FullDuplexStreamed'')))'
+                      || (has(self.processingMode.response) && has(self.processingMode.response.body)
+                      && self.processingMode.response.body == ''FullDuplexStreamed'')))'
                 maxItems: 16
                 type: array
               lua:
@@ -17523,6 +17540,13 @@ spec:
                                                     required:
                                                     - path
                                                     type: object
+                                                  initialJitter:
+                                                    description: |-
+                                                      InitialJitter defines the maximum time Envoy will wait before the first health check.
+                                                      Envoy will randomly select a value between 0 and the initial jitter value.
+                                                    format: duration
+                                                    pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+                                                    type: string
                                                   interval:
                                                     default: 3s
                                                     description: Interval defines
@@ -18031,6 +18055,7 @@ spec:
                                                       - 5xx
                                                       - gateway-error
                                                       - reset
+                                                      - reset-before-request
                                                       - connect-failure
                                                       - retriable-4xx
                                                       - refused-stream
@@ -18595,6 +18620,13 @@ spec:
                                                     required:
                                                     - path
                                                     type: object
+                                                  initialJitter:
+                                                    description: |-
+                                                      InitialJitter defines the maximum time Envoy will wait before the first health check.
+                                                      Envoy will randomly select a value between 0 and the initial jitter value.
+                                                    format: duration
+                                                    pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+                                                    type: string
                                                   interval:
                                                     default: 3s
                                                     description: Interval defines
@@ -19103,6 +19135,7 @@ spec:
                                                       - 5xx
                                                       - gateway-error
                                                       - reset
+                                                      - reset-before-request
                                                       - connect-failure
                                                       - retriable-4xx
                                                       - refused-stream
@@ -19770,6 +19803,13 @@ spec:
                                               required:
                                               - path
                                               type: object
+                                            initialJitter:
+                                              description: |-
+                                                InitialJitter defines the maximum time Envoy will wait before the first health check.
+                                                Envoy will randomly select a value between 0 and the initial jitter value.
+                                              format: duration
+                                              pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+                                              type: string
                                             interval:
                                               default: 3s
                                               description: Interval defines the time
@@ -20254,6 +20294,7 @@ spec:
                                                 - 5xx
                                                 - gateway-error
                                                 - reset
+                                                - reset-before-request
                                                 - connect-failure
                                                 - retriable-4xx
                                                 - refused-stream
@@ -20847,6 +20888,13 @@ spec:
                                         required:
                                         - path
                                         type: object
+                                      initialJitter:
+                                        description: |-
+                                          InitialJitter defines the maximum time Envoy will wait before the first health check.
+                                          Envoy will randomly select a value between 0 and the initial jitter value.
+                                        format: duration
+                                        pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+                                        type: string
                                       interval:
                                         default: 3s
                                         description: Interval defines the time between
@@ -21327,6 +21375,7 @@ spec:
                                           - 5xx
                                           - gateway-error
                                           - reset
+                                          - reset-before-request
                                           - connect-failure
                                           - retriable-4xx
                                           - refused-stream
@@ -21952,6 +22001,15 @@ spec:
                           type: array
                       type: object
                     type: array
+                  forwardClientIDHeader:
+                    description: |-
+                      ForwardClientIDHeader is the name of the header to forward the client identity to the backend
+                      service. The header will be added to the request with the client id as the value.
+                    type: string
+                  sanitize:
+                    description: Sanitize indicates whether to remove the API key
+                      from the request before forwarding it to the backend service.
+                    type: boolean
                 required:
                 - credentialRefs
                 - extractFrom
@@ -22744,6 +22802,13 @@ spec:
                                     required:
                                     - path
                                     type: object
+                                  initialJitter:
+                                    description: |-
+                                      InitialJitter defines the maximum time Envoy will wait before the first health check.
+                                      Envoy will randomly select a value between 0 and the initial jitter value.
+                                    format: duration
+                                    pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+                                    type: string
                                   interval:
                                     default: 3s
                                     description: Interval defines the time between
@@ -23215,6 +23280,7 @@ spec:
                                       - 5xx
                                       - gateway-error
                                       - reset
+                                      - reset-before-request
                                       - connect-failure
                                       - retriable-4xx
                                       - refused-stream
@@ -23714,6 +23780,13 @@ spec:
                                     required:
                                     - path
                                     type: object
+                                  initialJitter:
+                                    description: |-
+                                      InitialJitter defines the maximum time Envoy will wait before the first health check.
+                                      Envoy will randomly select a value between 0 and the initial jitter value.
+                                    format: duration
+                                    pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+                                    type: string
                                   interval:
                                     default: 3s
                                     description: Interval defines the time between
@@ -24185,6 +24258,7 @@ spec:
                                       - 5xx
                                       - gateway-error
                                       - reset
+                                      - reset-before-request
                                       - connect-failure
                                       - retriable-4xx
                                       - refused-stream
@@ -24880,6 +24954,13 @@ spec:
                                           required:
                                           - path
                                           type: object
+                                        initialJitter:
+                                          description: |-
+                                            InitialJitter defines the maximum time Envoy will wait before the first health check.
+                                            Envoy will randomly select a value between 0 and the initial jitter value.
+                                          format: duration
+                                          pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+                                          type: string
                                         interval:
                                           default: 3s
                                           description: Interval defines the time between
@@ -25362,6 +25443,7 @@ spec:
                                             - 5xx
                                             - gateway-error
                                             - reset
+                                            - reset-before-request
                                             - connect-failure
                                             - retriable-4xx
                                             - refused-stream
@@ -26097,6 +26179,13 @@ spec:
                                     required:
                                     - path
                                     type: object
+                                  initialJitter:
+                                    description: |-
+                                      InitialJitter defines the maximum time Envoy will wait before the first health check.
+                                      Envoy will randomly select a value between 0 and the initial jitter value.
+                                    format: duration
+                                    pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
+                                    type: string
                                   interval:
                                     default: 3s
                                     description: Interval defines the time between
@@ -26568,6 +26657,7 @@ spec:
                                       - 5xx
                                       - gateway-error
                                       - reset
+                                      - reset-before-request
                                       - connect-failure
                                       - retriable-4xx
                                       - refused-stream