Skip to content

fix(app-root): restore legacy redirects for HEAD and the /superset app root#42015

Merged
sadpandajoe merged 1 commit into
apache:masterfrom
sadpandajoe:fix/legacy-redirect-head-and-app-root
Jul 15, 2026
Merged

fix(app-root): restore legacy redirects for HEAD and the /superset app root#42015
sadpandajoe merged 1 commit into
apache:masterfrom
sadpandajoe:fix/legacy-redirect-head-and-app-root

Conversation

@sadpandajoe

Copy link
Copy Markdown
Member

SUMMARY

Two bugs in the legacy /superset/* → canonical route redirect shim
(LegacyPrefixRedirectMiddleware), both of which break real deployments.

1. HEAD against any legacy route returns 410 Gone instead of redirecting.

LEGACY_REDIRECT_MAP spells only the methods each canonical route's @expose
decorator names explicitly, which never includes HEAD — Werkzeug registers
HEAD implicitly on every GET rule, so no route declares it. The middleware
compared the raw request method against that map, so every legacy HEAD request
fell into the wrong-method branch and got 410 Gone. That breaks link-checkers,
uptime monitors, and plain curl -I against any legacy URL.

HEAD is GET-without-a-body (RFC 9110 §9.3.2), so it is now folded to GET
before the method check. It tracks GET rather than being blanket-allowed:
HEAD against the one POST-only canonical (/log/) still correctly returns
410.

2. The shim is disabled entirely when APPLICATION_ROOT is /superset.

An _enabled guard turned the middleware off in that deployment, on the premise
that the legacy prefix and the app-root prefix being the same token would produce
an infinite 308 loop.

That premise was incorrect. The app-root strip runs before the legacy-prefix
check, so a canonical URL can never re-enter the redirect branch: inbound
/superset/welcome/ strips to /welcome/, which is not a legacy path, and passes
through untouched. No redirect, no loop. The guard's only actual effect was to
make legacy bookmarks under that root — which arrive carrying the prefix twice,
/superset/superset/dashboard/1/ — hard-404 rather than 308 to
/superset/dashboard/1/.

The guard is removed. In its place is a precise per-request check: if the computed
redirect target is byte-identical to the inbound path, pass through instead of
emitting a redirect that could not converge. No enumerated row can produce that
today (no canonical target begins with the legacy prefix, so the rewrite always
shortens the path) — it's a guard against a future map edit, not a live condition.

TESTING INSTRUCTIONS

pytest tests/unit_tests/middleware/test_legacy_prefix_redirect.py — 206 pass.

Both fixes were written test-first; the 58 new tests fail against the previous
middleware and pass against this one.

  • HEAD: new "Section A.0" parametrizes every GET-redirecting row over HEAD,
    with and without an app-root prefix, plus the SQL Lab deep-link special case and
    a /log/ control asserting HEAD against a POST-only canonical still 410s.
  • App root: new tests assert that under APPLICATION_ROOT=/superset a doubled
    legacy path 308s to the canonical one, a canonical path passes straight through
    (the no-loop invariant), and the bare app root is untouched. Four existing tests
    that pinned the buggy _enabled behavior were rewritten to pin the fixed
    behavior.

Manual: deploy with SUPERSET_APP_ROOT=/superset and confirm
curl -I <host>/superset/superset/dashboard/1/ returns 308 with
Location: /superset/dashboard/1/ (both bugs at once — it was 410 before,
and 404 on the followed target).

ADDITIONAL INFORMATION

  • Has associated issue:
  • Required feature flags:
  • Changes UI
  • Includes DB Migration (follow approval process in SIP-59)
    • Migration is atomic, supports rollback & is backwards-compatible
    • Confirm DB migration upgrade and downgrade tested
    • Runtime estimates and downtime expectations provided
  • Introduces new feature or API
  • Removes existing feature or API

@codecov

codecov Bot commented Jul 14, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 20.00000% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 65.05%. Comparing base (4b07d43) to head (6dcdc65).
⚠️ Report is 8 commits behind head on master.

Files with missing lines Patch % Lines
superset/middleware/legacy_prefix_redirect.py 20.00% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #42015      +/-   ##
==========================================
- Coverage   65.06%   65.05%   -0.01%     
==========================================
  Files        2747     2747              
  Lines      153806   153842      +36     
  Branches    35266    35270       +4     
==========================================
+ Hits       100072   100088      +16     
- Misses      51824    51845      +21     
+ Partials     1910     1909       -1     
Flag Coverage Δ
hive 39.05% <20.00%> (-0.01%) ⬇️
mysql 57.88% <20.00%> (-0.01%) ⬇️
postgres 57.94% <20.00%> (-0.01%) ⬇️
presto 41.03% <20.00%> (-0.01%) ⬇️
python 59.31% <20.00%> (-0.01%) ⬇️
sqlite 57.54% <20.00%> (-0.01%) ⬇️
unit 100.00% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

…p root

The legacy-prefix redirect shim had two gaps that broke it for real
deployments.

HEAD requests returned 410 Gone on every legacy route. LEGACY_REDIRECT_MAP
lists only the methods each canonical @expose decorator names explicitly,
which never includes HEAD (Werkzeug registers it implicitly on GET rules).
The method check compared the raw request method against that map, so every
HEAD probe — link-checkers, uptime monitors, `curl -I` — fell into the
wrong-method branch. Fold HEAD to GET before the check, so it tracks GET
without blanket-allowing it: HEAD against the one POST-only canonical still
returns 410.

Deployments with APPLICATION_ROOT=/superset disabled the shim entirely. The
guard assumed that when the app-root and legacy prefixes are the same token,
any redirect would self-loop. It cannot: the app-root strip runs before the
legacy-prefix check, so a canonical URL never re-enters the redirect branch.
A legacy bookmark in that deployment carries the prefix twice
(/superset/superset/dashboard/1/) and hard-404'd instead of 308ing to
/superset/dashboard/1/. Remove the guard, and add a precise per-request check
that passes through rather than emitting a redirect whose target equals the
inbound path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sadpandajoe
sadpandajoe force-pushed the fix/legacy-redirect-head-and-app-root branch from 648c28a to 6dcdc65 Compare July 14, 2026 18:27
@sadpandajoe
sadpandajoe marked this pull request as ready for review July 14, 2026 23:58
@bito-code-review

bito-code-review Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #48fbf3

Actionable Suggestions - 0
Review Details
  • Files reviewed - 2 · Commit Range: 6dcdc65..6dcdc65
    • superset/middleware/legacy_prefix_redirect.py
    • tests/unit_tests/middleware/test_legacy_prefix_redirect.py
  • Files skipped - 0
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@sadpandajoe
sadpandajoe merged commit c6da740 into apache:master Jul 15, 2026
93 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants