Bug description

The bug is that when I set custom app root with SUPERSET_APP_ROOT but some endpoints was generated incorrect.

SUPERSET_APP_ROOT: /analytics

1) Export dashboard -> http://hostname/analytics/analytics/api/v1/chart/data, but should be http://hostname/analytics/api/v1/chart/data (duplicate prefix)

2) Userinfo -> http://hostname/user_info/, but should be http://hostname/analytics/user_info/ (missing prefix)

3) Icon png -> https://hostname/static/assets/images/superset-logo-horiz.png, but should be https://hostname/analytics/static/assets/images/superset-logo-horiz.png (missing prefix)

### Screenshots/recordings

Image

Image

### Superset version

master / latest-dev

### Python version

3.9

### Node version

16

### Browser

Chrome

### Additional context

My full hem values.yaml

# https://github.com/apache/superset/tree/master/helm/superset

ingress:
  enabled: true
  ingressClassName: nginx
  hosts:
    - <hostname>
  path: /
  pathType: Prefix
  tls: []
  annotations:
    nginx.ingress.kubernetes.io/proxy-connect-timeout: "300"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "300"
    nginx.ingress.kubernetes.io/proxy-body-size: 100M
    nginx.ingress.kubernetes.io/configuration-snippet: |
      if ($request_uri = "/") {
        return 301 /analytics;
      }
      if ($request_uri = "/static/assets/images/superset-logo-horiz.png") {
        return 301 /analytics/static/assets/images/superset-logo-horiz.png;
      }
image:
  tag: 6.0.0rc1

redis:
  enabled: true
  persistence:
    enabled: false

postgresql:
  enabled: true
  primary:
    persistence:
      enabled: false

bootstrapScript: |
  #!/bin/bash
  apt-get update && apt-get install -y gcc && \
  uv pip install \
    psycopg2-binary==2.9.10 \
    clickhouse-connect==0.7.7 \
    PyJWT==2.10.1 \
    authlib

configOverrides:
  secret: |
    from flask import g
    from jinja2 import pass_context
    from flask_appbuilder.security.manager import AUTH_DB

    SECRET_KEY = 's/lfxyPiVXz1H/mkCueBTJuTH7FlVoqCWVLCmOPqsCdE21pxoq4H4LNo'
    @pass_context
    def current_tenant(context, *args, **kwargs):
      return getattr(g, "tenant_id", None) or "-1"

    JINJA_CONTEXT_ADDONS = {
      "current_tenant": current_tenant,
    }

    AUTH_TYPE = AUTH_DB
    OAUTH_PROVIDERS = []
    AUTH_USER_REGISTRATION = False

    FEATURE_FLAGS = {
      "ALLOW_FULL_CSV_EXPORT": True,
      "ALERT_REPORTS": True,
      "DASHBOARD_RBAC": True,
      "ENABLE_TEMPLATE_PROCESSING" : True,
    }
    SQL_MAX_ROW = 300000
    ROW_LIMIT = 300000

    SESSION_COOKIE_SAMESITE = 'Lax'
    SESSION_COOKIE_HTTPONLY = True
    SESSION_COOKIE_SECURE = False
    TALISMAN_ENABLED = False
    PUBLIC_ROLE_LIKE = "Gamma"

supersetNode:
  env:
    SUPERSET_APP_ROOT: /analytics
    KEYCLOAK_BASE_URL: http://keycloak.dev.svc.cluster.local
  livenessProbe:
    httpGet:
      path: /analytics/health
  readinessProbe:
    httpGet:
      path: /analytics/health
  startupProbe:
    httpGet:
      path: /analytics/health

configOverridesFiles:
  custom_security_manager: files/custom_security_manager.py
  custom_jwt_middleware: files/custom_jwt_middleware.py
  custom_app_initialyzer: files/custom_app_initialyzer.py

clickhouse_connect_uri: host

extraConfigs:
  import_datasources.yaml: |
    {{ tpl (.Files.Get "files/import_datasources.yaml") . }}

Workaround with extra Ingress

``` apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: superset-fix namespace: {{ .Release.Namespace }} annotations: meta.helm.sh/release-name: superset-fix meta.helm.sh/release-namespace: {{ .Release.Namespace }} nginx.ingress.kubernetes.io/proxy-body-size: 100M nginx.ingress.kubernetes.io/proxy-connect-timeout: '300' nginx.ingress.kubernetes.io/proxy-read-timeout: '300' nginx.ingress.kubernetes.io/proxy-send-timeout: '300' nginx.ingress.kubernetes.io/rewrite-target: /analytics/$2 nginx.ingress.kubernetes.io/use-regex: 'true' spec: ingressClassName: nginx rules: - host: hostname http: paths: - path: /analytics/analytics(/|$)(.*) pathType: ImplementationSpecific backend: service: name: superset port: number: 8088


apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: superset-userinfo-redirect namespace: {{ .Release.Namespace }} annotations: nginx.ingress.kubernetes.io/use-regex: "true" # Permanent redirect to /analytics/user_info/ nginx.ingress.kubernetes.io/permanent-redirect: "https://hostname/analytics/user_info/" spec: ingressClassName: nginx rules: - host: hostname http: paths: # Only match /user_info - path: "/user_info(/|$)" pathType: ImplementationSpecific backend: service: name: superset port: number: 8088 ```

Checklist

  • [x] I have searched Superset docs and Slack and didn't find a solution to my problem.
  • [x] I have searched the GitHub issue tracker and didn't find a similar bug report.
  • [x] I have checked Superset's logs for errors and if I found a relevant Python stacktrace, I included it here as text in the "additional context" section.

Comment From: dosubot[bot]

Hi @Nurshinov! I'm Dosu and I’m helping the superset team.

Recent changes in Superset's frontend and backend have addressed issues with incorrect URL generation when using a custom SUPERSET_APP_ROOT. The frontend now consistently includes the appRoot prefix in endpoint URLs, preventing duplicate or missing prefixes for actions like login redirects and API calls (PR #34395). The bootstrap data handling was also improved to ensure application_root and static_assets_prefix are always set and normalized (PR #34680).

On the backend, setting SUPERSET_APP_ROOT triggers middleware that adjusts routing and automatically sets STATIC_ASSETS_PREFIX and APPLICATION_ROOT if not explicitly configured (app.py). For deployments under a subpath, it's important to set ENABLE_PROXY_FIX=True and configure your proxy (e.g., nginx) to set the X-Forwarded-Prefix header and route both static assets and API endpoints correctly (issue #34773).

If you're running the latest-dev branch, these fixes should be included. Double-check your environment variables and proxy configuration to ensure STATIC_ASSETS_PREFIX and APPLICATION_ROOT match your SUPERSET_APP_ROOT, and that your ingress/proxy sets X-Forwarded-Prefix. If you still see issues, make sure your frontend build is up-to-date and not caching old bootstrap data.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Chat with Dosu Join Discord Share on X

Comment From: Nurshinov

@dosu No, it still doesn't work even with:

ENABLE_PROXY_FIX=True
STATIC_ASSETS_PREFIX: /analytics
APPLICATION_ROOT: /analytics

and X-Forwarded-Prefix

Comment From: dosubot[bot]

Your config matches the documented approach, but there are still known edge cases and ongoing fixes for subpath deployments. Here are some key points and troubleshooting steps:

  • If you set both STATIC_ASSETS_PREFIX and APPLICATION_ROOT manually, and also set SUPERSET_APP_ROOT, you can get double prefixing (e.g., /analytics/analytics/...) if the values are not aligned. Try setting only SUPERSET_APP_ROOT and let Superset auto-populate the others, or ensure all three are set to exactly /analytics (no trailing slash) and not /analytics/ or /analytics/analytics.
  • The order of middleware matters: AppRootMiddleware must run before ProxyFix for correct SCRIPT_NAME/path handling. This is handled in the default code, but custom app initializers or WSGI wrappers can break it.
  • Hardcoded endpoints or static files (like /user_info/ or /static/assets/...) that don't use the bootstrapped prefix will miss the prefix unless rewritten at the proxy/ingress layer. Some open PRs are still fixing these cases (example, example).
  • The frontend must receive the correct application_root and static_assets_prefix in the bootstrap data. If the frontend is caching old bootstrap data or assets, you can get inconsistent URLs—try a hard reload or clear your browser cache.
  • This feature is still marked as BETA and some edge cases (like Swagger UI, OAuth/LDAP login, or certain links) may not fully respect SUPERSET_APP_ROOT yet (see open PRs).

If you still see issues, check the actual bootstrap data in your browser's dev tools (look for application_root and static_assets_prefix in the main page's JSON payload) and confirm they match your config. Also, verify that your ingress/proxy is not rewriting or stripping prefixes unexpectedly.

If you want to try the most robust config, set only SUPERSET_APP_ROOT=/analytics and ENABLE_PROXY_FIX=True, and let Superset auto-fill the other values. Make sure your proxy sets X-Forwarded-Prefix: /analytics and does not rewrite paths in a way that could cause double prefixing.

References:
- Backend config and middleware order
- Frontend fixes for appRoot in URLs
- Known edge cases and open PRs
- Official docs (feature is BETA)

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Chat with Dosu Join Discord Share on X

Comment From: msyavuz

I think the work for this was done by @martyngigg so maybe they have an input on this?