How to prevent Browser cache on Angular 2 site?
Solution 1
angular-cli resolves this by providing an --output-hashing
flag for the build command (versions 6/7, for later versions see here). Example usage:
ng build --output-hashing=all
Bundling & Tree-Shaking provides some details and context. Running ng help build
, documents the flag:
--output-hashing=none|all|media|bundles (String)
Define the output filename cache-busting hashing mode.
aliases: -oh <value>, --outputHashing <value>
Although this is only applicable to users of angular-cli, it works brilliantly and doesn't require any code changes or additional tooling.
Update
A number of comments have helpfully and correctly pointed out that this answer adds a hash to the .js
files but does nothing for index.html
. It is therefore entirely possible that index.html
remains cached after ng build
cache busts the .js
files.
At this point I'll defer to How do we control web page caching, across all browsers?
Solution 2
Found a way to do this, simply add a querystring to load your components, like so:
@Component({
selector: 'some-component',
templateUrl: `./app/component/stuff/component.html?v=${new Date().getTime()}`,
styleUrls: [`./app/component/stuff/component.css?v=${new Date().getTime()}`]
})
This should force the client to load the server's copy of the template instead of the browser's. If you would like it to refresh only after a certain period of time you could use this ISOString instead:
new Date().toISOString() //2016-09-24T00:43:21.584Z
And substring some characters so that it will only change after an hour for example:
new Date().toISOString().substr(0,13) //2016-09-24T00
Hope this helps
Solution 3
In each html template I just add the following meta tags at the top:
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
In my understanding each template is free standing therefore it does not inherit meta no caching rules setup in the index.html file.
Solution 4
A combination of @Jack's answer and @ranierbit's answer should do the trick.
Set the ng build flag for --output-hashing so:
ng build --output-hashing=all
Then add this class either in a service or in your app.module
@Injectable()
export class NoCacheHeadersInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler) {
const authReq = req.clone({
setHeaders: {
'Cache-Control': 'no-cache',
Pragma: 'no-cache'
}
});
return next.handle(authReq);
}
}
Then add this to your providers in your app.module
:
providers: [
... // other providers
{
provide: HTTP_INTERCEPTORS,
useClass: NoCacheHeadersInterceptor,
multi: true
},
... // other providers
]
This should prevent caching issues on live sites for client machines
Solution 5
Add this to your nginx
location ~ /index.html|.*\.json$ {
expires -1;
add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
}
location ~ .*\.css$|.*\.js$ {
add_header Cache-Control 'max-age=31449600'; # one year
}
location / {
try_files $uri $uri/ /index.html?$args;
add_header Cache-Control 'max-age=86400'; # one day
}
Comments
-
Rikku121 over 2 years
We're currently working on a new project with regular updates that's being used daily by one of our clients. This project is being developed using angular 2 and we're facing cache issues, that is our clients are not seeing the latest changes on their machines.
Mainly the html/css files for the js files seem to get updated properly without giving much trouble.
-
Rikku121 over 7 yearsWe have switched to webpack for some time now and it takes care of cache busting our angular apps. It's good to know your solution works though. Thanks
-
Rossco over 7 yearsSo my implementation actually didn't end up working. caching is a strange issue. sometimes works and sometimes not. oh the beauty of intermittent issues. So I actually adapted your answer to as such:
templateUrl: './app/shared/menu/menu.html?v=' + Math.random()
-
Shenbo about 7 yearsI'm getting 404 for my templateUrls. For example: GET localhost:8080/app.component.html/?v=0.0.1-alpha 404 (Not Found) Any idea why?
-
Shenbo about 7 years@Rikku121 No it doesn't. It's actually without the / in the url. I might have accidentally added it in when I post the comment
-
Shenbo about 7 yearsTurned out it could be because I have to give a full path rather than a relative path. For example:
templateUrl: 'app/app.component.html?v=' + VERSION
works. ButtemplateUrl: './app.component.html?v=' + VERSION
does not. Even though they are in the same directory -
Rikku121 about 7 yearsThen it's more of a why are my templates not loading question, thought you meant it wasn't loading after adding the ?v=... which was rather weird
-
Shenbo about 7 yearsYes that is what I meant. And yes that is quite weird @Rikku121
-
RAVI PATEL almost 7 yearsTo use this i have to go through to each component and append that "?v=".I have around 60 components. So it tedious job to go each component and do that. Is there a global way so we can define one variable and we can use in each templateUrl. Because i don't want that user every time take html from disk if there is no change in html.
-
Rikku121 almost 7 years@RAVIPATEL best way is to use webpack using angular CLI, it's working really well and you get aot compilation too
-
iniravpatel almost 7 yearsIt did for me too
-
jonesy827 over 6 yearsThis is the proper way to do this and should be the selected answer!
-
DDiVita over 6 yearsThis did not work for our app. Its too bad the templateUrl with a query string parameter does not work with CLI
-
Apurv Kamalapuri about 6 yearsWhat is the point of caching when you are busting cache every time even when there is no code change?
-
Paramvir Singh Karwal almost 6 yearsTotally busts the issue.
--aot
— enable Ahead-of-Time compilation. This will become a default setting in future versions of Angular CLI but for now we have to enable this manually--output-hashing all
— hash contents of the generated files and append hash to the file name to facilitate browser cache busting (any change to file content will result in different hash and hence browser is forced to load a new version of the file) [ source : medium.com/@tomastrajan/… ] -
stryba over 5 yearsThis won't work if your index.html is cached by the browser, hence won't see new hashed names for your javascript resources. I think this a combination of this and the answer @Rossco gave would make sense. It also makes sense to make this consistent with HTTP headers sent.
-
OzzyTheGiant over 5 years@stryba This is why html caching should be handled differenty. You should specify the Cache-Control, Pragma, and Expires response headers so that no caching should take place. This is easy if you are using a backend framework, but I believe you can also handle this in .htaccess files for Apache (idk how it works in nginx though).
-
Jayesh Dhandha about 5 years@ApurvKamalapuri I agree with you. I have similar kind of issue, I want to cache bust only when my application is updated and the new build is triggered.
-
Noppey about 5 yearsThis answer adds a hash to the js files, which is great. But as stryba said, you also need to make sure index.html is not cached. You shouldn't do this with html meta tags, but with response header cache-control: no-cache (or other headers for more fancy caching strategies).
-
Pranjal Successena almost 5 yearsng build --aot --build-optimizer=true --base-href=/<url>/ gives error --- Couldn't resolve resource ./login.component.html?v=${new Date().getTime()}
-
Dan Dohotaru almost 5 yearsi hardly believe busting caching for no reason other than elapsed time is sufficient in this context
-
NiallMitch14 over 4 yearsPerfect, thank you! Althought consider removing the --aot flag as this might not be applicable to everyone
-
Jack over 4 yearsAs per numerous recommendations, I've removed the
--aot
flag. -
Omar Salem over 3 yearsI got an error ``` ERROR in HostResourceResolver: could not resolve ./settings.component.html?v=1 in context of /src/app/account/components/settings/settings.component.ts) ```
-
Alexus about 3 yearsI cannot believe this is the accepted answer. This is just the worst recommendation I have ever seen. I'd rather suggest solving this problem from your static content service perspective (Apache, Nginx, IIS, ... )
-
Danilo Körber almost 3 yearsWhat if I am not using NGINX?
-
minigeek over 2 yearsI wonder why people haven't upvoted your answer. thanks mate. this index.html is making mess , ngnix is good way to handle it, fortunately i am using same for backend
-
Void over 2 yearsIn some cases this solution is not enough and your browser will still load the old version.