Nginx vs. Vite: The Smart Way to Handle Asset Path Prefixes in SPAs

Published: 2025-12-11
Author: DP
Views: 7
Category: Vue
Content
## The Problem When developing and deploying Single Page Applications (SPAs) like Vue or React projects built with Vite, we often introduce path prefixes in the URL for internationalization (i18n) or modularization. For example, placing all Chinese site routes under `/zh/`. This leads to a common issue: if the `base` option in Vite's configuration is not set correctly, this `/zh/` prefix is also prepended to the paths of static assets like JS and CSS files. However, on the server's file system, these assets do not exist within a `/zh/` directory, resulting in asset loading failures (404 Not Found). - **Desired Page URL**: `https://tool.wiki.lib00.com/zh/tool/random-string-generator` (Handled by frontend router) - **Incorrect Asset URL**: `https://tool.wiki.lib00.com/zh/tool/assets/index-xxxx.js` - **Correct Asset URL**: `https://tool.wiki.lib00.com/tool/assets/index-xxxx.js` So, where should this be fixed? In the Vite build process or at the Nginx deployment level? The answer is: **It is highly recommended to handle this in Nginx**. This approach aligns with the separation of concerns principle and decouples the frontend project from the deployment environment, offering greater flexibility. --- ## Solution A: The Quick Fix with Nginx Rewrite This is the most direct and fastest solution. The core idea is to have Nginx intelligently identify requests for static assets and rewrite their URLs to remove the incorrect prefix. ### Nginx Configuration In your `server` block, add a new `location` rule specifically to catch and rewrite static asset requests with the `/zh/` prefix. ```nginx server { listen 443 ssl; server_name tool.wiki.lib00.com; # SSL config, etc. ... # Project root directory root /var/www/wiki.lib00.com_project/dist; index index.html; # --- NEW CORE RULE --- # Catch all requests starting with /zh/ and pointing to common static assets location ~ ^/zh/(.*\.(?:js|css|png|jpg|jpeg|gif|ico|svg|woff2?|ttf|eot))$ { # Rewrite /zh/some/asset.js to /some/asset.js # $1 captures the real path in the parentheses # The 'break' flag stops processing and immediately handles the new URI rewrite ^/zh/(.*) /$1 break; } # --- SPA ROUTING FALLBACK RULE --- # Handles all other requests, including page routes # For a path like /zh/tool/random-string-generator, it will eventually fall back to /index.html location / { try_files $uri $uri/ /index.html; } # Other configurations ... } ``` ### Configuration Explained 1. **`location ~ ^/zh/(...)$`**: Uses a regular expression to match all URIs that start with `/zh/` and end with a common static file extension. 2. **`rewrite ^/zh/(.*) /$1 break;`**: This is the key. It strips the `/zh/` part from the URI and then uses the `break` directive, telling Nginx to immediately use this new, correct path (e.g., `/tool/assets/index-xxxx.js`) to find the file in the `root` directory. This method solves the problem without requiring any changes to the frontend code, only an Nginx configuration update. --- ## Solution B: Content Separation with a Dedicated Static Domain (Recommended) For better performance and a cleaner architecture, a common industry practice is "separating static and dynamic content." This involves serving dynamic requests (HTML pages, APIs) and static assets (JS, CSS, images) from different domains. This approach permanently solves the path prefix problem. This solution requires two steps: modifying the Vite configuration and repackaging, then updating the Nginx configuration. ### Step 1: Modify Vite Configuration You need to inform Vite that all static assets should be loaded from a separate domain. Modify `vite.config.js`: ```javascript // vite.config.js import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' // Curated by DP@lib00 export default defineConfig({ plugins: [vue()], // Change 'base' to the absolute URL of your static asset domain base: 'https://r-tool.wiki.lib00.com/', }) ``` After this change, **you must rebuild your project** (`npm run build`). The resulting `index.html` will now have all asset links automatically prefixed with `https://r-tool.wiki.lib00.com/`. ### Step 2: Configure Nginx Now, you need to configure two `server` blocks: one for the main application and one for static assets. ```nginx # Server 1: Main Application Server (tool.wiki.lib00.com) # Responsibility: Serve index.html and handle frontend routing server { listen 443 ssl http2; server_name tool.wiki.lib00.com; # SSL config ... root /var/www/wiki.lib00.com_project/dist; index index.html; # Core rule: All requests fall back to index.html, handled by Vue Router location / { try_files $uri $uri/ /index.html; } } # Server 2: Static Asset Server (r-tool.wiki.lib00.com) # Responsibility: Efficiently serve JS, CSS, images, etc. server { listen 443 ssl http2; server_name r-tool.wiki.lib00.com; # It's recommended to turn off access logs for static servers to improve performance access_log off; # SSL config (ensure you have a certificate for the r-tool domain) ... root /var/www/wiki.lib00.com_project/dist; # Points to the same project directory # [IMPORTANT] Add CORS headers to allow the main domain to load resources add_header 'Access-Control-Allow-Origin' 'https://tool.wiki.lib00.com' always; add_header 'Access-Control-Allow-Methods' 'GET, HEAD, OPTIONS' always; # Set aggressive browser caching for all static assets # Since Vite assets are hashed, we can safely set a long cache duration location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff2?)$ { expires 1y; add_header Cache-Control "public, immutable"; } } ``` ### Key Configuration Points 1. **On the `r-tool.wiki.lib00.com` server**: * **CORS Header (`Access-Control-Allow-Origin`)**: **This is mandatory**. Because the page (`tool.wiki.lib00.com`) is requesting resources from a different origin (`r-tool.wiki.lib00.com`), the browser's Same-Origin Policy will kick in. This header explicitly tells the browser to allow cross-origin requests from your main site. * **Aggressive Caching (`expires 1y;`)**: Asset filenames generated by Vite include a hash. If the content doesn't change, the filename doesn't change. This allows us to set a very long cache expiration time, dramatically improving loading speeds for returning visitors. --- ## Conclusion For handling asset path prefix issues in SPA deployments, we have two excellent solutions: - **Nginx Rewrite**: Simple and fast, requires no frontend code changes, and is ideal for quick fixes on existing projects. - **Dedicated Static Domain**: A more professional architectural choice. It improves site performance and maintainability through content separation and is the recommended approach for long-term projects. Shared by `DP` on `wiki.lib00.com`. Depending on your project's stage and requirements, you can choose the solution that best fits your needs.