From da6b6a7198c59315a4796a49ada3e0fca29d98b0 Mon Sep 17 00:00:00 2001
From: kenichi-mashiyama <274147049+kenichi-mashiyama@users.noreply.github.com>
Date: Thu, 20 Aug 2026 14:07:48 +0900
Subject: [PATCH] Add support for HTTP header regex replacement
---
apis/config/v1alpha1/common_types.go | 24 ++++++++++++++
apis/config/v1alpha1/listen_types_test.go | 8 +++++
apis/config/v1alpha1/zz_generated.deepcopy.go | 21 ++++++++++++
docs/api-reference.md | 21 ++++++++++++
.../crds/config.haproxy.com_backends.yaml | 32 +++++++++++++++++++
.../crds/config.haproxy.com_frontends.yaml | 32 +++++++++++++++++++
.../crds/config.haproxy.com_listens.yaml | 32 +++++++++++++++++++
7 files changed, 170 insertions(+)
diff --git a/apis/config/v1alpha1/common_types.go b/apis/config/v1alpha1/common_types.go
index 8e0ffdb..82b1b33 100644
--- a/apis/config/v1alpha1/common_types.go
+++ b/apis/config/v1alpha1/common_types.go
@@ -858,6 +858,9 @@ type HTTPRequestRules struct {
// The replacement does not modify the scheme, the authority and the query-string.
// +optional
ReplacePath []ReplacePath `json:"replacePath,omitempty"`
+ // ReplaceValue matches occurrences of a header value using a regex and replaces them with the specified format.
+ // +optional
+ ReplaceValue []ReplaceValue `json:"replaceValue,omitempty"`
// Deny stops the evaluation of the rules and immediately rejects the request and emits an HTTP 403 error.
// Optionally the status code specified as an argument to deny_status.
// +optional
@@ -918,6 +921,17 @@ func (h *HTTPRequestRules) Model() (models.HTTPRequestRules, error) {
})
}
+ for _, header := range h.ReplaceValue {
+ model = append(model, &models.HTTPRequestRule{
+ Type: "replace-value",
+ HdrName: header.Name,
+ HdrMatch: header.MatchRegex,
+ HdrFormat: header.ReplaceFmt,
+ Cond: header.ConditionType,
+ CondTest: header.Condition,
+ })
+ }
+
for _, deny := range h.Deny {
if deny.Enabled {
model = append(model, &models.HTTPRequestRule{
@@ -1077,6 +1091,16 @@ type ReplacePath struct {
ReplaceFmt string `json:"replaceFmt"`
}
+type ReplaceValue struct {
+ Rule `json:",inline"`
+ // Name specifies the header name.
+ Name string `json:"name"`
+ // MatchRegex is a string pattern used to identify the header values that need to be replaced.
+ MatchRegex string `json:"matchRegex"`
+ // ReplaceFmt defines the format string used to replace the values that match the pattern.
+ ReplaceFmt string `json:"replaceFmt"`
+}
+
func (h *HTTPHeaderValue) String() string {
str := ptr.Deref(h.Str, "")
if h.Env != nil {
diff --git a/apis/config/v1alpha1/listen_types_test.go b/apis/config/v1alpha1/listen_types_test.go
index 3737228..1ce157f 100644
--- a/apis/config/v1alpha1/listen_types_test.go
+++ b/apis/config/v1alpha1/listen_types_test.go
@@ -131,6 +131,13 @@ var _ = Describe("Listen", Label("type"), func() {
Name: "Proxy",
},
},
+ ReplaceValue: []configv1alpha1.ReplaceValue{
+ {
+ Name: "Host",
+ MatchRegex: "(.*):.*",
+ ReplaceFmt: "\\1",
+ },
+ },
},
},
},
@@ -144,6 +151,7 @@ var _ = Describe("Listen", Label("type"), func() {
Ω(p.String()).Should(ContainSubstring("http-request set-path /metrics if !{ ssl_fc }"))
Ω(p.String()).Should(ContainSubstring("http-request del-header Proxy"))
Ω(p.String()).Should(ContainSubstring("http-request del-header regex -m str"))
+ Ω(p.String()).Should(ContainSubstring("http-request replace-value Host (.*):.* \\1"))
Ω(p.String()).Should(ContainSubstring("http-response set-header Strict-Transport-Security max-age=16000000; includeSubDomains; preload; if !{ ssl_fc }"))
})
It("should create binds", func() {
diff --git a/apis/config/v1alpha1/zz_generated.deepcopy.go b/apis/config/v1alpha1/zz_generated.deepcopy.go
index 3671876..6cdeffb 100644
--- a/apis/config/v1alpha1/zz_generated.deepcopy.go
+++ b/apis/config/v1alpha1/zz_generated.deepcopy.go
@@ -838,6 +838,11 @@ func (in *HTTPRequestRules) DeepCopyInto(out *HTTPRequestRules) {
*out = make([]ReplacePath, len(*in))
copy(*out, *in)
}
+ if in.ReplaceValue != nil {
+ in, out := &in.ReplaceValue, &out.ReplaceValue
+ *out = make([]ReplaceValue, len(*in))
+ copy(*out, *in)
+ }
if in.Deny != nil {
in, out := &in.Deny, &out.Deny
*out = make([]Deny, len(*in))
@@ -1320,6 +1325,22 @@ func (in *ReplacePath) DeepCopy() *ReplacePath {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ReplaceValue) DeepCopyInto(out *ReplaceValue) {
+ *out = *in
+ out.Rule = in.Rule
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReplaceValue.
+func (in *ReplaceValue) DeepCopy() *ReplaceValue {
+ if in == nil {
+ return nil
+ }
+ out := new(ReplaceValue)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Resolver) DeepCopyInto(out *Resolver) {
*out = *in
diff --git a/docs/api-reference.md b/docs/api-reference.md
index 7066d7c..56eeaa8 100644
--- a/docs/api-reference.md
+++ b/docs/api-reference.md
@@ -547,6 +547,7 @@ _Appears in:_
| `delHeader` _[HTTPDeleteHeaderRule](#httpdeleteheaderrule) array_ | DelHeader removes all HTTP header fields | | |
| `redirect` _[Redirect](#redirect) array_ | Redirect performs an HTTP redirection based on a redirect rule. | | Optional: \{\}
|
| `replacePath` _[ReplacePath](#replacepath) array_ | ReplacePath matches the value of the path using a regex and completely replaces it with the specified format.
The replacement does not modify the scheme, the authority and the query-string. | | Optional: \{\}
|
+| `replaceValue` _[ReplaceValue](#replacevalue) array_ | ReplaceValue matches occurrences of a header value using a regex and replaces them with the specified format. | | Optional: \{\}
|
| `deny` _[Deny](#deny) array_ | Deny stops the evaluation of the rules and immediately rejects the request and emits an HTTP 403 error.
Optionally the status code specified as an argument to deny_status. | | Optional: \{\}
|
| `return` _[HTTPReturn](#httpreturn)_ | Return stops the evaluation of the rules and immediately returns a response. | | |
@@ -909,6 +910,26 @@ _Appears in:_
| `replaceFmt` _string_ | ReplaceFmt defines the format string used to replace the values that match the pattern. | | |
+#### ReplaceValue
+
+
+
+
+
+
+
+_Appears in:_
+- [HTTPRequestRules](#httprequestrules)
+
+| Field | Description | Default | Validation |
+| --- | --- | --- | --- |
+| `conditionType` _string_ | ConditionType specifies the type of the condition matching ('if' or 'unless') | | Enum: [if unless]
Optional: \{\}
|
+| `condition` _string_ | Condition is a condition composed of ACLs. | | Optional: \{\}
|
+| `name` _string_ | Name specifies the header name. | | |
+| `matchRegex` _string_ | MatchRegex is a string pattern used to identify the header values that need to be replaced. | | |
+| `replaceFmt` _string_ | ReplaceFmt defines the format string used to replace the values that match the pattern. | | |
+
+
#### Resolver
diff --git a/helm/haproxy-operator/crds/config.haproxy.com_backends.yaml b/helm/haproxy-operator/crds/config.haproxy.com_backends.yaml
index 3b8e92a..1003021 100644
--- a/helm/haproxy-operator/crds/config.haproxy.com_backends.yaml
+++ b/helm/haproxy-operator/crds/config.haproxy.com_backends.yaml
@@ -788,6 +788,38 @@ spec:
- replaceFmt
type: object
type: array
+ replaceValue:
+ description: ReplaceValue matches occurrences of a header value
+ using a regex and replaces them with the specified format.
+ items:
+ properties:
+ condition:
+ description: Condition is a condition composed of ACLs.
+ type: string
+ conditionType:
+ description: ConditionType specifies the type of the condition
+ matching ('if' or 'unless')
+ enum:
+ - if
+ - unless
+ type: string
+ matchRegex:
+ description: MatchRegex is a string pattern used to identify
+ the header values that need to be replaced.
+ type: string
+ name:
+ description: Name specifies the header name.
+ type: string
+ replaceFmt:
+ description: ReplaceFmt defines the format string used to
+ replace the values that match the pattern.
+ type: string
+ required:
+ - matchRegex
+ - name
+ - replaceFmt
+ type: object
+ type: array
return:
description: Return stops the evaluation of the rules and immediately
returns a response.
diff --git a/helm/haproxy-operator/crds/config.haproxy.com_frontends.yaml b/helm/haproxy-operator/crds/config.haproxy.com_frontends.yaml
index 4fdde7b..95990ea 100644
--- a/helm/haproxy-operator/crds/config.haproxy.com_frontends.yaml
+++ b/helm/haproxy-operator/crds/config.haproxy.com_frontends.yaml
@@ -1116,6 +1116,38 @@ spec:
- replaceFmt
type: object
type: array
+ replaceValue:
+ description: ReplaceValue matches occurrences of a header value
+ using a regex and replaces them with the specified format.
+ items:
+ properties:
+ condition:
+ description: Condition is a condition composed of ACLs.
+ type: string
+ conditionType:
+ description: ConditionType specifies the type of the condition
+ matching ('if' or 'unless')
+ enum:
+ - if
+ - unless
+ type: string
+ matchRegex:
+ description: MatchRegex is a string pattern used to identify
+ the header values that need to be replaced.
+ type: string
+ name:
+ description: Name specifies the header name.
+ type: string
+ replaceFmt:
+ description: ReplaceFmt defines the format string used to
+ replace the values that match the pattern.
+ type: string
+ required:
+ - matchRegex
+ - name
+ - replaceFmt
+ type: object
+ type: array
return:
description: Return stops the evaluation of the rules and immediately
returns a response.
diff --git a/helm/haproxy-operator/crds/config.haproxy.com_listens.yaml b/helm/haproxy-operator/crds/config.haproxy.com_listens.yaml
index 82fe7d5..4bdf654 100644
--- a/helm/haproxy-operator/crds/config.haproxy.com_listens.yaml
+++ b/helm/haproxy-operator/crds/config.haproxy.com_listens.yaml
@@ -1314,6 +1314,38 @@ spec:
- replaceFmt
type: object
type: array
+ replaceValue:
+ description: ReplaceValue matches occurrences of a header value
+ using a regex and replaces them with the specified format.
+ items:
+ properties:
+ condition:
+ description: Condition is a condition composed of ACLs.
+ type: string
+ conditionType:
+ description: ConditionType specifies the type of the condition
+ matching ('if' or 'unless')
+ enum:
+ - if
+ - unless
+ type: string
+ matchRegex:
+ description: MatchRegex is a string pattern used to identify
+ the header values that need to be replaced.
+ type: string
+ name:
+ description: Name specifies the header name.
+ type: string
+ replaceFmt:
+ description: ReplaceFmt defines the format string used to
+ replace the values that match the pattern.
+ type: string
+ required:
+ - matchRegex
+ - name
+ - replaceFmt
+ type: object
+ type: array
return:
description: Return stops the evaluation of the rules and immediately
returns a response.