开发者

Better way to prevent browser caching of JavaScript files

开发者 https://www.devze.com 2023-01-25 07:44 出处:网络
This is how we prevent caching of JS and CSS files by browsers.This seems slightly hacky..is there a better way?

This is how we prevent caching of JS and CSS files by browsers. This seems slightly hacky.. is there a better way?

<%
//JSP code
long ts = (new Date()).getTime(); //Used to prevent JS/CSS caching
%>

<link rel="stylesheet" type="text/css" media="screen" href="/css/management.css?<%=ts %>" />
<script type="text/javascript" src="/js/pm.init.js?<%=ts %>"></script> 
<script type="text/javascript" src="/js/pm.util.func.js?<%=ts %>"></script> 

Update: The reason we want to prevent caching is to ensure the newer version of the f开发者_运维技巧iles are loaded when we do a new release.


You want CSS and JS to be cached. It speeds up the loading of the web page when they come back. Adding a timestamp, your user's will be forced to download it time and time again.

If you want to make sure they always have a new version, than have your build system add a build number to the end of the file instead of a timestamp.

If you have issues with it just in dev, make sure to set up your browsers to not cache files or set headers on your dev pages to not cache.


Caching is your friend. If browsers are caching these files incorrectly, it means something is wrong with the HTTP headers your web server is sending along with the JS and CSS files themselves (not the HTML page that uses them). The browser uses those headers to figure out if it can cache the file.

Your Web server can send these headers (on every JS and CSS file it serves) to tell browsers not to cache them:

Cache-Control: no-store
Pragma: no-cache
Expires: Sat, 01 Jan 2000 00:00:00 GMT

But that will increase the network load on your site, and users will see the page load slower. You could be a little more lenient and allow the browser to cache the CSS file for 60 seconds:

Cache-Control: max-age=60

If you really want the browser to check for a new file with every single page load, you can still save some network traffic by using an ETag:

Cache-Control: max-age=0
Pragma: no-cache
Expires: Sat, 01 Jan 2000 00:00:00 GMT
ETag: "o2389r-98ur0-w3894tu-q894"

The ETag is simply a unique identifier your Web server makes up each time the file changes. The next time the browser wants the file, it asks the server, "does /js/pm.init.js still have the ETag o2389r98ur0w3894tuq894?" and if so, your server simply says, "yes". That way your server doesn't have to send the whole file again, and the user doesn't have to wait for it to load. Win-win.

How to convince your web server to autogenerate ETags depends on the server. It's usually not hard.

I've seen the hack you're using before. Like so much on the Web, it's not pretty or particularly efficient, but it works.


If The reason we want to prevent caching is to ensure the newer version of the files are loaded when we do a new release., you want that the new js is loaded when THERE IS a NEW release, not all the times.

In order to do that you want that the "ts" value is linked with the file not with the time of the day. You can either use one of these system:

  1. ts = Timestamp OF THE FILE
  2. ts = MD5 (or whatever checksum) of the FILE
  3. ts = version of the code. If you have a script that do deployment be sure that it adds the version code (or the date of the release) in some include file that will be assigned to ts.

In this way the browser will reload the file only if it is new and not all the times.


A simple approach to this would be to use last modified date of the js or css files in the URL instead of a time stamp. This would have the effect of preventing caching only when there is a new version of the file on the server.


Edit

In case you are using Spring Boot it's now much simpler to prevent caching of modified files.

All you need to do is add this to application.properties:

