Deploying a Vite App Under a Subdirectory: An Architecture Note on `base`
A wrong `base` leaves HTML loading while CSS and JS 404. Learn how Vite's `base` option rewrites asset URLs for subpath deployments, what it does not secure, and the proxy checks that keep client-side routing working.
20 Nov 2025, 22:24 UTC

Problem: assets 404 under a subpath
Deploying a Vite application to https://example.com/app/ often produces a page that loads but is unstyled and inert. The HTML shell returns 200, while JavaScript and CSS requests return 404. The built output still points to root-absolute paths such as /assets/index-a1b2c3.js. Vite's base build option is the supported way to fix this: it sets the public URL prefix for production assets, and Vite rewrites references in HTML, JavaScript, and CSS during vite build.
Requirements for a non-root deployment
- A static host or reverse proxy that serves the application under a subdirectory.
- A fallback route that returns
index.htmlfor client-side routes under that subdirectory. - Development-server parity, so live updates and asset requests use the same prefix as production.
- Application code that does not hardcode absolute paths like
/apior/static.
Smallest suitable design
Set base in the Vite configuration file to the deployment path, including a trailing slash.
// vite.config.js
import { defineConfig } from 'vite'
export default defineConfig({
base: '/app/'
})
Run the production build from the project root with standard user permissions:
npm run build
Vite rewrites entry and asset URLs in dist/index.html and in the emitted JavaScript and CSS. Dynamic imports and import.meta.url-based asset URLs are resolved relative to the configured base.
For development, the dev server honors base from the config file or from the command line:
npx vite --base /app/
This makes the browser request /app/@vite/client instead of /@vite/client, matching production behavior.
The web server or reverse proxy must map the subpath to the dist directory and return index.html for any route under /app/ that does not match a static file. Editing server configuration requires administrative access to the host. A typical Nginx rule looks like this, though exact syntax depends on the server version and layout:
location /app/ {
alias /srv/apps/myapp/dist/;
try_files $uri $uri/ /app/index.html;
}
If the host does not perform this fallback, a browser refresh on /app/settings returns 404 even though the application itself handles the route.
Trust and data boundaries
base is a URL prefix only. It does not authenticate requests, enforce origin boundaries, or restrict which paths a user may access. Do not rely on it to prevent path traversal; validate any user-supplied path separately at the edge or origin.
Hardcoded absolute paths in application code break under base. A fetch to /api/users still targets the domain root, not /app/api/users. Use relative paths, import.meta.env.BASE_URL, or runtime configuration instead.
In library mode (build.lib), consumers host the bundle themselves. base does not control their entry-point URL, so it is not a substitute for documenting how the library should be served.
Operational checks
- Inspect the build output. After
vite build, opendist/index.htmland confirm thatscriptandlinktags use the/app/prefix. - Serve from the matching subdirectory. Place
distinside a directory namedappunder a local web root, or configure a local reverse proxy. Verify that all JS and CSS requests return 200 and that the browser console shows no 404 errors. - Confirm the option controls output. Remove
baseor set it to/, rebuild, and confirm asset paths revert to root-relative. - Check dev parity. Run
vite --base /app/and confirm the browser loads/app/@vite/clientwithout 404.
Failure modes
A wrong base produces a partial failure: the HTML loads, but CSS and JS return 404. A status-page check against /app/ returns 200 and can miss the broken page entirely. Always verify the Network tab in a browser or an automated check that requests the asset URLs found in the built HTML.
Trailing-slash handling and default normalization can differ between Vite major versions. Confirm the exact behavior against the documentation for the installed version rather than assuming a missing or extra slash is harmless.
Client-side routing fails on refresh if the proxy returns 404 instead of index.html for deep paths. This is a server configuration problem, not a base problem, but the two are often confused.
Conditions that would change the design
- Multiple applications on one domain. Each app needs its own
baseand proxy mapping. Shared asset directories or global absolute paths require a different layout. - Server-side rendering or framework adapters. When an SSR framework injects asset tags,
basemay be less central or may need to align with framework-specific configuration. - Unknown deployment path at build time. A relative
basesuch as'./'makes asset URLs relative to the HTML document, which is useful when the final public path is not known duringvite build. - Library mode. As noted above, consumers control hosting, so
basedoes not govern their entry-point URL.
Limitations and verification
base rewrites what Vite controls during the build. It does not rewrite arbitrary strings in application source code, nor does it configure the web server. The reverse proxy or static host remains a separate concern.
The practical check is to open dist/index.html in a text editor and confirm every local src and href begins with the configured prefix. Then serve that directory under the same prefix and confirm the browser Network tab shows 200 for all JavaScript and CSS. If any asset 404s, the base value or the server path mapping is wrong.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.