Skip to content

feat: add support for from-to-www-redirect#337

Merged
k8s-ci-robot merged 8 commits intokubernetes-sigs:mainfrom
chakravardhan:feature/nginx-redirects
Apr 1, 2026
Merged

feat: add support for from-to-www-redirect#337
k8s-ci-robot merged 8 commits intokubernetes-sigs:mainfrom
chakravardhan:feature/nginx-redirects

Conversation

@chakravardhan
Copy link
Copy Markdown
Contributor

@chakravardhan chakravardhan commented Feb 6, 2026

What type of PR is this?

/kind feature

What this PR does / why we need it:

When the from-to-www-redirect annotation is encountered and set to "true", the translator generates a completely separate, dedicated HTTPRoute for the redirect.

Translation Logic:
When the from-to-www-redirect annotation is encountered and set to "true":

  1. The translator generates a completely separate, dedicated HTTPRoute for the redirect listening on the alternative domain. For example, if the Ingress specifies example.com, this feature attaches a new HTTPRoute listening on www.example.com that returns a 308 permanent redirect to example.com.
  2. The translator dynamically inspects the generated IR Gateway and injects a new Listener for the alternative domain. This ensures the Gateway accepts traffic for the redirect route.

Compatibility with permanent-redirect (#299)

There is a potential edge case where a user specifies both from-to-www-redirect and permanent-redirect on the same Ingress resource.

How this is handled:

  • This PR (from-to-www-redirect) generates a completely separate HTTPRoute dedicated to the www/non-www redirect. Because they operate on different HTTPRoute objects or distinct hostnames, they do not overwrite each other during the Intermediate Representation (IR) generation. When emitted, the Gateway API handles this overlap natively and gracefully by selecting the most specific Hostname match for routing the request.

Testing

  • Unit Tests: Added tests in redirect_test.go to verify route generation and hostname swapping.
  • Integration Tests: Added an end-to-end from-to-www-redirect scenario in converter_test.go verifying the correct emission of dual Gateway listeners and appropriate side-car HTTPRoutes.

Which issue(s) this PR fixes:

Fixes #

Does this PR introduce a user-facing change?:

Add translation support for NGINX Ingress annotations  `from-to-www-redirect`.

@k8s-ci-robot k8s-ci-robot added the kind/feature Categorizes issue or PR as related to a new feature. label Feb 6, 2026
@k8s-ci-robot k8s-ci-robot added cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. labels Feb 6, 2026
@k8s-ci-robot
Copy link
Copy Markdown
Contributor

Hi @chakravardhan. Thanks for your PR.

I'm waiting for a kubernetes-sigs member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@k8s-ci-robot k8s-ci-robot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Feb 6, 2026
@Stevenjin8
Copy link
Copy Markdown
Contributor

/ok-to-test

@k8s-ci-robot k8s-ci-robot added ok-to-test Indicates a non-member PR verified by an org member that is safe to test. and removed needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. labels Feb 6, 2026
@Stevenjin8
Copy link
Copy Markdown
Contributor

@chakravardhan the from-www-redirect seems reasonable to me, but have you how it would work with #299? what happens if you have from-www-redirect and that the ones in #299? I'm hoping #299 merges soon. Otherwise, the implementation seems good at a quick glance.

As for force-ssl-redirect, I'm not sure if we want or event need this annotation. My understanding as follows:

  1. User configures Ingress resource to point to TLS certs
  2. Ingress NGINX creates a self signed cert for hosts that do not have a configured TLS cert
  3. For requests with a host without a configured cert, ingress nginx will not redirect HTTP to HTTPS unless force-ssl-redirect is explicitly enabled.

The problem is that gateway API doesn't have a "listen on 443 with a runtime-generate self-signed cert". That is, we might redirect to HTTPS, but not have an HTTPS listener at all! Haven't looked at the code too much, but LMK what you think.

@chakravardhan
Copy link
Copy Markdown
Contributor Author

@chakravardhan the from-www-redirect seems reasonable to me, but have you how it would work with #299? what happens if you have from-www-redirect and that the ones in #299? I'm hoping #299 merges soon. Otherwise, the implementation seems good at a quick glance.

As for force-ssl-redirect, I'm not sure if we want or event need this annotation. My understanding as follows:

  1. User configures Ingress resource to point to TLS certs
  2. Ingress NGINX creates a self signed cert for hosts that do not have a configured TLS cert
  3. For requests with a host without a configured cert, ingress nginx will not redirect HTTP to HTTPS unless force-ssl-redirect is explicitly enabled.

The problem is that gateway API doesn't have a "listen on 443 with a runtime-generate self-signed cert". That is, we might redirect to HTTPS, but not have an HTTPS listener at all! Haven't looked at the code too much, but LMK what you think.

Thanks for your reply! @Stevenjin8

Regarding force-ssl-redirect:

You are absolutely right. Gateway API doesn't have the concept of generating self-signed listener certificates on the fly like NGINX does. If we force a redirect to 443 without actual TLS secrets configured, most Gateway implementations (like GKE) will just reject the route or fail to open the listener, leading to a broken state.
I completely agree that we don't need this annotation translation since the architecture paradigms don't align. I will drop force-ssl-redirect from this PR.

Regarding from-to-www-redirect and #299 compatibility:

In our current implementation, we generate an entirely separate HTTPRoute dedicated to the www redirect (e.g., routeName-www-redirect).

Because #299 adds RequestRedirect filters to the existing route based on the permanent-redirect annotation, these two features should naturally avoid stepping on each other's toes—one modifies the primary route, and the other creates an additional standalone route specifically for the WWW redirect.

If there is an overlap where a user configures both from-to-www-redirect and a manual permanent-redirect that also points to www, the Gateway API should handle this cleanly: the underlying controller will simply use the most specific matching rule (or fallback to the oldest creation timestamp) to cleanly serve the redirect, rather than crashing or causing route conflicts. wdyt?

@chakravardhan chakravardhan changed the title feat(ingressnginx): add support for force-ssl-redirect and from-to-www-redirect feat: add support for from-to-www-redirect Feb 6, 2026
@Stevenjin8
Copy link
Copy Markdown
Contributor

@chakravardhan I don't think this is quite right. Do you mind writing an e2e/integration test just to verify that behavior is correct/taking a quick look at https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#redirect-fromto-www ?

For example, the annotation can redirect host.com to www.host.com and from www.host.com to host.com, depending on the initial configuration.

Also when I run it with

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: example-ingress
  namespace: default
  annotations:
    nginx.ingress.kubernetes.io/from-to-www-redirect: "true"
spec:
  ingressClassName: nginx
  rules:
    - host: example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: httpbin
                port:
                  number: 8000

I get

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  annotations:
    gateway.networking.k8s.io/generator: ingress2gateway-dev
  name: nginx
  namespace: default
spec:
  gatewayClassName: nginx
  listeners:
  - hostname: example.com
    name: example-com-http
    port: 80
    protocol: HTTP
status: {}
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  annotations:
    gateway.networking.k8s.io/generator: ingress2gateway-dev
  name: example-ingress-example-com
  namespace: default
spec:
  hostnames:
  - example.com
  parentRefs:
  - name: nginx
  rules:
  - backendRefs:
    - name: httpbin
      port: 8000
    matches:
    - path:
        type: PathPrefix
        value: /
    name: rule-0
status:
  parents: []
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  annotations:
    gateway.networking.k8s.io/generator: ingress2gateway-dev
  name: example-ingress-example-com-www-redirect
  namespace: default
spec:
  hostnames:
  - example.com
  parentRefs:
  - name: nginx
  rules:
  - filters:
    - requestRedirect:
        hostname: www.example.com
        statusCode: 308
      type: RequestRedirect
status:
  parents: null

We have two http routes with the same host name, the last httproute should be

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  annotations:
    gateway.networking.k8s.io/generator: ingress2gateway-dev
  name: example-ingress-example-com-www-redirect
  namespace: default
spec:
  hostnames:
-  - example.com
+  - www.example.com
  parentRefs:
  - name: nginx
  rules:
  - filters:
    - requestRedirect:
-        hostname: www.example.com
+        hostname: example.com
        statusCode: 308
      type: RequestRedirect
status:
  parents: null

Also, the gateway would need to be updated

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  annotations:
    gateway.networking.k8s.io/generator: ingress2gateway-dev
  name: nginx
  namespace: default
spec:
  gatewayClassName: nginx
  listeners:
  - hostname: example.com
    name: example-com-http
    port: 80
    protocol: HTTP
+ - hostname: www.example.com
+    name: www.example-com-http
+   port: 80
+   protocol: HTTP
status: {}

@chakravardhan
Copy link
Copy Markdown
Contributor Author

chakravardhan commented Feb 9, 2026

Thanks @Stevenjin8, I misunderstood this!

I've pushed a fix that addresses both issues:

  1. Redirect Direction: The redirect HTTPRoute now correctly listens on the alternative host (e.g., www.example.com) and redirects traffic to the primary Ingress host (e.g., example.com), and vice-versa.
  2. Gateway Listeners: The translation logic now dynamically searches the Parent Gateway and injects a new Listener for the alternative hostname so that the Gateway accepts the traffic meant for the redirect route.
    I've also added an integration test in converter_test.go, which now passes and outputs the correct Gateway and HTTPRoutes.

@Stevenjin8
Copy link
Copy Markdown
Contributor

@chakravardhan, integration tests go under ./e2e.

@chakravardhan
Copy link
Copy Markdown
Contributor Author

@chakravardhan, integration tests go under ./e2e.

@Stevenjin8, Added the e2e Test.

I discovered that while ingress-nginx natively accepted and returned 308 (Permanent Redirect) for this annotation, the Kubernetes Gateway API specification enforces a strict validation that limits RequestRedirect status codes to exactly 301 or 302.

I've updated the translator to natively map the from-to-www-redirect config into the valid Gateway-compliant 301 status code, and verified this functionality end-to-end against an Istio cluster via the E2E framework.

@Stevenjin8
Copy link
Copy Markdown
Contributor

@chakravardhan could you also have teh tests send to port 443?

@Stevenjin8
Copy link
Copy Markdown
Contributor

I think my main point is that the listeners you create do no have tls certs configured and I'm pretty sure ingress nginx would do that.

@k8s-ci-robot k8s-ci-robot added size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. and removed size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Feb 13, 2026
@chakravardhan
Copy link
Copy Markdown
Contributor Author

chakravardhan commented Feb 13, 2026

I think my main point is that the listeners you create do no have tls certs configured and I'm pretty sure ingress nginx would do that.

@Stevenjin8, Fixed this in the latest commit! The code scans the Gateway's existing listeners for the dstHost (the target domain), grabs its gatewayv1.ListenerTLSConfig (which contains the test-cert SecretRef), and successfully injects it into the newly generated https redirect Listener.

Copy link
Copy Markdown
Contributor

@Stevenjin8 Stevenjin8 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will definitely conflict with #330, but I can figure that out.

@k8s-ci-robot k8s-ci-robot added approved Indicates a PR has been approved by an approver from all required OWNERS files. needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. labels Feb 13, 2026
@k8s-ci-robot k8s-ci-robot added size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. and removed size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. labels Feb 19, 2026
@chakravardhan chakravardhan force-pushed the feature/nginx-redirects branch from 939b361 to 7f5e1d9 Compare February 19, 2026 06:57
@k8s-ci-robot k8s-ci-robot added size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. and removed needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. labels Feb 19, 2026
@chakravardhan
Copy link
Copy Markdown
Contributor Author

@chakravardhan please rebase

done

@k8s-ci-robot k8s-ci-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Feb 27, 2026
@bexxmodd
Copy link
Copy Markdown

bexxmodd commented Mar 9, 2026

@chakravardhan We'll need to rebase it again.

@chakravardhan chakravardhan force-pushed the feature/nginx-redirects branch from 7f5e1d9 to f739ead Compare March 31, 2026 18:49
@k8s-ci-robot k8s-ci-robot added size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. and removed needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. labels Mar 31, 2026
@chakravardhan
Copy link
Copy Markdown
Contributor Author

@Stevenjin8, if this PR still makes sense, could you provide LGTM label on this? Thanks

@Stevenjin8
Copy link
Copy Markdown
Contributor

@chakravardhan id rather not lgtm and approve the same PR. Could you get @bexxmodd or @spencerhance to take a look?

@bexxmodd
Copy link
Copy Markdown

bexxmodd commented Apr 1, 2026

/lgtm
/approve

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Apr 1, 2026
@k8s-ci-robot
Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: bexxmodd, chakravardhan, Stevenjin8

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robot k8s-ci-robot merged commit 531147e into kubernetes-sigs:main Apr 1, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. kind/feature Categorizes issue or PR as related to a new feature. lgtm "Looks good to me", indicates that a PR is ready to be merged. ok-to-test Indicates a non-member PR verified by an org member that is safe to test. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants