Skip to content

Commit 9ab8a5e

Browse files
igoramfclaude
andauthored
fix(decoredirect): preserve path on apex redirect (#23)
* fix(decoredirect): preserve path on apex redirect using \$request_uri The permanent-redirect annotation was set to the bare target URL (e.g. https://www.client.com), causing nginx to redirect all requests to the root — discarding the path. Append nginx's \$request_uri variable so the full path and query string are preserved. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(decoredirect): use server-snippet to preserve path on apex redirect permanent-redirect annotation does not support nginx variables — the admission webhook rejects values containing $. Switch to server-snippet with $request_uri so the full path and query string are preserved. Requires allow-snippet-annotations: true and annotations-risk-level: Critical in the nginx ingress controller config (infra_applications PRs #156, #157). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 68f58ea commit 9ab8a5e

2 files changed

Lines changed: 10 additions & 10 deletions

File tree

internal/controller/decoredirect_controller.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"fmt"
77
"net"
88
"net/http"
9-
"strconv"
109
"strings"
1110
"time"
1211

@@ -137,11 +136,12 @@ func (r *DecoRedirectReconciler) reconcileIngress(ctx context.Context, rd *decos
137136
code = *rd.Spec.RedirectCode
138137
}
139138

140-
// nginx returns the configured redirect code (default 307) via the permanent-redirect annotation before reaching any backend.
139+
// server-snippet injects a raw nginx return directive that preserves the full request path and
140+
// query string via $request_uri. The permanent-redirect annotation cannot be used here because
141+
// the nginx admission webhook rejects values containing nginx variables (e.g. $request_uri).
141142
_, err := controllerutil.CreateOrUpdate(ctx, r.Client, ingress, func() error {
142143
ingress.Annotations = map[string]string{
143-
"nginx.ingress.kubernetes.io/permanent-redirect": rd.Spec.To,
144-
"nginx.ingress.kubernetes.io/permanent-redirect-code": strconv.Itoa(code),
144+
"nginx.ingress.kubernetes.io/server-snippet": fmt.Sprintf("return %d %s$request_uri;", code, rd.Spec.To),
145145
}
146146
ingress.Spec = networkingv1.IngressSpec{
147147
IngressClassName: &r.IngressClass,

internal/controller/decoredirect_controller_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ var _ = Describe("DecoRedirect Controller", func() {
7171
Expect(cert.Spec.SecretName).To(Equal("tls-client-com"))
7272
})
7373

74-
It("should create an Ingress with permanent-redirect annotation", func() {
74+
It("should create an Ingress with server-snippet redirect preserving path", func() {
7575
_, err := newReconciler().Reconcile(ctx, reconcile.Request{NamespacedName: nn})
7676
Expect(err).NotTo(HaveOccurred())
7777

@@ -80,7 +80,7 @@ var _ = Describe("DecoRedirect Controller", func() {
8080
Name: "redirect-client-com", Namespace: rdNS,
8181
}, ing)).To(Succeed())
8282
Expect(*ing.Spec.IngressClassName).To(Equal("nginx"))
83-
Expect(ing.Annotations["nginx.ingress.kubernetes.io/permanent-redirect"]).To(Equal(toDomain))
83+
Expect(ing.Annotations["nginx.ingress.kubernetes.io/server-snippet"]).To(Equal("return 307 " + toDomain + "$request_uri;"))
8484
Expect(ing.Spec.TLS[0].Hosts).To(ContainElement(fromDomain))
8585
Expect(ing.Spec.TLS[0].SecretName).To(Equal("tls-client-com"))
8686
Expect(ing.Spec.Rules[0].Host).To(Equal(fromDomain))
@@ -149,18 +149,18 @@ var _ = Describe("DecoRedirect Controller", func() {
149149
Expect(count).To(Equal(1))
150150
})
151151

152-
It("should set permanent-redirect-code to 307 by default", func() {
152+
It("should use return 307 in server-snippet by default", func() {
153153
_, err := newReconciler().Reconcile(ctx, reconcile.Request{NamespacedName: nn})
154154
Expect(err).NotTo(HaveOccurred())
155155

156156
ing := &networkingv1.Ingress{}
157157
Expect(k8sClient.Get(ctx, types.NamespacedName{
158158
Name: "redirect-client-com", Namespace: rdNS,
159159
}, ing)).To(Succeed())
160-
Expect(ing.Annotations["nginx.ingress.kubernetes.io/permanent-redirect-code"]).To(Equal("307"))
160+
Expect(ing.Annotations["nginx.ingress.kubernetes.io/server-snippet"]).To(ContainSubstring("return 307 "))
161161
})
162162

163-
It("should use redirectCode 301 in the Ingress annotation when specified", func() {
163+
It("should use return 301 in server-snippet when redirectCode is 301", func() {
164164
code := 301
165165
rd301 := &decositesv1alpha1.DecoRedirect{
166166
ObjectMeta: metav1.ObjectMeta{Name: "test-redirect-301", Namespace: rdNS},
@@ -181,7 +181,7 @@ var _ = Describe("DecoRedirect Controller", func() {
181181
Expect(k8sClient.Get(ctx, types.NamespacedName{
182182
Name: "redirect-redirect301-com", Namespace: rdNS,
183183
}, ing)).To(Succeed())
184-
Expect(ing.Annotations["nginx.ingress.kubernetes.io/permanent-redirect-code"]).To(Equal("301"))
184+
Expect(ing.Annotations["nginx.ingress.kubernetes.io/server-snippet"]).To(ContainSubstring("return 301 "))
185185
})
186186
})
187187

0 commit comments

Comments
 (0)