Part 2: Caching Strategy for Vue + Rails API with Nginx

In Part 1, we explored the request flow between Nginx, Vue (frontend), and Rails (API backend). We also covered how Nginx routes traffic and why caching matters in such a setup.

Now in Part 2, we’ll go deeper into asset caching strategies — specifically tailored for a Rails API-only backend + Vue frontend deployed with Nginx.

🔑 The Core Idea

  • HTML files (like vite.html) should never be cached. They are the entry point of the SPA and change frequently.
  • Hashed assets (like /vite/index-G34XebCm.js) can be cached for 1 year safely, because the hash ensures cache-busting.
  • Non-hashed assets (images, fonts, legacy JS/CSS) should get short-term caching (e.g., 1 hour).

This split ensures fast repeat visits while avoiding stale deploys.

📂 Example: Files in public/vite/

Your build pipeline (via Vite) outputs hashed assets like:

vite/
  index-G34XebCm.js
  DuckType-CommonsRegular-CSozX1Vl.otf
  Allergens-D48ns5vN.css
  LoginModal-DR9oLFAS.js

Notice the random-looking suffixes (G34XebCm, D48ns5vN) — these are hashes. They change whenever the file content changes.

➡️ That’s why they’re safe to cache for 1 year: a new deploy creates new filenames, so the browser will fetch fresh assets.

By contrast, files like:

assets/
  15_minutes.png
  Sky_background.png

do not have hashes. If you update them, the filename doesn’t change, so the browser might keep showing stale content if cached too long. These need shorter cache lifetimes.


🛠️ Final Nginx Caching Configuration

Here’s the Nginx cache snippet tuned for your setup:

# =====================
# HTML (always no-cache)
# =====================
location = /vite.html {
    add_header Cache-Control "no-cache";
}

location ~* \.html$ {
    add_header Cache-Control "no-cache";
}

# ==============================
# Hashed Vue/Vite assets (1 year)
# ==============================
location ^~ /vite/ {
    add_header Cache-Control "public, max-age=31536000, immutable";
}

# ==================================================
# Other static assets (non-hashed) - 1 hour caching
# ==================================================
location ~* \.(?:js|css|woff2?|ttf|otf|eot|jpg|jpeg|png|gif|svg|ico)$ {
    add_header Cache-Control "public, max-age=3600";
}

🔍 Explanation

  • location = /vite.html → explicitly disables caching for the SPA entry file.
  • location ~* \.html$ → covers other .html files just in case.
  • location ^~ /vite/ → everything inside /vite/ (all hashed JS/CSS/images/fonts) gets 1 year caching.
  • Final block → fallback for other static assets like /assets/*.png, with only 1-hour cache.

⚠️ What Happens If We Misconfigure?

  • If you cache .html → new deploys won’t show up, users may stay stuck on the old app shell.
  • If you cache non-hashed images for 1 year → product images may stay stale even after updates.
  • If you don’t use immutable on hashed assets → browsers may still revalidate unnecessarily.

🏗️ Real-World Examples

  • GitLab uses a similar strategy with hashed Webpack assets, caching them long-term via Nginx and Cloudflare.
  • Discourse does long-term caching of fingerprinted JS/CSS, but keeps HTML dynamic with no-cache.
  • Basecamp (Rails + Hotwire) fingerprints all assets, leveraging 1-year immutable caching.

These projects rely heavily on content hashing + Nginx headers — exactly what we’re setting up here.

✅ Best Practices Recap

  1. Always fingerprint (hash) assets in production builds.
  2. Cache HTML for 0 seconds, JS/CSS hashed files for 1 year.
  3. Use immutable for hashed assets.
  4. Keep non-hashed assets on short lifetimes or rename them when updated.

This ensures smooth deploys, lightning-fast repeat visits, and no stale content issues.

📌 In Part 3, we’ll go deeper into Rails + Passenger integration, showing how Rails API responses fit into this caching strategy (and what not to cache at the API layer).


Unknown's avatar

Author: Abhilash

Hi, I’m Abhilash! A seasoned web developer with 15 years of experience specializing in Ruby and Ruby on Rails. Since 2010, I’ve built scalable, robust web applications and worked with frameworks like Angular, Sinatra, Laravel, Node.js, Vue and React. Passionate about clean, maintainable code and continuous learning, I share insights, tutorials, and experiences here. Let’s explore the ever-evolving world of web development together!

Leave a comment