node-red-dashboard icon indicating copy to clipboard operation
node-red-dashboard copied to clipboard

Handle trailing slashes in the URL

Open Steve-Mcl opened this issue 7 months ago • 3 comments

closes #1476

Description

After some consideration, we will stick with slash-less URLs like /page1, and enforce that style with a 301 redirect middleware. It’s cleaner, SEO-friendly, backward-compatible, and avoids unnecessary rewrites.

Related Issue(s)

#1476

Checklist

  • [x] I have read the contribution guidelines
  • [x] Suitable unit/system level tests have been added and they pass
  • [ ] Documentation has been updated
    • [ ] Upgrade instructions
    • [ ] Configuration details
    • [ ] Concepts
  • [ ] Changes flowforge.yml?
    • [ ] Issue/PR raised on FlowFuse/helm to update ConfigMap Template
    • [ ] Issue/PR raised on FlowFuse/CloudProject to update values for Staging/Production

Labels

  • [ ] Includes a DB migration? -> add the area:migration label

Steve-Mcl avatar Apr 08 '25 18:04 Steve-Mcl

@robertsLando @Bond246 @rsp92028 Would the changes in this PR satisfy your issue?

The important part of the change is here: https://github.com/FlowFuse/node-red-dashboard/pull/1663/files#diff-76241199d5a97f1b00433a720ea273842ca6515d31015b1b24aee147e2a975bdR137-R143

In effect, what it does is:

  1. It checks if someone is trying to open a page with with a / at the end
  2. It then redirects (with a 301 status) to the correct page without the trailing slash.

Steve-Mcl avatar Apr 09 '25 08:04 Steve-Mcl

@joepavitt one for you when you return

I am thinking this may be better handled in vue router?

I am unsure if redirecting will cause a loop with nginx

FWIW, if you enter a URL on the FF website without a trailing slash, it gets appended e.g. https://flowfuse.com/blog/2025/04/building-oee-dashboard-with-flowfuse-2 becomes https://flowfuse.com/blog/2025/04/building-oee-dashboard-with-flowfuse-2/

Steve-Mcl avatar Apr 09 '25 09:04 Steve-Mcl

i will test it when it is merged to the main release. Hope it work then i will be fine with it 💯

Bond246 avatar Apr 09 '25 11:04 Bond246

@Steve-Mcl @joepavitt - please see this thread - https://discourse.nodered.org/t/flowfuse-dashboard-2-0-issue-err-too-many-redirects/97107/6

