Open Closed

React: Admin Console cannot be hosted under a sub-path: `<base href>` and asset URLs are baked root-absolute and ignore `Request.PathBase` #10823


User avatar
0
severances@gc.adventist.org created
  • ABP Framework version: v10.5.0 (Volo.Abp.AdminConsole 10.5.0)
  • UI Type: React
  • Database System: EF Core (SQL Server)
  • Tiered (for MVC) or Auth Server Separated (for Angular): no — single host (API + OpenIddict auth server)
  • Exception message and full stack trace: none. There is no exception; the failure is silent. The Admin Console returns HTTP 200 and renders a blank page, with 404s for its assets in the browser network log.
  • Steps to reproduce the issue: below.

Summary

When the host runs under a non-root PathBase, the Admin Console routes correctly but serves a shell whose <base href> and asset URLs omit the PathBase. The browser therefore requests the assets from the site root, they 404, and the console renders as a blank page.

We hit this deploying to an IIS virtual application, where IIS sets Request.PathBase at the server layer. It applies equally to any reverse-proxy sub-path deployment.

Steps to reproduce

  1. Create an ABP 10.5.0 React solution whose host references AbpAdminConsoleModule, with AdminConsole:IsEnabled = true.
  2. In Program.cs, add app.UsePathBase("/sub"); immediately before await app.InitializeApplicationAsync();. This puts the PathBase in the same position IIS in-process hosting does.
  3. Run the host and request GET /sub/admin-console.
  4. Observe HTTP 200, then inspect the response body: its <base href> is /admin-console/, and every asset URL is root-absolute, none carrying /sub.
  5. Load /sub/admin-console in a browser: the page is blank and every asset request 404s.

Deploying the same host as an IIS virtual application at /sub (in-process) reproduces it with no code change at all.

The defect can also be confirmed without deploying anything, because the shell is a static file shipped in the package. This prints it in full:

$dll = "$env:USERPROFILE\.nuget\packages\volo.abp.adminconsole\10.5.0\lib\net10.0\Volo.Abp.AdminConsole.dll"
$asm = [System.Reflection.Assembly]::LoadFrom($dll)
$s = New-Object System.IO.StreamReader(
    $asm.GetManifestResourceStream('Volo.Abp.AdminConsole.wwwroot.admin_console.index.html'))
$s.ReadToEnd()

Expected behaviour

The Admin Console honours Request.PathBase in its <base href> and asset URLs, so it works when the host is deployed under a virtual application or reverse-proxy sub-path. Under a PathBase of /sub, the shell's <base href> should be /sub/admin-console/, with asset URLs that resolve correctly beneath it.

Actual behaviour

<base href> and all asset URLs are emitted root-absolute, so the Admin Console is unusable under any non-root PathBase.

Root cause

The shell is not rendered at request time. It is a static file embedded in the shipped assemblyVolo.Abp.AdminConsole.wwwroot.admin_console.index.html, 948 bytes, registered through ABP's Virtual File System and served by AdminConsoleSpaMiddleware — with /admin-console/ written into it as a literal at your build time.

I would have pasted that file here, but this forum's security filter rejects posts carrying a page's worth of markup, so here it is transcribed instead. The PowerShell above prints the byte-exact original if you want to check the transcription. Its <head> holds exactly these eight references, in this order — tag, then attributes, then URL:

| # | tag | attributes | URL | | --- | --- | ---------- | --- | | 1 | base | — | /admin-console/ | | 2 | link | rel="icon" type="image/svg+xml" | /admin-console/favicon.svg | | 3 | script | type="module" crossorigin | /admin-console/assets/index-CVgBU-QA.js | | 4 | link | rel="modulepreload" crossorigin | /admin-console/assets/rolldown-runtime-S-ySWqyJ.js | | 5 | link | rel="modulepreload" crossorigin | /admin-console/assets/vendor-query-Drt5MjtQ.js | | 6 | link | rel="modulepreload" crossorigin | /admin-console/assets/vendor-react-Deby2yrZ.js | | 7 | link | rel="modulepreload" crossorigin | /admin-console/assets/vendor-router-DR8po1Y7.js | | 8 | link | rel="stylesheet" crossorigin | /admin-console/assets/index-C6allXm8.css |

Besides those, the <head> carries only charset and viewport meta tags and the title Admin Console; the <body> is a single empty div with id="root". Nothing else.

So: eight root-absolute, PathBase-free URLs — the <base href>, the favicon, the entry script, four modulepreload links, and the stylesheet.

Because nothing renders this file per request, no runtime configuration can correct it — which is why the options below do not help.

For contrast, Swagger UI in the same host is unaffected under the same PathBase, because it emits relative references (./swagger-ui.css, ./swagger-ui-bundle.js) that resolve against the current URL.

Configuration we checked first

AbpAdminConsoleOptions declares twelve properties, read directly from the shipped assembly's metadata:

IsEnabled, RedirectRootToAdminConsole, Authority, AuthServerApplicationUrl, ClientId, Scope, ThemeOverrideCssPath, ApplicationName, LogoUrl, InitialTheme, CustomizationPermissionName, LocalizationLanguages.

None of them is a base path or route prefix, and no other type in the assembly declares one. We also found nothing in the Admin Console documentation describing sub-path or reverse-proxy hosting.

