[SIP] Allow to deploy Superset under the prefix of an URL

Motivation

We faced a problem in the deployment of the Superset under the prefix like https://{domain}/superset. We can not use subdomains. Configuring a gateway (like nginx) didn't work since there were problems with redirects. After a lot of efforts we decided to refactor the Superset. Furthermore, we saw many requests and questions about how to deploy Superset under the prefix without any working solutions.

Proposed Change

I refactored a backend code by replacing simple string urls with the url_for function on the backend, or with the APPLICATION_ROOT variable in places with no app context. I added a function withPrefix on the frontend and use it for all the urls, generated on the frontend.

New or Changed Public Interfaces

There are no changes in the public interfaces

New dependencies

No new dependencies

Migration Plan and Compatibility

No migrations

Rejected Alternatives

Configuration of gateways is not enough.

Configuration

To use deployment with the prefix you just need to add the following variables into the Superset config

ENABLE_PROXY_FIX = True
STATIC_ASSETS_PREFIX = "/superset"
APPLICATION_ROOT = "/superset"

and to configure the gateway with this minimal configuration (nginx syntax)

location /superset/ {
    proxy_pass         http://{superset_host}:{superset_port}/;
    proxy_set_header   X-Forwarded-Prefix /superset/;
}

Be noticed that the trailing slashes are important for the nginx configuration

Comment From: sfirke

People in the Slack chat often ask how to deploy like this, so I can confirm there is community demand for the feature to come out-of-the-box. At the very least, this SIP should result in adding to the docs (though I hope it can result in implementing the feature).

Comment From: sanasreenivasulu

is the change complete, how to test

Comment From: sfirke

The linked pull request is a draft so no this has not been implemented. @vadym-serdiuk do you need any support/contributions from committers? There is now a recurring SIP call to discuss ideas like this and get them across the finish line

Comment From: vadym-serdiuk

@sfirke Hi, I completed refactoring all the places in the code (FE, BE) where urls were created directly without ablity to use prefixes. Currently, we are able to build a docker image and embed it in our solution. However, there is one moment that needs to be solved before it can be used widely. The variable ASSET_BASE_URL used inside the webpack.config.js should be set on the building stage. I can't find a solution for avoiding this or having a workaround. So everyone who wants to use a prefix needs to build his own docker image.

Comment From: sanasreenivasulu

Hi. I hard code the ASSET_BASE_URL="/data" in the docker file and built the image, noticed it breaks all the CSS, images and js and not able to login in to the superset and throws a 302 error code its redirects back to the login page

Thanks Sana

On Tue, Feb 20, 2024 at 7:33 AM vadym-serdiuk @.***> wrote:

@sfirke https://github.com/sfirke Hi, I completed refactoring all the places in the code (FE, BE) where urls were created directly without ablity to use prefixes. Currently we are able to build a docker image and embed it in our solution. However, there is one moment that need to be solved before it can be used widely. The variable ASSET_BASE_URL used inside the webpack.config.js should be set on the building stage. I can't find a solution for avoiding this or having a workaround. So everyone who wants to use a prefix need to build his own docker image.

— Reply to this email directly, view it on GitHub https://github.com/apache/superset/issues/26319#issuecomment-1953370889, or unsubscribe https://github.com/notifications/unsubscribe-auth/AMAZM5N5INY2VC4XZSYNDTDYUP75LAVCNFSM6AAAAABA462M2CVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTSNJTGM3TAOBYHE . You are receiving this because you are subscribed to this thread.Message ID: @.***>

Comment From: vadym-serdiuk

@sanasreenivasulu What is your nginx config?

Comment From: sanasreenivasulu

Hi,

Attached Dockerfile and nginx.conf and superset_config.py

On Tue, Feb 20, 2024 at 7:54 AM vadym-serdiuk @.***> wrote:

@sanasreenivasulu https://github.com/sanasreenivasulu What is your nginx config?

— Reply to this email directly, view it on GitHub https://github.com/apache/superset/issues/26319#issuecomment-1953384814, or unsubscribe https://github.com/notifications/unsubscribe-auth/AMAZM5JWIGRE6QZPYQCWSKLYUQCN3AVCNFSM6AAAAABA462M2CVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTSNJTGM4DIOBRGQ . You are receiving this because you were mentioned.Message ID: @.***>

Comment From: sanasreenivasulu

Hi, STATiC_ASSETS_PREFIX change is required in the superset_config.py file

STATIC_ASSETS_PREFIX = "/data"

On Tue, Feb 20, 2024 at 8:06 AM Sana Sreenivasulu ***@***.*** wrote:

Hi,

Attached Dockerfile and nginx.conf and superset_config.py

On Tue, Feb 20, 2024 at 7:54 AM vadym-serdiuk @.***> wrote:

@sanasreenivasulu https://github.com/sanasreenivasulu What is your nginx config?

— Reply to this email directly, view it on GitHub https://github.com/apache/superset/issues/26319#issuecomment-1953384814, or unsubscribe https://github.com/notifications/unsubscribe-auth/AMAZM5JWIGRE6QZPYQCWSKLYUQCN3AVCNFSM6AAAAABA462M2CVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTSNJTGM4DIOBRGQ . You are receiving this because you were mentioned.Message ID: @.***>

Comment From: vadym-serdiuk

@sanasreenivasulu I don't see your attachment

Comment From: sanasreenivasulu

Hi, Let me resend again

On Tue, Feb 20, 2024 at 8:38 AM vadym-serdiuk @.***> wrote:

@sanasreenivasulu https://github.com/sanasreenivasulu I don't see your attachment

— Reply to this email directly, view it on GitHub https://github.com/apache/superset/issues/26319#issuecomment-1953413760, or unsubscribe https://github.com/notifications/unsubscribe-auth/AMAZM5O2ALL3HM5L5FINWA3YUQHT7AVCNFSM6AAAAABA462M2CVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTSNJTGQYTGNZWGA . You are receiving this because you were mentioned.Message ID: @.***>

Comment From: sfirke

@sanasreenivasulu it looks like you are replying via email and the attachments are not coming through. Maybe copy-paste the relevant portions of those files into a Github post instead with proper code formatting?

Comment From: sanasreenivasulu

prefix_files.zip attached all the files in the zip

Comment From: sanasreenivasulu

there is issue with basic.html file

Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self' 'strict-dynamic' 'nonce-PJiqKv1fUV-BDW0GNIyi9khxS9Ic8Ow3'". Either the 'unsafe-inline' keyword, a hash ('sha256-2lnlRuPtBZrqhSGBsKWUf6tH7/uHIa/CHSTRdhivnH8='), or a nonce ('nonce-...') is required to enable inline execution.

Comment From: rameshreddy89

@sfirke Hi, I completed refactoring all the places in the code (FE, BE) where urls were created directly without ablity to use prefixes. Currently, we are able to build a docker image and embed it in our solution. However, there is one moment that needs to be solved before it can be used widely. The variable ASSET_BASE_URL used inside the webpack.config.js should be set on the building stage. I can't find a solution for avoiding this or having a workaround. So everyone who wants to use a prefix needs to build his own docker image.

@vadym-serdiuk is it ok if I use hard coded value in webpack.config.js and use that hard coded path in nginx like below ? I have tested it and it works.

webpack.config.js

publicPath: /client/static/assets/,

nginx.conf

