Best way to handle HTTP redirects within the cluster?

INFORMATION

ISSUE

We are hosting a React application in our cluster, behind a Cloudfront distribution, very similar to what is described here: How to use CloudFront with a React frontend application on Qovery | Qovery

The upstream (origin) server is running nginx and hosting our static React app.

We need to implement a 301 HTTP redirect in our application to redirect my.company.comapp.company.com. We are attempting to implement this by adding an ingress object to our environment that looks something like this:

$ kubectl -n <env> get ingress
**Added by us** --> zebb526a6-<env>     app-redirect                <none>   ...      
**Qovery managed** -->zebb526a6-<env>      router-z971ef310-fe-admin          <none>   ...
**Qovery managed** -->zebb526a6-<env>      router-zdbc3d4a6-fe-app            <none>   ...
$ kubectl -n zebb526a6-<env> get ingress app-redirect -o yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
...
    kubernetes.io/ingress.class: nginx-qovery
    kubernetes.io/tls-acme: "true"
    nginx.ingress.kubernetes.io/permanent-redirect: https://app.company.com/
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/whitelist-source-range: 0.0.0.0/0
...
spec:
  rules:
  - host: my.company.com
    http:
      paths:
      - backend:
          service:
            name: app-zdbc3d4a6-app
            port:
              number: 80
        path: /
        pathType: Prefix
  tls:
  - hosts:
    - my.company.com
    secretName: app-redirect-tls
status:
  loadBalancer:
    ingress:
    - hostname: 1234-1234.elb.us-east-1.amazonaws.com

This does what we want, but is maintaining a separate Kubernetes object in our environment safe? If not, is there a recommended way to handle this?

Hi @Kyle_Flavin ,

Thanks for asking. It’s a very common use case, and here is a better way to do this than updating your cluster ingresses.

You can inspire yourself from this thread:

The idea is to deploy an NGINX or Caddy (recommended) container in front of your application to redirect the traffic the way you want.

The caddy configuration could be something like this:

http:// {
        bind 0.0.0.0
        reverse_proxy /other-path/* https://your-old-app.domain.tld
        reverse_proxy /* http://{$FRONTEND_HOST}:8000
}

:warning: Note: FRONTEND_APP is an environment variable that you have defined in the Qovery environment variable. {$FRONTEND_APP} is the Caddy syntax to declare an environment variable.


Advantages

  • This is a much better and simpler way than modifying the NGINX ingress controller on your Kubernetes controller.
  • There is no risk that the configuration breaks in case of a Kubernetes upgrade
  • The container can be scaled the way you want - it’s stateless
  • You can version (GitOps) the caddy configuration into Git :slight_smile:
1 Like

Thanks Romaric! One clarification: we’re not updating the ingresses that Qovery created; we are adding a new ingress just for the redirect.

My issue with the Caddy approach is it adds another layer of indirection to our primary domain. So instead of this:

user request → AWS load balancer → ingress nginx → nginx container serving app

we end up with this:

user request → AWS load balancer → ingress nginx → caddy service → nginx container serving app

With requests for both the primary domain, app.company.com, and the host we want to redirect, my.company.com, going through Caddy. (although, I suppose we could route only my.company.com through Caddy)

Since this is a one-off use case for us, we’ve decided to just add the redirect directly into the nginx app container, which seems simplest, and should be good enough for now.

Is there a timeframe on your roadmap for supporting redirects through ingresses in the UI?

Hello Kyle,

Yes it is safe to do, the service name of the app is not going to change once created. One caveat, though, is that you will not be able to clone this environment, as your new ingress will not follow/or the service name is not going to change.

Another option you have, is to declare a 2nd domain app.company.com on your service, and in your code have a handler that returns a 301 permanent redirect if the host header is not matching app.company.com