How Inappropriate Browser Caching Can Cause a Major Bug

What happened to me was that I enabled browser caching at my root path (/). Because of that, all routes, including protected routes, were cached in the browser.

So, what happened?

I logged in to my dashboard, and everything was working fine. I also had that dashboard tab pinned in my browser.

The next day, when I opened Chrome, the browser still displayed the old cached page. Note that this was a protected page, and it was still being served even though my authentication cookie had expired on the server side.

Now, when I reloaded the page, the browser made a request to the server. Since the page was protected and the browser sent an expired cookie, the server responded with a 302 status code and a Location header pointing to /login.

Here's where the major bug occurred.

The browser cached the /dashboard redirect response as well. In other words, it remembered that whenever someone visited /dashboard, it should redirect them to /login.

Now, when I tried to log in again, everything worked perfectly. My authentication cookie was valid, and I was successfully logged in.

But when I visited /dashboard, the browser redirected me to /login again.


Why?

Because the browser was using the previously cached redirect response instead of making a new request to the server. As a result, the server never got a chance to validate my new authentication cookie.

So, I thought my panel was logging me out, even though my authentication cookie was perfectly valid.


What I Learned From This

1. Cached redirects can prevent users from accessing their dashboard.

As I discussed above, if the browser caches a 302 redirect response from a protected route, it may continue redirecting users to the login page even after they successfully log in again.

2. Cached protected pages can expose sensitive information.

Imagine someone else using the same system and browser. If the browser still has a cached protected page, it might render that page without performing fresh authentication validation.

This could expose sensitive information to another person, even if the original user's authentication session has expired.


How to Handle Browser Caching in Nginx

We don't need to disable browser caching for the entire website. Instead, we can configure caching based on URL paths.

For example:

  • Root path (/) and protected routes: Disable browser caching.
  • Static assets (CSS, JavaScript, images, fonts): Enable browser caching.

Here's an example Nginx configuration:

nginx
server {
    listen 80;
    server_name example.com;

    # Protected routes and application pages
    location / {
        add_header Cache-Control "no-store" always;

        proxy_pass http://backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    # Static CSS files
    location /assets/css/ {
        expires 30d;
        add_header Cache-Control "public, max-age=2592000";
    }

    # Static JavaScript files
    location /assets/js/ {
        expires 30d;
        add_header Cache-Control "public, max-age=2592000";
    }

    # Images
    location /images/ {
        expires 30d;
        add_header Cache-Control "public, max-age=2592000";
    }

    # Fonts
    location /fonts/ {
        expires 30d;
        add_header Cache-Control "public, max-age=2592000";
    }
}

1. location /: We are applying Cache-Control: no-store to application routes so that protected pages and authentication redirects are not stored in the browser.

2. location /assets/css/: CSS files can be cached for 30 days.

3. location /assets/js/: JavaScript files can be cached for 30 days.

4. location /images/: Images can be cached for 30 days.

5. location /fonts/: Fonts can be cached for 30 days.