`The Secret Formula`: Hidden tips for improved Web Performance

Bhavya Saggi
Engineering @ Housing/Proptiger/Makaan
9 min readOct 25, 2018

There are plenty articles around, on how to setup a robust infrastructure, and even more articles on how to make it resilient and faster. But other than optimising the infrastructure & web-services, the individual webpages that are served to user must also be optimised to gain attention of your consumers. This heralds the need for a smooth “Content Delivery” & efficient “On-Page SEO”.

Following the premise, similar common optimisations were in place at my workplace at Makaan.com, but I found I could shave down a few extra seconds off the web-page & deliver more content (to Search-Engine Crawlers and Users).
Following is a summary of actions, optimisations and experiments that were performed to this effect.

Minify HTML

A build automation tool like grunt, gulp or webpack is often used to create minified-uglified-hashed static resources, but the HTML document (or HTML template) slip in between.
Since the HTML document is an accumulation of all HTML, inline-CSS (style tags) and inline-JS (script tags) resources it may be tricky, but a gluttonous presence is common amongst all, The ‘white-space’.

The white-spaces from <script> & <style>tags can be eliminated by writing as less as possible in these tags & moving other content to an external file, which later can be minimised & uglified by utilising already available tasks under major build automation tools like grunt-usemin for grunt.

For the HTML templates or static HTML files, whitespace elimination between HTML-tags should be done either via regex-replacement or innate template optimisation.

In our case, we had many inline<script> tags containing relevant information in JSON-format. Therefore, We simply overrode our <script> tag in template-renderer to wrap contents said script-tags in JSON.stringify(JSON.parse()) & for other script tags, simply made a regex replacement replace(/[\s]*[\n\r][\s]*/gi,'') over the HTML document.

Use of Link tags to hint future resources

After setting up the HTML, its time we checked the external resources. Resources are requested by browser via 2 ways:

  1. Explicit inclusion by <script> , <style>, or <link> tag in HTML document.
  2. Resources needed by external resources, eg. a background-image in a CSS style or dependency for a JS files.

The first priority should be to minimise resource-chaining e.g. a external Javascript file requesting a CSS file which later on requests an image. And to this affect modern browsers allow us to declare future resources beforehand for apriori management, some are discussed as follows:

  1. <link rel="dns-prefetch" href="resource">
    This directive mentions to the browser to prematurely resolve the DNS query to a domain, so that the future resources from the domain can be resolved.
    dns-prefetch is most effective when used for the CDN domain, or declaring alternate domains if domain-sharding is employed.
  2. <link rel="preconnect" href="resource" crossorigin>
    Moving further ahead from dns-preconnect, the preconnect directs the browser to actually setup the connection to the domain.
    This is most effective when used in conjunction with a domain that utilises HTTP/2, as HTTP/2 keeps a connection opened till it is explicitly closed.
  3. <link rel="preload" href="resource" as="type">
    If there is a resource that is sure to be used on a webpage, but is declared deep in a chain, it can indicated to the browser beforehand using this directive. preload allows browser to fetch the resource and keep it in its cache, and serve it from cache itself when resource is requested.
    This can be effectively used for mentioning the font/image files mentioned further in the CSS stylesheet.
  4. <link rel="prefetch" href="resource">
    Similar to preload, the prefetch directive hints browser to request the resource, but in the background in idle-time.
    Link prefetch is supported by most modern browsers with the exception of Safari, IOS Safari, and Opera Mini.
  5. <link rel="prerender" href="resource">
    Prerendering is very similar to prefetching in that it gathers resources that the user may navigate to next. The difference is that prerendering actually renders the entire page in the background.
    Hence, it is most effective to indicate the next likely HTML navigation target.

Ergo, we added all the CDNs under the ‘preconnect’ link-tag, the image-sprites & font-woff files under ‘prefetch’ link-tag, and future-needed lazy-loaded javascript files under ‘preload’ link-tag.
You may even go further ahead and add the next page resources under ‘prerender’ link-tag if your web-application is like a SPA (Single Page Application).

Chunking, Segmenting & Deferring Page Resources.

Payload Size of a TCP packet ~63Kb, which means that we can transfer 63Kb of data in a singe RTT (Round-Trip Time) before the network waits for next packet.Hence, it means that

  1. Very large files would take many packets (which arrive out-of-order), and network waits till all are received to they can be processed.
  2. Very small files would be resolved in a single RTT, but multiple requests pollute the network with copious amount of packets.

Therefore, the experience can be improved by chunking the external resource files into independent modules of size of 40–50Kb. But this comes with a problem that multiple independent modules, all requested and executing in parallel (thanks to http/2), chokes the network and main thread of the browser.
To solve this, manual intervention is needed to segment resource requests/execution in 3 phases:

  1. First-Fold Resources (inline resources)
    As per Google’s Pagespeed & Lighthouse tool’s guidelines, the CSS for content in first-fold should be inlined, so to maintain user-engagement & avoid jerky/clunky-loading on webpage.
    Other than CSS, a few essential/declarative JavaScript could be inlined e.g. infrastructure related JS, on which future JS execution is dependent. It is done so to avoid any race conditions that may happen, i.e. if the definition of a function has not arrived yet and it is used.
  2. Page-Essential Resources (linked resources)
    Only resources which provide basic functionality for your webpage should be ‘linked’ in the <head> of the HTML document, to reduce the number of requests the browser sends and avoid network choking.
    That means, adding only the CSS for basic & static components, & only the JS for essential functionalities (e.g. event-binding or tracking).
  3. Lazy-loaded resources
    Heavy & dynamic resources should be lazyloaded & their resources should be fetched only when the domInteractive event for browser has fired. You may even go further and delay a few resources after the pageLoad event. This allows us to manually defer resources and in a hacky-manner define sequential order of resources.

