Caching is a technique used for storing resource copies, which later allows the serving of such copy upon request without making a query to the server.
While talking about caching, it is common that it makes us think of it as something great and useful since it’s closely related to performance, since better performance provides better software solutions.
However, when it comes to implementation in itself, HTTP caching has been rarely configured properly, and the most common reasons for this are:
The image below represents a case with no caching, allowing you to see that all client requests are forwarded directly to the server.
There are several kinds of caches, but all of them may be categorized into two groups - private and shared caches.
Private cache is dedicated to a single user. The browser stores resource copies previously obtained from the server in its local cache, located on the file system.
Upon the following request, the browser will first verify whether the local cache contains the resource and only then make a request to the server if there is no resource in the cache, like Image 2 shows. This cache is also used for back/forward navigation in browsers, view as a source options, viewing offline content etc.
How this works in practice? Let’s take an example. Image 3 shows a portion of resources loaded from the Vega website’s Contact page, in case when cache is turned off. All resources have been obtained from the server and no local cache has been used.
On the right side, we see the time necessary for loading these resources and notice that it takes a bit more than 4 seconds to load the entire page.
Image 4 shows a list of loaded resources with cache turned on. On the right side you can see that the resources have been loaded either from disk or from memory.
In this case, the loading time of the whole page is decreased now to around 1.5 seconds.
Shared cache keeps a copy of the resource that can be reused by multiple users. This means that the resource is not unique per user. It is located between the client and the web server, it intercepts all requests to the server and decides whether to serve a copy of the resource from cache or contact the server to obtain such the resource.
As shown on the image below, the first request will reach the server to obtain the resource, while all other requests related to that resource will end up on shared cache that will return the resource copy from cache.
Cache configuration is carried out by using HTTP cache header:
Cache headers can be configured through the web configuration file, as shown in the image, and they can also be configured through IIS on the web server, which will also be reflected by changes in the web.config file. In the particular example shown in the image below, max-age is set to 15 seconds for all files in the “images” folder.
The second image shows setting of the Expires HTTP header. In case we have set both max-age and Expires, max-age has a higher priority and this value will simply be taken into account by cache.
When it is necessary to cache dynamic resources or an output of a certain method, we can use the built-in OutputCache attribute in MVC.
The OutputCache attribute can accept the following parameters:
OutputCache can also be configured in the web configuration file, where we can have several defined profiles. The example below shows HeaderCache and FooterCache which we can refer to later in the OutputCache attribute and set the CacheProfile property of this attribute. This type of configuration allows us to define cache profiles once in the web config file and later to reuse them from several locations in the code.
If we want to cache the entire page in ASP.NET web forms, there is OutputCache directive which we can define in the page header and use the same properties. The value of VaryByParam is set here to “none”, meaning that it does not take into account page parameters such as post values or query string.
If we set Server for Output Cache Location property, the content will be cached in the server’s internal memory by default. However, this can be a problem if we have a load balancing environment since we don’t want to cache the same resource on each server separately. Therefore we would introduce a kind of a distributed cache, such as MemedCache, Redis or any other, but in order to do that we would have to implement a custom cache provider that would carry out the communication with the distributed cache. The output cache section in the web configuration file allows us to do so, and as shown on the Image 12 we have defined the VegaCache provider from the Vega assembly.
CDN - content delivery network is the best solution for caching both static and dynamic resources. CDN is a distributed server system that delivers resources to end users taking into account their geographic location. When a resource is uploaded to CDN, it’s being automatically distributed to other servers around the world within the CDN network, also known as Edge servers.
How this work in practice? Let’s take an example, if the server that hosts our website is located in Europe, and the visitor accessing the site is from Australia, all requests made from Australia are then referred to the origin server in Europe. Regardless of how fast the Internet works today, it will take a few seconds to obtain a response from our server. Caching of resources in CDN has been introduced in order to reduce the response time by reading resources from the cache rather than hitting the server every time. This ensures that, when a user from Australia accesses our website, the majority of resources will be served from a local Edge server which is also located in Australia, instead of fetching all those resources from the origin server.
By using CDN, we do not have to implement custom cache provider, as previously mentioned, which would work with distributed cache, given that CDN itself is a distributed server system.
The Image 14 shows an example of a request with and without CDN. In this particular case, if a request is made from Africa and sent to a server located in America, it will take longer than 3s to receive a response without CDN, while with CDN present the time will be reduced to less than 1s. The request will certainly be made to the server in America, but the majority of resources will be fetched from the local CDN edge server.
CDN recognizes HTTP cache headers and will cache responses from the server as long as specified in the server response. This will prevent CDN from contacting the server at each request. Every call from CDN to the origin server costs money and incorrect configuration may lead to higher costs.
CDN acts as a proxy between client and server, so when we need a resource we will specify a CDN path and it will return a copy of the resource from cache or contact the server to fetch that resource. For example, if we want to fetch the logo.png resource which is available at https://www.vegaitsourcing.rs/images/logo.png we’ll make a request to https://cdn.vegaitsourcing.rs/images/logo.png instead and CDN will do the rest.
Regardless of the fact that all these numbers are small, if we do not have correct caching configured on server, our costs will increase as well, because a website with large traffic may easily incur big expenses.
CDN also provides a cache clearing option called Purge.
Keep in mind which resources you want to cache, do not include private resources or resources that can be obtained only if the user is logged onto the website, as that would involve a security risk. Cache resources until they change, and for refreshing use the Purge option provided by CDNs to clear cache on Edge servers.
Dejan brings expertise in multiple web development platforms to Vega IT.