spring.resources.chain.strategy.content.enabled=true
spring.resources.chain.strategy.content.paths=/**

If you are using Thymeleaf or FreeMarker it's completely autoconfigured. If you are using JSPs you need to manually declare a ResourceUrlEncodingFilter.

Read more here: http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-developing-web-applications.html#boot-features-spring-mvc-static-content

What follows now is my "old" post which also works but requires more work.


Since you are using Java there is a chance you are also using maven to manage your project.

In that case, to improve performance, and to make sure that no browser caches your static resources when a new release of your software is produced, you should combine all of your stylesheets and JavaScript files into on single file of their type, and you should make your resource urls change when you create a new release.

Luckily maven can do all this for you at build-time. You'll need minify-maven-plugin and maven-replacer-plugin.

This excerpt of a pom.xml should get you started:

<properties>
    <timestamp>${maven.build.timestamp}</timestamp>
    <maven.build.timestamp.format>yyyyMMddHHmm</maven.build.timestamp.format>
</properties>

<plugin>
    <groupId>com.samaxes.maven</groupId>
    <artifactId>minify-maven-plugin</artifactId>
    <version>1.6</version>
    <executions>
        <execution>
            <id>minify-css</id>
            <phase>process-resources</phase>
            <goals>
                <goal>minify</goal>
            </goals>
            <configuration>
                <linebreak>-1</linebreak>
                <cssSourceDir>resources/css</cssSourceDir>
                <cssSourceFiles>
                    <cssSourceFile>bootstrap.css</cssSourceFile>
                    <cssSourceFile>style.css</cssSourceFile>
                </cssSourceFiles>
                <cssTargetDir>resources/css</cssTargetDir>
                <cssFinalFile>${timestamp}.css</cssFinalFile>
            </configuration>
        </execution>
        <execution>
            <id>minify-js</id>
            <phase>process-resources</phase>
            <goals>
                <goal>minify</goal>
            </goals>
            <configuration>
                <linebreak>-1</linebreak>
                <jsSourceDir>resources/js</jsSourceDir>
                <jsSourceFiles>
                    <jsSourceFile>jquery.js</jsSourceFile>
                    <jsSourceFile>bootstrap.js</jsSourceFile>
                    <jsSourceFile>script.js</jsSourceFile>
                </jsSourceFiles>
                <jsTargetDir>resources/js</jsTargetDir>
                <jsFinalFile>${timestamp}.js</jsFinalFile>
            </configuration>
        </execution>
    </executions>
</plugin>

<plugin>
    <groupId>com.google.code.maven-replacer-plugin</groupId>
    <artifactId>replacer</artifactId>
    <version>1.5.2</version>
    <executions>
        <execution>
            <id>replaceDynPartInResourcePath</id>
            <phase>prepare-package</phase>
            <goals>
                <goal>replace</goal>
            </goals>
            <configuration>
                <ignoreMissingFile>false</ignoreMissingFile>
                <basedir>${project.build.directory}</basedir>
                <file>${project.artifactId}/WEB-INF/views/header.jsp</file>
                <regex>false</regex>
                <replacements>
                    <replacement>
                        <token>$dynamicResourceNamePart$</token>
                        <value>${timestamp}</value>
                    </replacement>
                </replacements>
            </configuration>
        </execution>
    </executions>
</plugin>

This is how to include your static resources in header.jsp

<c:choose>
    <c:when test="${not fn:contains(pageContext.request.serverName, 'localhost') and empty param.nocombine}">
        <link href="${pageContext.request.contextPath}/resources/css/$dynamicResourceNamePart$.min.css" rel="stylesheet" type="text/css" />
        <script src="${pageContext.request.contextPath}/resources/js/$dynamicResourceNamePart$.min.js" type="text/javascript"></script>
    </c:when>
    <c:otherwise>
        <link href="${pageContext.request.contextPath}/resources/css/bootstrap.css" rel="stylesheet">
        <link href="${pageContext.request.contextPath}/resources/css/style.css" rel="stylesheet">
        <script type="text/javascript" src="${pageContext.request.contextPath}/resources/js/jquery.js"></script>
        <script type="text/javascript" src="${pageContext.request.contextPath}/resources/js/bootstrap.js"></script>
        <script type="text/javascript" src="${pageContext.request.contextPath}/resources/js/script.js"></script>
    </c:otherwise>
</c:choose>


For debug purposes, install the web developer toolbar for FireFox and activate there "deactivate Cache"

Update:
When you have FireBug installed, you can disable caching as well in the network tab settings.


If you can include Java Servlet Filter in your application, here is a working solution: CorrectBrowserCacheHandlerFilter.java

Basically, when your browser requests the static files, the server will redirect every requests to the same one but with a hash query parameter (?v=azErT for example) which depends on the content of the target static file.

Doing this, the browser will never cache the static files declared in your index.htmlfor example (because will always received a 302 Moved Temporarily), but will only cache the ones with the hash version (the server will answer 200 for them). So the browser cache will be used efficiently for those static files with hash version.

Disclaimer: I'm the author of CorrectBrowserCacheHandlerFilter.java.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号