A colleague wrote an interesting article on employing advanced brotli compression algorithm to the static assets & serving them if client supports brotli-compression.

Image Optimisation

While images provide a more user-friendly and a visual medium to a web-page, they are often a load on the webpage either because of their count or their size.

A solution for smooth visual experience is to defer image loading on the webpage. To achieve this, we placed a dummy-image (usually a 1px jpg) in the src attribute of <img> tag and url of original image in the data-src attribute of the tag. Upon ‘domInteractive’ or ‘pageLoad’ event, we initiate a IntersectionObserver and start replacing the src of Image-tags with the value in data-src attribute as the images come in view.

To extend user-experience and gain SEO/Accessibility brownie-points, popular tools suggest

  1. Provide all height, width, & src attribute for the Image (<img>) tags.
  2. Serve images in webp image format, if browser supports.
  3. Resize & Serve Images for same size as required on the webpage.

A colleague wrote an interesting article on how we setup a service for cost-effective, user-friendly & optimised images.

Use HTML5 semantic Tags & Follow W3C Validations

Because of highly interactive and content-rich webpages, crawlers & browsers often lose sight of what may be important. This is where HTML5 semantic tags come into play.

HTML5 provide numerous tags, which are similar to classical <div> but provide a universal semantic meaning. <header>, <footer>, <section>, etc. are a few to name which provide a semblance of HTML markup to crawlers and browsers.
Furthermore, modern browsers are resilient and often self-correct incorrect HTML tags (e.g. a tag you forgot to close), but we should make sure that we provide as perfect content as possible, therefore the HTML document should pass the W3C validations. Not only this helps browsers render the document with much ease, but makes your content apprehensible to crawlers.

Update & Optimise Nginx configuration

We use Nginx as our primary front-facing load-balancer & web server. Following are general optimisations that should be performed to get most out of it.

Mozilla SSL Configuration Generator
  1. Enable http/2
    The HTTP/2 (h2) being the successor of HTTP/1.x (h1) provides a range of optimisations, some of which includes, Header-Compression & Request-Multiplexing.
    HTTP/2 also comes with additional feature of ‘Server Push’, which can be enabled from Nginx after v1.13.9. (which will be discussed a later in this article)
    One limitation before enabling http/2 is that it runs only over https, making us look for SSL related improvements.
  2. Enable OSCP stapling
    Online Certificate Status Protocol (OCSP) is used to check the revocation status of X.509 digital certificates. Nginx allows appending (“stapling”) a time-stamped OCSP response which is signed by the Certificate Authority to the initial TLS handshake (eliminating the need for clients to contact the CA) & reducing time taken to setup a SSL connection.
  3. Extend SSL cache.
    Nginx allows to sharing of the SSL-session between its workers through ngx_http_ssl_module. The cache size is specified in bytes; one megabyte can store about 4000 sessions, therefore reducing SSL resolution time for a recurring user.
    The ssl_session_ticket can also be used as an alternate to SSL-cache. In case of session tickets, information about session is given to the client. If a client has a session ticket, it can present it to the server and re-negotiation is not necessary.
  4. Enable Server-push
    When a connection is http/2 enabled, asset files can Server Push is where the server pushes a resource directly to the client without the client asking for the resource, saving 1 round-trip for assets.
    Since v1.13.9, Nginx natively supports Server-push, by pushing the assets defined in the “Link preload” response headers.
With HTTP/2 Push, the server can take the initiative to send content even before it is requested. In this example scenario, the server knows that anyone requesting demo.html will need styles.css and image.jpg, so it can push them to the client immediately without waiting for the client to request them.

Furthermore, do refer the [Mozilla SSL Configuration Generator] as a boilerplate to generate a secure & optimised Server configuration, as per the need.
The discussed optimisations are quite generic and can easily be extended to other web-servers (e.g. Apache, IIS).

Conclusion

The culmination of the set of aforementioned activities is evident in the following snapshot of ‘Average Page-Load Time’ for Makaan.com (taken from Google Analytics).

Not only did we managed to reduce the size of each asset (including HTML, CSS, and JS files), we chunked & linearised the delivery for the assets over http/2 protocol. This provided us a drop of ~60KB drop in average data transmission till page-load & full effective boost of ~2 seconds in page-load time across the entire website!!

For further detailed explanation, here’s a waterfall-snapshot for a webpage on Makaan.com

Useful References

Following are popular webpage optimisation tools which should be used & referred for a better user-experience.

  1. TestMySite — Provides a comprehensive performance & User Engagement result from a suite of tests.
  2. PageSpeed Insights — A tool which scores your webpage out of 100, based upon a list of a optimisations which are expected to be present.
  3. Webpagetest — A utility which allows to visualise the webpage’s performance in metrics & screenshots.
  4. Lighthouse — A client-side utility which can be run on browser itself (can be found in Google Chrome, in Developer Tools, under Audits tab, giving a broad-spectrum report.
  5. Google Structured-Data Testing Tool — Google’s Utility to verify & validate ‘Structured Data Markup’ on a webpage.
  6. Google Mobile-Friendly Test — Google’s Utility to verify & validate if a webpage performs & works as expected on Mobile Devices.
  7. W3C Validator — The Markup Validator is a free service by W3C that helps check the validity of Web documents.

--

--