I also saw this too_many_redirects loop when using my worldmap node (which sets up it's own endpoint also) but never figured out why...

dceejay avatar May 18 '25 09:05 dceejay

@dceejay what endpoint is setup in your problematic instance?

from my testing, the middleware only affects /dashboard/... routes.

If your route is /dashboard/worldmap[/...] I can understand why (and makes the fix a little tougher!)

Also is a reverse proxy involved in your setup?

EDIT

Never mind. Routes added after Node-RED is setup are actually affected. Fix incoming

Steve-Mcl avatar May 18 '25 10:05 Steve-Mcl

Hi - I was using it within an iframe so should was just set to /worldmap - but on one instance it went into that loop and wouldn't come out.. See also https://discourse.nodered.org/t/worldmap-dashboard-2-0-iframe-example-error-too-many-redirects/97123 for another user with same issue

dceejay avatar May 18 '25 19:05 dceejay

For me the problem still exists. I use traefik reverse proxy with an "addprefix" middleware that redirects the base-path "/" to "/dashboard/some-page". Before the 1.24er dashboard-version there was just a problem with trailing slashes and traefik said it can't reach /dashboard/some-page/.

Now with the new version the browser-path is updated to /dashboard/some-page and traefik try to reach /dashboard/some-page/dashboard/some-page... obviously not working...

Bond246 avatar May 27 '25 12:05 Bond246

@Bond246 is this not an issue with your setup?

You state that "addprefix" middleware that redirects the base-path "/" to "/dashboard/some-page". so why would your configuration be appending "dashboard/some-page" to "dashboard/some-page"?

All the fix in this PR does is to redirect (with a 302 status code) from /dashboard/xxx/ to /dashboard/xxx (the redirect from dashboard/ to dashboard/first-page was an existing feature)

Steve-Mcl avatar May 27 '25 12:05 Steve-Mcl

@Steve-Mcl the "addprefix" thing is really fine in treafik because if you just want to bypass some parts of the path all path-parts after the hardcoded prefix are to be seen in the browser. It just always adds the defined prefix.

In this situation here if the redirect status is mapped to the requesting browser as whole path of course you get appending paths. I actually try to ask the traefik community if there is another way but at the end of the day i just want to get the same behaviour like in the old nodered-dashboard the the /ui path where this "hack" worked really fine.

Bond246 avatar May 27 '25 13:05 Bond246

@Bond246 could you share your traefik config (sanitise where necessary).

Steve-Mcl avatar May 28 '25 08:05 Steve-Mcl

@Steve-Mcl while it's inside kubernetes i try to copy what is need in my opinion. Node-Red Service:

apiVersion: v1
kind: Service
metadata:
  name: nodered-test
  namespace: nodered
spec:
  clusterIP: 10.211.92.133
  clusterIPs:
  - 10.211.92.133
  internalTrafficPolicy: Cluster
  ipFamilies:
  - IPv4
  ipFamilyPolicy: SingleStack
  ports:
  - port: 1880
    protocol: TCP
    targetPort: 1880
  selector:
    app: nodered-test
  sessionAffinity: None
  type: ClusterIP
status:
  loadBalancer: {}

IngressRoute with multible rules:

apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
  generation: 3
  name: nodered-test
  namespace: nodered
spec:
  entryPoints:
  - web
  routes:
  - kind: Rule
    match: Host(`nodered-test.domain.de`)
    services:
    - kind: Service
      name: nodered-test
      namespace: nodered
      port: 1880
  - kind: Rule
    match: Host(`nodered-test-page1.domain.de`)
    middlewares:
    - name: nodered-dashboard-page1
    services:
    - kind: Service
      name: nodered-test
      namespace: nodered
      port: 1880
  - kind: Rule
    match: Host(`nodered-test-page2.domain.de`)
    middlewares:
    - name: nodered-dashboard-page2
    services:
    - kind: Service
      name: nodered-test
      namespace: nodered
      port: 1880

Middlewares:

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  generation: 1
  name: nodered-dashboard-page1
  namespace: nodered
spec:
  addPrefix:
    prefix: /dashboard/page1
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  generation: 1
  name: nodered-dashboard-page2
  namespace: nodered
spec:
  addPrefix:
    prefix: /dashboard/page2

Bond246 avatar May 28 '25 09:05 Bond246

@Bond246 I am not up to speed on traefik but

You currently have two middlewares:

  • nodered-dashboard-page1: adds prefix /dashboard/page1
  • nodered-dashboard-page2: adds prefix /dashboard/page2

BUT:

  • They just add a prefix → e.g., / becomes /dashboard/page1,
  • they don’t check the incoming path — they will blindly apply the prefix even if the request is already under /dashboard/page1.

This is why you’re seeing things like:

/dashboard/page1 → /dashboard/page1/dashboard/page1 → /dashboard/page1/dashboard/page1/dashboard/page1 ...

You could try Redirect only:

  • /
  • /dashboard
  • /dashboard/

But leave:

  • /dashboard/page1
  • /dashboard/page2
  • anything under /dashboard/* untouched.

Something like:

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: nodered-dashboard-redirect
  namespace: nodered
spec:
  redirectRegex:
    regex: ^(/dashboard)?/?$
    replacement: /dashboard/page1
    permanent: true

Why I suspect addPrefix is not right here: addPrefix always prepends the string, no matter what the incoming path is. AFAIK, it doesn’t check if the path already has it so can lead to repeated prefixes. I think by switching to redirectRegex, you can conditionally match and rewrite only when necessary?

Steve-Mcl avatar May 28 '25 10:05 Steve-Mcl

@Steve-Mcl you are correct, addPrefix always would add the path even if it already exists. But i also could got to google.com/foo/bar and would get 404 becaus it doesn't exist. So in my case the user doesn't know and doesn't have to know that there is something like /dashboard/xyz because he stays in his single specific space under /dashboard/page1 and thats it. Everything he needs is there.

But i will try to test redirectRegex which in this case would make the path that is added visible. Not so nice in my opinion like addPrefix but would fix the issue.

Bond246 avatar Jun 04 '25 12:06 Bond246

redirectRegex worked well like it should. Not so nice like addprefix where the path was not provided to the browser url-bar but ok for what i want to do with it.

Bond246 avatar Jun 24 '25 10:06 Bond246