Fully Verified Tencent Cloud Account Deploy Vue Projects with Nginx Optimization on Tencent Cloud
Deploy Vue Projects with Nginx Optimization on Tencent Cloud
So you built a Vue app. It runs beautifully on your laptop, where everything is fast, the weather is perfect, and the only person you need to impress is… yourself. Then you upload it to Tencent Cloud and suddenly your browser is asking questions like: “Why is the initial load taking 8 eternities?” “Why does refreshing a route show a 404?” “Why do fonts look like they escaped a typo?”
Fear not. This guide shows how to deploy your Vue project on Tencent Cloud using Nginx, while optimizing it so your site loads quickly, routing behaves correctly, and visitors don’t immediately decide to go back to their cat videos.
Fully Verified Tencent Cloud Account What We’re Actually Building
A typical Vue production deployment is simple: compile the Vue app into static files (usually in a dist folder), then serve those files via Nginx. However, the “simple” part ends the moment you remember Vue is usually a single-page application (SPA). That means:
- Your app’s routes (like
/dashboard) are handled client-side. - When someone refreshes the page or directly visits a deep link, the server must still return
index.htmlinstead of a 404. - Performance matters: caching, compression, and smart headers can make a huge difference.
We’ll solve all that. Additionally, we’ll set you up with a practical, readable Nginx configuration tailored for Vue SPAs.
Prerequisites
Before we get dramatic with Nginx, make sure you have:
- A Tencent Cloud instance ready to run Nginx (CVM or similar).
- Node.js installed locally for building Vue (you build on your machine; Nginx serves on the server).
- A built Vue project (Vite or Vue CLI works).
- SSH access to your Tencent Cloud instance.
- Basic comfort with editing configuration files. (If not, don’t worry. We’ll keep it simple.)
Step 1: Build Your Vue App for Production
You want the production output that Nginx can serve as static assets. Depending on your toolchain:
If You’re Using Vue CLI
Run:
npm run build
This usually generates a dist folder.
If You’re Using Vite
Run:
Fully Verified Tencent Cloud Account npm run build
Vite also outputs to a dist folder by default.
Now you have static files. Your server should serve those files, aggressively and without complaining.
Step 2: Upload the Build Output to Tencent Cloud
Pick your preferred deployment method:
- SCP/SFTP upload of the
distdirectory - rsync for faster incremental updates
- Or build directly on the server (not recommended unless you really enjoy waiting for npm to finish like it’s a hobby)
Where should you place the files on the server?
Common choice: a directory like /var/www/your-app. You can adapt this path to match your setup.
Example:
/var/www/vue-app/dist (or just copy the contents of dist directly into /var/www/vue-app)
Just be consistent, because Nginx loves consistency the way cats love knocking things off tables.
Step 3: Install Nginx (If Needed)
If Nginx isn’t installed on your Tencent Cloud instance, install it using your OS package manager. Many common images already include Nginx, but not always.
Typical workflow (you may need to adjust for OS version):
- Install: apt/yum (depending on system)
- Start Nginx
- Enable on boot
- Open ports 80/443 in security group
Remember: Tencent Cloud security group rules must allow inbound traffic. Your config can be perfect, but if the firewall says “no,” your visitors will see an empty sky.
Step 4: Create an Nginx Server Block for Your Vue SPA
Here’s the key idea: Nginx must serve static assets normally, but for “unknown routes” (like /profile or /settings/account), it should return index.html.
That’s what makes Vue Router behave properly when the user refreshes a route.
Example Nginx Configuration (Recommended Starting Point)
Below is an example configuration you can adapt. It includes:
- SPA routing fallback to
index.html - Compression (gzip)
- Better caching for static assets
- Security-related headers (reasonable defaults)
- MIME types included via standard Nginx behavior
Put it in a file like /etc/nginx/conf.d/vue-app.conf or /etc/nginx/sites-available/... depending on your setup.
Fully Verified Tencent Cloud Account
server {
listen 80;
server_name your-domain.com;
root /var/www/vue-app;
index index.html;
# Access log (you can adjust or disable)
access_log /var/log/nginx/vue-app_access.log;
error_log /var/log/nginx/vue-app_error.log;
# 1) Compression
gzip on;
gzip_min_length 10240;
gzip_comp_level 6;
gzip_types
text/plain
text/css
application/json
application/javascript
application/x-javascript
text/javascript
application/xml
application/xml+rss
image/svg+xml;
gzip_vary on;
# 2) Cache strategy
# Cache immutable assets (Vue/Vite typically uses content hashes)
location ~* \.(?:css|js|map|woff|woff2|ttf|eot|svg|png|jpg|jpeg|gif|webp|ico)$ {
expires 365d;
add_header Cache-Control "public, max-age=31536000, immutable";
try_files $uri =404;
}
# 3) Prevent caching of HTML so updates take effect
location = /index.html {
expires -1;
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
# 4) SPA fallback: send index.html for routes
location / {
try_files $uri $uri/ /index.html;
}
# 5) Security headers (tune based on your needs)
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options SAMEORIGIN;
add_header Referrer-Policy strict-origin-when-cross-origin;
# Optional: HSTS if you enable HTTPS
# add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
}
Now, why do we use try_files $uri $uri/ /index.html?
- If the path matches a real file (like
/assets/app.abc123.js), serve it. - Fully Verified Tencent Cloud Account If not, return
/index.htmlso Vue Router can handle the route.
Fully Verified Tencent Cloud Account Without this, refreshing a client-side route often results in a 404 from Nginx. That’s the classic “works in navigation, breaks on refresh” trap.
Step 5: If You Use HTTPS (Strongly Recommended)
In production, HTTPS is not optional; it’s the bare minimum. With Tencent Cloud, you can use:
- Cloud Load Balancer with TLS termination
- Or install certificates directly on your instance (Let’s Encrypt or CA-issued)
If your load balancer handles TLS, your instance may still see traffic on port 80 internally. If you terminate TLS on the instance, you’ll have a separate server block for 443.
Either way, your users will thank you and your browser won’t throw a tantrum.
Quick Note About HSTS
HSTS makes the browser always use HTTPS for your domain. Add it only when you’re confident HTTPS is properly configured. Otherwise, you’ll be locked out like a door with a key you misplaced.
Step 6: Make Sure File Paths and Roots Are Correct
Nginx uses:
rootto point to the directory containing your built filesindexto serveindex.html
If your build output is in /var/www/vue-app/dist, but your config says root /var/www/vue-app;, Nginx will look for /var/www/vue-app/index.html and might find nothing.
Choose one of these options:
- Set
root /var/www/vue-app/dist; - Or copy contents of
distinto/var/www/vue-app
Most people choose the second option because it’s simpler: no nested dist confusion.
Step 7: Enable Nginx Optimizations (Beyond the Basics)
We already added gzip and caching. Now let’s go a bit further, still keeping it practical and readable.
Consider Brotli (If You Can)
Gzip is supported everywhere, but Brotli usually gives smaller compressed files. However, Brotli support often requires additional Nginx modules or configuration. On some environments, it’s straightforward; on others, it’s… “let’s see what breaks today.”
If your setup supports Brotli, it can be a nice upgrade. But don’t block your deployment over it. Performance is a marathon, not a midnight rescue mission.
Use a Dedicated Cache Header for Asset Types
For Vue/Vite, your JS/CSS files typically include content hashes in filenames (like app.3f1a9c.css). That means those files are immutable until you deploy new builds.
That’s why caching them for a long time is safe. But HTML should not be cached aggressively, because it changes when you deploy new builds.
The config above handles exactly that: long cache for assets, no cache for index.html.
Step 8: Add Correct Handling for Browser Refresh and Deep Links
Here’s the classic symptom:
- Clicking a link inside your SPA works.
- Refreshing that same link shows a 404.
That means Nginx is treating /your-route as a request for a real file, fails to find it, and returns 404.
The fix is the SPA fallback rule:
try_files $uri $uri/ /index.html;
That forces all “not-a-file” requests to return the SPA entry point.
Step 9: Validate Vue Router Base Path (Common Deployment Gotcha)
Vue Router often needs a correct base configuration when deploying under a subpath.
Examples:
- Your app is served at
https://example.com/(root). Usually no special base needed. - Your app is served at
https://example.com/myapp/(subpath). Then you must setbaseappropriately in the build tool and set router base behavior.
If your routes look correct but assets fail to load, you may have a base-path mismatch. Fixing it means aligning your build configuration and router base.
For Vite, look at base in vite.config.js. For Vue CLI, you might use publicPath. Match it to your actual deployment path.
Step 10: Security Headers Without Breaking Everything
Headers are useful, but they can also break weird edge cases. The config included safe-ish defaults:
X-Content-Type-Options nosniffreduces MIME sniffing.X-Frame-Options SAMEORIGINreduces clickjacking.Referrer-Policy strict-origin-when-cross-originimproves privacy.
If you use third-party widgets or need framing from other origins, you may need to adjust X-Frame-Options. Security is a balancing act—like riding a bicycle while balancing a laptop on your head. Possible, but carefully.
Step 11: Troubleshooting Like a Calm Adult
Even with the perfect config, you might hit issues. Here are common ones and what to check.
Problem: Routes Return 404 on Refresh
- Confirm you included
try_files $uri $uri/ /index.html;in thelocation /block. - Confirm
rootpoints to the directory containingindex.html. - Check if your Vue app uses history mode vs hash mode. The config above supports history mode well.
Problem: CSS/JS Loads Slowly
- Make sure caching headers are set for asset files.
- Ensure your build output filenames include hashes. If not, long caching may be unsafe.
- Check gzip settings. Confirm the browser receives compressed responses.
Problem: Some Assets 404 While Others Work
- Check
basepath configuration in your build. - Verify your
rootand file structure on the server. - Confirm you copied the entire
distcontents, not justindex.html.
Problem: Nginx Doesn’t Start After Changes
Don’t panic. Always test your config before restart:
nginx -t
Then restart:
systemctl restart nginx
If there’s a typo, Nginx will usually tell you exactly where it hurts. Treat that output like a doctor’s note, not like a mysterious fortune cookie.
Step 12: Performance Checklist (Quick Wins That Actually Matter)
Here’s a deployment checklist you can reuse every time you ship a Vue project.
- Build in production mode (
npm run build). - Fully Verified Tencent Cloud Account Serve the correct
distdirectory via Nginxroot. - Use SPA fallback:
try_files $uri $uri/ /index.html;. - Enable gzip for text and JS/CSS.
- Cache hashed assets with long TTL and
immutable. - Disable caching for
index.html. - Set reasonable security headers.
- Test deep links: open
/some-route, refresh it, and verify it loads. - Test mobile network simulation to see if your compression/caching helps.
Ready-to-Adapt “Production-ish” Nginx Config
If you want a slightly more complete config template, here’s a cleaner version that keeps the same core ideas. You can paste it, modify server_name, and adjust the root path.
server {
listen 80;
server_name your-domain.com;
root /var/www/vue-app;
index index.html;
access_log /var/log/nginx/vue-app_access.log;
error_log /var/log/nginx/vue-app_error.log;
# Compression
gzip on;
gzip_vary on;
gzip_min_length 10240;
gzip_comp_level 6;
gzip_types
text/plain
text/css
application/json
application/javascript
text/javascript
application/xml
application/xml+rss
image/svg+xml;
# Long-term caching for static assets
location ~* \.(?:css|js|map|woff|woff2|ttf|eot|svg|png|jpg|jpeg|gif|webp|ico)$ {
expires 365d;
add_header Cache-Control "public, max-age=31536000, immutable";
try_files $uri =404;
}
# Do not cache HTML
location = /index.html {
expires -1;
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
# SPA fallback
location / {
try_files $uri $uri/ /index.html;
}
# Helpful headers
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options SAMEORIGIN;
add_header Referrer-Policy strict-origin-when-cross-origin;
}
Bonus: What About the Backend?
This article focuses on deploying the Vue frontend. Usually you’ll also have an API backend (Node, Java, Go, etc.). In a real system, you might want Nginx to:
- Serve static frontend assets
- Proxy API calls to your backend at a different upstream
Example conceptually:
/api/requests go to backend (proxy)- Everything else goes to Vue static files (SPA)
If you want that, tell me what backend URL and paths you’re using, and I’ll tailor an Nginx config that doesn’t accidentally treat /api/login as a frontend route (Nginx can be very literal; it’s both a strength and a weakness).
Conclusion: Your Vue App, Now Faster and Less Confused
Deploying a Vue project on Tencent Cloud with Nginx doesn’t need to be painful. The main things you must get right are:
- Serve the correct built output directory
- Configure SPA fallback so deep links work on refresh
- Use caching and compression so assets load quickly
- Add a few reasonable headers for safety
Once those are in place, your site stops feeling like it’s loading through three time zones and a fog machine. Your users will notice. Your future self will also notice, which is the best kind of notification.
If you share your Vue build type (Vite or Vue CLI), router mode (history/hash), and your intended domain/path (root or subpath), I can customize the Nginx config precisely for your scenario.

