Even in a well configured web-app that caches all static content appropriately, the vast majority of sites go back to the server to retrieve the HTML for a page every time. This seems like a flaw to me, as we are continually serving elements in the page that don’t change often and could be cached; but currently you can’t - it’s an all or nothing affair. So if there is something dynamic on the page, back to the server we go. I wish we could do this:
<!DOCTYPE html> <html> <body> <header src="/header"></header> <article src="/articles/1"></article> <footer src="/footer"></footer> </body> </html>
It seems nuts that we treat all the elements on the page as a single unit. The contents of my header, article and footer may have different cacheability requirements; for instance my header may contain my name, which means that only I should be able to cache it, whilst an article could be cached for a few hours, whilst my footer changes infrequently and could be cached for a longer time. Our requests in the above example could have cache-control headers like this:
/header
Cache-Control: private, max-age=600
/articles/1
Cache-Control: public, max-age=7200
/footer
Cache-Control: public, max-age=86400
Now that that HTML page mainly contains placeholder elements, it could be cached too, as it doesn’t contain any user specific data. This is essentially donut caching – we’re caching all the bits around the bit that changes (in our example, the <header>)
To speed things up some more, if you route all your traffic through a CDN like Akamai, then you increase the chance of a first time visitor being served content from a cache (one of their edge servers, potentially located near them), meaning less traffic on your servers and a better experience for users. Even without a CDN, using a reverse proxy like nginx can help reduce load on your web-servers.
Can’t we just use IFrames?
No, this wouldn’t be the same thing. An IFrame is a completely isolated document hosted within another document, with it’s own DOM. What I want here is that all the composed elements forms a single document, with one DOM. If this was standardised, search engines could also treat all the content as one, i.e. it would fetch all the dependent content and treat the merged result as a single page.
Can’t we just use JavaScript?
Kind of. We could simulate composeable elements with the following;
<!DOCTYPE html> <html> <head> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script> <script src="composeable-elements.js"></script> </head> <body> <header data-src="/header"></header> <article data-src="/articles/1"></article> <footer data-src="/footer"></footer> </body> </html>composeable-elements.js
$(document).ready(function(){ // Find all elements that have a data-src attribute var elements = $("[data-src]"); // For each one, perform a GET request and replace the element with the HTML in the response elements.each(function(){ var element = $(this); $.get(element.data("src"), function(html){ element.replaceWith(html); }); }); });
This approach works, but has some down-sides:
- A user without JavaScript will just see blank page.
- A search engine will just see (and index) a blank page
- To cater for both non-JS users and search engines, you would have to detect them and return a single HTML document in the traditional method.
- The $(document).ready() event would loose it’s meaning. If you controlled every bit of JS on the page you could work around this by firing an event when all the dependant elements have been received, but if you used any external scripts or plugins that used $(document).ready, they would break.
I really do think this would be better handled by browsers in a standard way, and in a way to maintain backward compatibility. It’s in the spirit of the web, embraces http cacheability and would speed up the web by making more of the bytes that fly around cacheable.
For many of them, we can provide reasonable work-arounds with JavaScript based polyfills, which I think we could do in this case, using script similar to that in my post (although there are draw-backs as I mention).
Another possibility would be to detect older browsers on the server and serve them the fully resolved content. Not the best solution, but it is doable.