Your Deployment Was Successful. So Why Are Users Still Seeing the Old Version?
Your Deployment Was Successful. So Why Are Users Still Seeing the Old Version?
A successful deployment occurs. The CI/CD pipeline has turned green, the server has the most recent code, and there are no errors recorded in the deployment log file. All seems well.
However, a user accesses the site and complains,
“I am still getting the old version.”
Such issues are some of the production challenges that may be difficult to comprehend since, by technical standards, the deployment has gone according to plan. The crucial element here is that your code does not pass directly from the server to the client. In between could be web browsers, CDNs, reverse proxies, service workers, load balancers, and other application servers. Any one of these may be responsible for a user receiving an outdated version.
Deployment Success Doesn’t Always Mean User Success
Developers often imagine deployment as:
Code → Build → Server → User
A real production environment can look more like:
Code → Build → Server → CDN → Cache → Browser → User
There may also be several application instances running behind a load balancer. This means two things can be true at the same time:
- The new version is successfully deployed.
- A user is still receiving the previous version.
The problem is not necessarily the deployment itself. The problem may be what happens after deployment.
1. Browser Cache
The browser is usually the first place to investigate.
Browsers cache files such as:
- HTML
- CSS
- JavaScript
- Images
- Fonts
This works since the browser does not need to request for the same file repeatedly when the client revisits a particular web page. However, this approach may cause issues in the case of a deployment. For example, let’s say you deployed a new version of your React app. Your server holds new JavaScript and CSS files, but the user’s browser still holds the old versions cached.
This leads to the following:
Your server serves the new application, but your user sees the old one.
One way to test this is to open your site in the incognito/private mode or hard reload your browser. If you see the new version of your app opened in such a manner, it might be related to caching. However, forcing everyone to clear their cache is certainly not a production solution.
2. CDN Caching
A lot of websites these days rely on a CDN to increase performance. A CDN has caches containing copies of static files stored on multiple servers around the globe. In this way, users can download content from a cache that is closer to their location instead of accessing it directly from an origin server. It makes sense in terms of performance; however, it adds yet another caching level to the picture.
For instance:
User -> CDN -> Origin Server
Let us assume you update a JavaScript file. The origin server will have the new file; however, the CDN will still have the old version of the file in its cache. Consequently, when a request is made, the CDN can return the cached file instead of the updated one. For the deployment system, everything works fine. For the user, there is no change in the website.
This is the reason why production deployments sometimes require CDN cache invalidation or proper cache expiration policies.
3. Static Files Can Create Version Conflicts
Frontend applications depend heavily on static assets such as JavaScript and CSS files. Consider a simple example:
main.js
You deploy Version 1 of the application, and users cache
main.js.
Later, you make changes and deploy Version 2. The filename is still:
main.js
The browser or CDN may continue using the cached file. Now the HTML might belong to Version 2 while the JavaScript belongs to Version 1. This can lead to unexpected behavior:
- New features don’t appear.
- Styling looks outdated.
- JavaScript errors occur.
- Parts of the application work while others don’t.
This is one reason modern build systems use hashed filenames.
Instead of:
main.js
the build might produce:
main.82fd31.js
After another deployment, it becomes:
main.a73bc9.js
Because the filename changes when the content changes, the browser knows it is a different file and downloads it. This allows developers to use long-term caching without constantly serving outdated assets.
4. Service Workers Can Serve Old Versions
In addition to the above-mentioned methods for caching and serving stale content, there is one more possible way how outdated information can be shown to the user on your website if you use a service worker or PWA capabilities. As a service worker is able to intercept network requests and deliver cached files, the user’s browser will receive the old version of your application even if the server has been updated.
You upload the new version. The server is updated. The CDN is updated. The page is refreshed. Still, the old version is displayed. It may be because of a service worker. Therefore, proper caching management and service workers’ updates are required.
5. Reverse Proxies and Server-Side Caching
Caching can also happen on the infrastructure between the user and your application.
A production environment might contain:
- Nginx
- Apache
- Varnish
- API gateways
- Reverse proxies
- Load balancers
For example:
User → CDN → Nginx → Application
In case Nginx or any other proxy is set up in such a way as to cache responses, requests will never make their way to the application itself. You can update the software and restart the app, but the proxy can still serve old cached responses to your requests. Therefore, testing only the application server will sometimes fail to provide the right results.
6. Multiple Servers Can Run Different Versions
Another common problem occurs when an application is deployed across multiple servers or containers.
Imagine you have three application instances:
- Server A → Version 2
- Server B → Version 2
- Server C → Version 1
The load balancer will then distribute traffic among them. So User 1 is now running Version 2. And User 2 is now running Version 1. Different users have different versions of the website they are using. This may occur during rolling deployments if there are still old instances available while deploying new ones.
Or this may happen because one of the containers or servers did not update properly. Proper production deployment will ensure that all instances will be running on the right version at some point. Strategies like rolling deployment and blue-green deployment can minimize such cases.
7. Docker Can Also Be Part of the Problem
Docker is heavily used in the deployment of production environments due to its ability to package and replicate applications. The problem with Docker builds is that they have cached layers. When you configure your Docker build, you might reuse an old layer when the layer should have been new. Your container will deploy, but the application within that container won’t have the updates you expect. An additional technique is to use descriptive images version tags.
For example:
app:latest is not very helpful in identifying which version is deployed. A version or commit-based version like app:2.4.1 or: app:8f31c2a makes it easier to identify exactly which application version is running in production.
What About DNS?
DNS is frequently fingered in scenarios where someone sees an outdated version of their website; however, DNS should not always be the first place that one goes looking for trouble. DNS is important only in the case where there is a modification of where the domain is pointing, such as moving from one server to another. Otherwise, if your DNS settings are unchanged and you merely updated your application, DNS is not usually at fault.
Caching, multiple servers, CDNs, or other issues could be the culprit.
How to Prevent These Problems
The best solution isn’t to manually clear caches after every deployment. Instead, the deployment process should be designed to handle versioning and caching correctly.
Use Hashed Assets
Use build systems that generate unique filenames for JavaScript and CSS files. When the content changes, the filename changes too. This prevents browsers from confusing an old asset with a new one.
Configure Cache-Control
Different resources should have different caching policies. Static files with hashed filenames can generally be cached for a long time. HTML files often need more careful handling because they point to the latest JavaScript and CSS files.
Manage CDN Caches
If your CDN caches content, include cache invalidation or appropriate expiration rules in your deployment process.
Keep Application Versions Consistent
If you have multiple servers or containers, make sure they are all running the same intended release.
Track the Deployed Version
A version number or Git commit hash can make troubleshooting much easier.
For example:
Application Version: 2.4.1
When a user reports a problem, you can determine which version they’re actually receiving instead of guessing.
A Simple Troubleshooting Checklist:
When someone says “I’m still seeing the old version,” don’t immediately deploy the application again. Check the system layer by layer:
1. Check the build
Was the new code actually included in the production build?
2. Check the server
Is the latest build present on the server?
3. Check the running application
Is the correct version actually running?
4. Check other instances
Are all servers or containers running the same version?
5. Check the CDN
Could an older file or response still be cached?
6. Check the browser
Does the new version appear after a hard refresh or in a private window?
7. Check service workers
Is an older cached application being served?
8. Check network requests
Browser developer tools can show which HTML, JavaScript, and CSS files are being requested and where they are coming from.
9. Check the version
Compare the version or commit hash being served with the version you intended to deploy. This approach is much more effective than repeatedly running the deployment.
The Real Lesson
A successful deployment does not mean that all users are already viewing the latest version right away. Today, applications consist of many layers from the code written by developers to files viewed in the browser. Browser caches files, CDNs cache content, reverse proxies cache responses, service workers can cache files, load balancers can redirect users to different servers, and containers can run different builds.
These technologies are not bad at all; on the contrary, they are necessary for creating high-performing, scalable applications. It is important to understand how they work. An efficient deployment process makes it possible to easily answer three questions:
Which version was deployed?
Which version is running?
Which version is being received by users?
If these three questions are the same, production management is significantly simplified. Since in modern web development, “the deployment was successful” is just a beginning.