Other workarounds considered, and why they are inadequate

  • A server-level URL rewrite mapping /admin-console/* into the virtual application rescues exactly one deployment. Two environments of the same application on one site — for us, Staging and Demo — collide on the same root-level prefix, so it cannot work in general.
  • Patching the packaged SPA is not viable: the paths are baked into the published assets inside the NuGet package.

What we are doing instead

We wrote response-rewriting middleware in our own host that prefixes every root-absolute href/src in the shell with Request.PathBase. It works, verified against 10.5.0, but it is a workaround rather than a fix: we are rewriting your HTML on the way out, and it will break silently if the shell is restructured. We would prefer to delete it.

Suggested fix

Because the shell is pre-built, the fix belongs at build or serve time. Two changes would do it, and they compose:

  1. Build the Admin Console bundle with a relative base, so emitted asset references are relative (assets/index-*.js) rather than root-absolute (/admin-console/assets/index-*.js). They then resolve against whatever <base href> is in effect, collapsing eight URLs into one value that has to be correct.
  2. Substitute <base href> when the file is served. With (1) in place this is the only request-dependent value left, and AdminConsoleSpaMiddleware already owns the response — a placeholder token replaced on the way out, or a cached transformed copy, would be enough. The SPA router's basename can then be read from document.baseURI instead of being fixed at build time.

Either half alone is an improvement: (1) makes the problem tractable for consumers who can set a base, and (2) fixes it outright.

One other thing

The UI section of this form's question details form doesn't include React as an option.

Markdown supported.
Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)

3 Answer(s)
  • User Avatar
    0
    AI-Bot created
    Support Team ABP Agent
    • In ABP 10.5.0, the Admin Console SPA is shipped as pre-built static assets (embedded via the Virtual File System and served by AdminConsoleSpaMiddleware) and is hosted under /admin-console/*. Because the shipped index.html contains root-absolute URLs (e.g. href="/admin-console/...", src="/admin-console/...") they won’t automatically include Request.PathBase when your app is hosted under a sub-path (IIS virtual application, reverse proxy path prefix, app.UsePathBase("/sub"), etc.). Your analysis of the failure mode is correct.

    • There is no runtime option in AbpAdminConsoleOptions to set a base path / path base, so you cannot fix this purely with configuration.

    • Practical workaround (server-side, non-invasive to the NuGet package): keep your response-rewriting (or equivalent) but do it in a way that is tightly scoped and resilient:

      1. Only target the Admin Console shell request (the SPA fallback index.html), not all responses.
      2. Rewrite:
        • <base href="/admin-console/"> to <base href="{Request.PathBase}/admin-console/">
        • any href="/admin-console/... / src="/admin-console/... to be prefixed with Request.PathBase
      3. Cache the rewritten HTML per PathBase value (usually there’s only one in a deployment).

      This is the only host-level workaround that keeps using the packaged SPA as-is.

    • If you can change deployment topology: the other workaround is to ensure the reverse proxy/IIS doesn’t mount the app under a PathBase (i.e., make the app the site root), but you already explained why that’s often not possible.

    • Suggested product fix (what you proposed is aligned with the underlying issue):

      • Build the Admin Console bundle so assets are relative (or otherwise PathBase-aware).
      • Or have AdminConsoleSpaMiddleware inject PathBase at response time (e.g., placeholder token replacement for the <base href> and/or asset URLs).

    No verified source available that documents a built-in setting or officially supported approach for hosting the embedded Admin Console under a non-root PathBase in 10.5.x.

    • https://abp.io/docs/10.5/framework/ui/react/admin-console

    This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    severances@gc.adventist.org created

    This is a bug report, not a request for a workaround. It seems that while PathBase is a supported configuration, ABP never tests with PathBase set to anything other than root. In a previous Blazor Server project, I ended up filing multiple PathBase bug reports, which were ultimately fixed. I can't control the infrastructure my projects have to work with; creating new domains or subdomains for each project would require months of approval bureaucracy and slow work to a crawl, leaving my only option to install in a subdirectory of an existing domain.

    My recommendation is that for each supported frontend type ABP add some tests with PathBase set to a subdirectory which exercise URL generation across all UI surfaces.

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Hi,

    You're right, this is a bug. The Admin Console shell is pre-built with root-absolute /admin-console/... URLs and the middleware serves it as-is, so it can't work under a non-root PathBase, and there is no configuration in 10.5.0 that can change it.

    Your response-rewriting middleware is a reasonable interim solution, but it covers the shell only. The SPA bundle also resolves the default API base to the root-absolute /api, and its router basename is fixed at build time. With app.UsePathBase("/sub") locally this doesn't show up, because requests without the /sub prefix are still answered by the same app — but in a real IIS virtual application, the module API calls (Identity, Settings, etc.) go to the site root. Worth double-checking those pages in your Staging/Demo deployments.

    We're preparing a fix along the lines you suggested: the middleware will prefix the <base href> and asset URLs with Request.PathBase when serving the shell, the config endpoint will return a PathBase-aware authority and application root, and the SPA will derive its router basename and API base at runtime instead of baking them in at build time. Once it's released you can delete your middleware; we'll post the version here when it's confirmed.

    Your ticket has been refunded. And thanks for the note about the missing React option in the question form — we'll forward it to the team.

    Thanks

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v10.8.0-preview. Updated on September 16, 2026, 14:50
1
ABP Assistant
🔐 You need to be logged in to use the chatbot. Please log in first.