location /client/ { proxy_pass http://superset_app/; }

Comment From: vadym-serdiuk

prefix_files.zip attached all the files in the zip

@sanasreenivasulu I don't see your nginx config

Comment From: vadym-serdiuk

@rameshreddy89

Of cause you can

Comment From: sanasreenivasulu

@vadym-serdiuk added the nginx.config to the below attached zip file prefix_files.zip

Comment From: vadym-serdiuk

@sanasreenivasulu I don't see anything that can explain your problem. Be sure, that the correct superset_config.py is used by your superset. Check the actual request/response, may be you'll see something.

Comment From: nfalco79

highly requested feature. Solving the problem via NGIX configurations is not an option when embedding dashboards in k8s environment where the superset is exposed via a subpath https://acme.com/superser/embedded/.... to avoid CORS issues

Comment From: rusackas

If anyone wants to move forward with this SIP officially, please take the next step int he process by starting a [DISCUSS] thread on the dev@ mailing list.

Comment From: rusackas

If anyone wants to steward this SIP and move it forward, please do. If nobody does, it will be closed as dismissed/inactive at some point in the near future.

Comment From: rusackas

Closing as discarded (stale) since it hasn't been put up for discussion after 6 months. See relevant passed SIP

If you want to rekindle this, just open the discussion, and we can re-open this Issue and move it on the board.

Comment From: martyngigg

Hi,

As mentioned on the dev mailing list I'm interested in reviving this. I have a draft PR with some changes across the codebase that are working under the circumstances that I have.

I don't know if this approach is acceptable or there would be a better way to implement this behaviour? The downside is that it requires a rebuild of the frontend assets but I'm having difficulty seeing a way around this. Can this issue be reopened to continue the discussion on this?

Thanks, Martyn

Comment From: rusackas

Anyone want to put this up for Discussion on the Dev mailing list so it can officially be carried forward? Anyone is welcome to do so.

Comment From: mistercrunch

I think this SIP is missing a lot of details on how we might want to centralize routes/routing and maybe create more cohesion between how the backend/frontend agree on a a list of routes. I don't think wrapping all urls on the frontend withwithPrefix goes deep enough. Some things the SIP could explore:

  • on the frontend, do we need a file centralizing routes? routes.ts
  • do we need a router/handler, methods helping around processing routes (handling Rison, sanitizing, handling querystring params, ...)
  • interactions/interplay with React router
  • interactions with the Superset [api] Client
  • feels we have other url-utils places (?), should we centralize all existing url/pathing logic in a single module?

Comment From: mistercrunch

Used GPT here to try and structure the thoughts into something more ambitious that could superseed this SIP:

SIP Proposal: Enhanced Route Management and URL Handling in Superset

Abstract

This SIP proposes a comprehensive strategy to enhance and centralize route management and URL handling in Superset. While the initial work to enable URL prefix support (APPLICATION_ROOT) is a step forward, this proposal addresses the need for deeper cohesion between the frontend and backend, security through centralized sanitization, and validation to enforce best practices. Additionally, it explores leveraging OpenAPI for route synchronization and introduces strategies for enforcing standards and ensuring consistent behavior across deployments.


Problem Statement

The current URL handling approach has the following challenges:

  1. Frontend/Backend Route Mismatch: Routes are defined independently, increasing the risk of mismatches.
  2. Decentralized Utilities: URL handling logic (e.g., Rison, querystrings, sanitization) is fragmented, leading to redundancy and inconsistency.
  3. Security Concerns: Lack of centralized sanitization opens potential security vulnerabilities, such as injection attacks from improperly processed URLs or query parameters.
  4. Deployment Validation: The application behaves differently when APPLICATION_ROOT (or similar) is enabled, but there’s no systematic way to validate this in CI/CD.
  5. OpenAPI Underutilization: OpenAPI is already used but could be leveraged to sync route definitions between frontend and backend.

Proposed Solution

1. Centralized Route and URL Handling

  • Introduce a routes.ts file as the single source of truth for frontend routes, with metadata including:
  • Route name, URL, and type (API, React Router).
  • Expected querystring or Rison parameters.
  • Relevant metadata, such as permissions or special handling rules.

  • Centralize all URL/pathing utilities into a single url-utils.ts module:

  • Handle Rison encoding/decoding with a reusable wrapper.
  • Simplify querystring operations (e.g., appending, sanitizing).
  • Include sanitization using libraries like dompurify to ensure safe URL handling and prevent injection attacks.

2. Leverage OpenAPI for Synchronization

  • Use OpenAPI as a source of truth for API routes, exposing metadata (e.g., parameters, expected responses) that can be consumed by both the frontend and backend.
  • Build a tool or script to generate a TypeScript file from OpenAPI specs to ensure route consistency between the backend and frontend. Maybe we look into Swagger CodeGen and similar utilities (?) Might be overkill but worth looking into.

3. Security Enhancements

  • Introduce a centralized sanitization strategy in url-utils.ts:
  • Centralize and guaranteed the use dompurify or similar libraries to sanitize all URLs and querystring parameters where needed.
  • Enforce sanitization by requiring all components to use the centralized utilities for route handling.

4. Standard Enforcement and Validation

  • Implement custom ESLint rules to enforce the use of centralized utilities (routes.ts and url-utils.ts) for all route-related operations.
  • Add test coverage to validate behavior with APPLICATION_ROOT (or equivalent prefix) enabled and disabled. This should include:
  • Running parts of the test suite with the prefix on and off.
  • Validating that routes and utilities behave consistently across configurations.

5. React Router and Navigation Improvements

  • Centralize navigation-related routes in routes.ts and integrate them with React Router.
  • Create a wrapper or handler for React Router that:
  • Dynamically generates routes from metadata in routes.ts.
  • Handles navigation logic (e.g., redirects, transitions).
  • Ensures consistent behavior with APPLICATION_ROOT.

Open Questions

  1. Frontend/Backend Synchronization:
  2. How do we best synchronize and share route metadata between the frontend and backend? Options include:
    • openapi-driver client solutions (looks like it's generally done through code gen)
    • Shared JSON/YAML schema?
    • Backend API endpoints that expose route metadata dynamically. (not big on that as it should be static enough)
  3. Testing
    • why test suite? e2d?
    • matrixify the whole thing to run the test suite with and without prefix?

Next Steps

  1. Define the structure of routes.ts and its metadata schema.
  2. Create a POC for synchronizing routes using OpenAPI.
  3. Audit the codebase for existing URL and path handling patterns, consolidating into url-utils.ts.
  4. Implement sanitization using dompurify or a similar library in url-utils.ts.
  5. Add ESLint rules and CI validation for route and utility usage.
  6. Refactor existing components to use the centralized route and URL handling modules.

Comment From: martyngigg

It certainly does feel like a code smell to have to remember to prefix all urls in the frontend.

In terms of the current changeset in #30134, it feels like the backend changes are relatively uncontroversial? Shall I extract those into a new pull request and then they could be merged while the further discussions take place on the frontend design?

Comment From: mistercrunch

Yes, seems https://github.com/apache/superset/pull/30134 is becoming a step in the right direction. Only concern there is around preventing people from adding new URLs without using the right wrapper, and having everything works (the app, CI, ...) but breaks when specifying an approot. Maybe modifying e2e test suite to run with an approot would be sufficient(?) Coverage isn't complete though. Another option is to label the feature as in beta for now until we can prevent regressions.

Comment From: ryuuc

For those who want to try deploying superset to k8s using the Helm chart before its official release, the following modifications are required:

  1. custom image tag, I found it from github ci https://github.com/apache/superset/actions/runs/14366622555/job/40281019265 , use the py311 version, since wsgiref.types does not exist on Python 3.10, there is a pr to fix https://github.com/apache/superset/pull/33072/files#diff-9689cb75a0ebe47604d59e0a54c904040cd60b45a283a87d0f8d77cac60751d8L21
image:
  repository: apachesuperset.docker.scarf.sh/apache/superset
  tag: 09b92e7-py311
  pullPolicy: IfNotPresent
  1. install the required dependencies. refer to https://github.com/apache/superset/discussions/31431, but need to use uv instead of pip
bootstrapScript: |
  #!/bin/bash

  # Install system-level dependencies
  apt-get update && apt-get install -y \
    python3-dev \
    default-libmysqlclient-dev \
    build-essential \
    pkg-config 

  # Install required Python packages
  uv pip install --native-tls \
    authlib \
    psycopg2-binary \
    mysqlclient

  # Create bootstrap file if it doesn't exist
  if [ ! -f ~/bootstrap ]; then
    echo "Running Superset with uid {{ .Values.runAsUser }}" > ~/bootstrap
  fi
  1. Add SUPERSET_APP_ROOT env variables, and update health check paths
supersetNode:
  env:
    SUPERSET_APP_ROOT: /your-path
  livenessProbe:
    httpGet:
      path: /your-path/health
  readinessProbe:
    httpGet:
      path: /your-path/health
  startupProbe:
    httpGet:
      path: /your-path/health

Comment From: wqbbs911

For those who want to try deploying superset to k8s using the Helm chart before its official release, the following modifications are required:

  1. custom image tag, I found it from github ci https://github.com/apache/superset/actions/runs/14366622555/job/40281019265 , use the py311 version, since wsgiref.types does not exist on Python 3.10, there is a pr to fix https://github.com/apache/superset/pull/33072/files#diff-9689cb75a0ebe47604d59e0a54c904040cd60b45a283a87d0f8d77cac60751d8L21

image: repository: apachesuperset.docker.scarf.sh/apache/superset tag: 09b92e7-py311 pullPolicy: IfNotPresent

  1. install the required dependencies. refer to ModuleNotFoundError: No module named 'psycopg2' during k8 installation #31431, but need to use uv instead of pip

``` bootstrapScript: | #!/bin/bash

# Install system-level dependencies apt-get update && apt-get install -y \ python3-dev \ default-libmysqlclient-dev \ build-essential \ pkg-config

# Install required Python packages uv pip install --native-tls \ authlib \ psycopg2-binary \ mysqlclient

# Create bootstrap file if it doesn't exist if [ ! -f ~/bootstrap ]; then echo "Running Superset with uid {{ .Values.runAsUser }}" > ~/bootstrap fi ```

  1. Add SUPERSET_APP_ROOT env variables, and update health check paths

supersetNode: env: SUPERSET_APP_ROOT: /your-path livenessProbe: httpGet: path: /your-path/health readinessProbe: httpGet: path: /your-path/health startupProbe: httpGet: path: /your-path/health

i tyied to use this configuration to deploy superset under the prefix /analytics, but it report error when installed by helm:

10.11.1.129 - - [24/Apr/2025:07:53:00 +0000] "GET /analytics/health HTTP/1.1" 404 0 "-" "kube-probe/1.26+"
2025-04-24 07:53:05,427:WARNING:superset.views.error_handling:HTTPException
Traceback (most recent call last):
  File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 1484, in full_dispatch_request
    rv = self.dispatch_request()
         ^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 1458, in dispatch_request
    self.raise_routing_exception(req)
  File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 1440, in raise_routing_exception
    raise request.routing_exception  # type: ignore
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/flask/ctx.py", line 353, in match_request
    result = self.url_adapter.match(return_rule=True)  # type: ignore
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/werkzeug/routing/map.py", line 629, in match
    raise NotFound() from None
werkzeug.exceptions.NotFound: 404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.

Image

BTW , i use image: 4.1.2-py311, if i used 09b92e7-py311 , it report error even it install mysqlclient:

Image

Image

Comment From: ryuuc

@wqbbs911 4.1.2-py311 was released 1 month ago, which didn't include the prefixed url feature. try to use the uv pip install command to install those packages

Image

Comment From: rusackas

Just noting that this is still in the DISCUSS phase. Anyone feel this has enough detail to carry through to Vote? If not, we should figure out how to improve it or it may need to be shot down if all the right details are not included.

Comment From: sfirke

It seems like #31034 implemented the backend already, per the comment:

In terms of the current changeset in https://github.com/apache/superset/pull/30134, it feels like the backend changes are relatively uncontroversial? Shall I extract those into a new pull request and then they could be merged while the further discussions take place on the frontend design?

That PR got merged and people anecdotally in the community seem to be using it (one comment).

Is there discussion still needed? If so, can someone articulate the outstanding questions?

Comment From: mistercrunch

I believe we now run our end-to-end test suite against this feature (with and without) but may not guarantee full coverage. As more people start using it in 5.0, we should see the feature stabilize quickly as issues will quickly get reported and addressed (and hopefully covered with e2e tests too).

Comment From: arashdalir

hi everyone! has anyone been able to start superset with the prefix now? i've been trying to get the prefix configured on superset 5.0.0, but it's not working. checking the code for v5.0.0 would also confirm that the newest version of app.py is not included in that version, although 5.0.0 is newer than the app.py in main repo. am I missing something here?

Comment From: mistercrunch

5.0.0 was "cut" back in February I believe, and stabilized in that branch there. I don't know the timeline of the prefix-url, but likely 6.0 would be the first official release with these features (?)