GWT applications are basically a bunch of static resources, and static resources are strong candidates for browser caching.
The GWT generated files make it quite easy to follow some of the tips for building a cache-aware site:
If a resource (especially a downloadable file) changes, change its name. That way, you can make it expire far in the future, and still guarantee that the correct version is served; the page that links to it is the only one that will need a short expiry time.
If we have a look at the generated files we will find
- strongly unique filenames
- cache and nocache hints in the filename
It would be possible to add servlet filters to the Java EE Web Container (I will call it tomcat from now on…) delivering those files, but I prefer to have it outside of tomcat. As long as I don’t find the “GWT optimization all-in-one-filter” I will keep using the Apache http server for optimization. Besides, it is not unusual to hide one ore more tomcat instances behind an Apache http server.
Well, I found it quite tricky to setup compression, http headers and the right proxy configuration, so here is my config as of today…
I am using mod_deflate, mod_expires and mod_headers, mod_proxy and mod_proxy_ajp. It is up to you to have those loaded in your Apache configuration. Load balancing with mod_proxy_balancer is out of scope for this article.
So here is my configuration:
I added a version number to the configuration.
# Version 2
# Source: http://pgt.de/2011/01/27/apache-configuration-for-gwt-applications/
Header Set Cache-Control "max-age=0, no-store"
Header Set Cache-Control "max-age=31536000, public, must-revalidate"
Header Set Cache-Control "max-age=31536000, private"
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/x-shockwave-flash
ExpiresByType application/json "now"
ExpiresByType text/css "now"
ExpiresByType text/html "now"
Header unset ETag
Header unset Last-Modified
Allow from all
As you can see I turned off caching of css and html files for my GWT app for a simple reason: my GWT apps follow the single page principle, so users won’t leave/ reload the page. And when they do, it is ok to deliver the actual version. You can fill in your own strategy here, depending on your own application lifecycle.
The compression is working fine, as you can see at the difference between the size and the transfered size:
As configured, the expiration headers for the cacheable filenames are set to the far future, here are the headers for a split point:
And, as required to get updated GWT apps pushed to the clients on a browser refresh, the http header for the not cacheable file: