HTTP caching is a common topic at work and in our daily study and interview. We should not avoid these common knowledge points, but face them bravely and understand them.

Why cache?

In any front-end project, it is common to visit the server for data, and if the same data is requested more than once, the extra requests inevitably waste network bandwidth and delay the browser rendering of the content to be processed, affecting the user experience. If users use pay-as-you-go access to the network, extra requests can also add to the user’s network traffic bill. Therefore, it is an effective strategy to use caching technology to reuse acquired resources to improve website performance and user experience.

How caching works

The principle of caching is to save a response copy of the requested resource after the first request. When the user initiates the same request again, the request is intercepted if the cache matches the request and the response copy is returned to the user, thus avoiding the re-request to the server.

HTTP cache

HTTP cache should be front developing one of the most often contact cache, it also can be subdivided into compulsive cache cache and negotiation, the two biggest difference is that a judge a cache hit, the browser will need to ask the server to negotiate the cached information, and then determine whether need to request regarding the response content, Let’s take a look at the mechanics of HTTP caching and its decision strategy.

Mandatory cache

In the case of the forced cache, if the browser determines that the requested target resource is a valid hit, it returns the request response directly from the forced cache without any communication with the server.

The two fields related to forced caching are Expires and cache-control. Expires is a field declared in HTTP1.0 to control the expiration date and timestamp of the cache. It is specified by the server and notified by the response header to the browser, which will cache the response body with this field.

If the browser makes the same resource request again, it compares expires with the current local timestamp. If the local timestamp of the current request is less than the value of expires, the response cached by the browser has not expired and can be used directly without making a request to the server. Only when the local timestamp is greater than the expires value and the cache expires, requests to the server are allowed again.

Judging from the above mandatory cache is out of date mechanism of it is not hard to see, this means there is a big loophole, namely of a strong reliance on local timestamp, local time if the client and the server time out of sync, or to the client’s time to take the initiative to change, then for the judgment of the cache expiration may be prevented from and expectations.

To address the limitations of Expires, cache-Control fields are added from HTTP1.1 to expand and improve the expires function. Cache-control sets maxAge =31536000 to control the validity period of the response resource. This is the length of time in seconds, indicating that the resource will be valid for 31536000 seconds after the request is received. This avoids the problem of server – and client-side timestamps being out of sync.

Note: If both max-age and Expires of cache-control exist, max-age prevails.

Other parameters of cache-control

  • no-cache

Setting no-cache does not exclude caching, but enforces negotiation cache. That is, the server does not determine whether the mandatory cache expires every time a request is initiated. Instead, it directly writes to the server to verify the validity of the cache.

  • no-store

If no-store is set, no cache is allowed. Each request from the client requires a new response from the server. No-cache and no-store are mutually exclusive attributes and cannot be set at the same time.

  • public

If the cache-Control field in the resource response header sets the public property value, the response resource can be cached by both the browser and the proxy server.

  • private

Private limits the response resource to be cached only by the browser, and defaults to private if no display is specified.

  • max-age

Indicates the expiration time that the server tells the client browser to respond to the resource.

  • s-maxage

For projects with large architectures, which usually involve the use of various proxy servers, it is necessary to consider the validity of the cache on the proxy server. Here is the significance of the existence of S-maxage, which indicates the expiration time of the cache in the proxy server, and is valid only when the public attribute value is set.

As a result, cache-control can be a complete replacement for Expires and has some cache-control features it doesn’t, so it’s sufficient to use it in project practice. The only reason expires currently exists is because of backward compatibility.

Negotiate the cache

As the name suggests, negotiated caching means that before using the local cache, a GET request is made to the server to negotiate whether the local cache saved by the current browser has expired. This is usually determined by the last modified timestamp of the requested resource.

  • The instance

Suppose the client needs to request a manifest.js js file from the server. In order for the resource to be re-requested using the local cache through negotiated caching, the response header that returns the image resource for the first time should contain a field named Last-Modified. The property value of this field is the timestamp of the last modification of the JS file.

When we refresh the web page, because the JS file uses the negotiated cache, the client browser cannot determine whether the local cache is expired, so it needs to send a GET request to the server to negotiate the validity of the cache. The request header of this GET request needs to contain an ifModified-since field. The value is the last-Modified field value from the last response header.

When the server receives the request, it compares the current modification timestamp of the requested resource with the value of the if-Modified-since field. If the two are the same, the cache is not expired and can continue to use the local cache. Otherwise, the server returns a new file resource.

Last-modified negotiated cache (server-side code)

 const data = fs.readFileSync('./imgs/CSS.png');
 const { mtime } = fs.statSync('./imgs/CSS.png');
 const ifModifiedSince = req.headers['if-modified-since'];
 if (ifModifiedSince === mtime.toUTCString()) {
 	res.statusCode = 304;
 	res.end();
 	return
 }
 res.setHeader('last-modified',mtime.toUTCString())
 res.setHeader('Cache-Control'.'no-cache');
 res.end(data);
Copy the code

Last-modified negotiation cache process

When a client requests a target resource for the first time, the server returns a response header containing last-Modified and the timestamp of the last modification of the resource, as well as cache-control: No-cache: When the client requests the resource again, it carries an IfmodifiedSince field. If the time corresponding to this field is compared with the timestamp of the target resource, it returns a 304 status code if there is no change.

Note that the response status code of the negotiated cache is 304, but the response status code of the forced cache is 200.

The shortage of the last-modified

  1. Last-modified is judged according to the last modified timestamp of the requested resource. Although the requested file resource is edited, the content does not change and the timestamp will be updated. As a result, the judgment on the validity of the negotiated cache is validated as invalid and a complete resource request needs to be made again. This will undoubtedly cause the waste of network bandwidth resources, and prolong the time for users to obtain the target resources.
  2. The timestamp of a file resource change is in seconds. If the file change is very fast, let’s say in a few hundred milliseconds, the update of the file resource will not be recognized by using the timestamp to verify the validity of the cache.

In fact, both defects are caused by the server’s inability to recognize a real update based on the timestamp of the resource modification, resulting in a re-request that uses a cached Bug scenario.

Etag-based negotiation cache (server code)

To compensate for the lack of timestamps, an Etag header has been added since the HTTP1.1 specification. Its content is mainly a string generated by the hash calculation of different resources by the server. This string is similar to file fingerprint. As long as there are differences in the encoding of file contents, the corresponding Etag can more accurately perceive the changes of file resources.

Etag Negotiation cache flow

  1. First, the data the server will return to the client is hashed through the eTAG module to generate a string, which is similar to a file fingerprint.
  2. Check whether the value of ifNoneMatch field in the client request header is the same as the value calculated in the first step. If so, 304 is returned.
  3. If not, the eTAG header and cache-control: no-cache are returned.

The shortage of the Etag

Unlike cache-control in forced caches, which can completely replace Expires, Etag in negotiated caches is not a last-Modified alternative but a complementary one because there are still some drawbacks.

  1. The server needs to pay extra computational overhead to generate eTAGS for file resources. If the size of resources is large, the number of resources is large, and the modification is frequent, the process of generating ETAGS will affect the performance of the server.
  2. Etag generated field values can be divided into strong and weak authentication, strong validation according to generate resources content, to ensure that each byte is the same, weak authentication is generated according to the part of the resource attribute, grow faster but can’t ensure that each byte are the same, and in the server cluster scenario, will also reduce the negotiation because of my lack of accurate cache on the effectiveness of the success rate, Therefore, the appropriate way is to select the appropriate cache verification mode according to the specific resource usage scenarios.

Caching decisions and considerations

Caching decisions

In an ideal scenario, regardless of client cache capacity or server computing power, we would certainly want the client browser to have as high a cache trigger rate and retention time as possible, as well as Etag efficient revalidation when the resource is updated. However, the actual situation is often limited capacity and computing power, so it is necessary to develop a suitable cache strategy to use limited resources to achieve the optimal performance, clear the boundary of capacity, and strive to do the best within the boundary.

Cache decision tree

In the face of a specific cache requirement, we can refer to the following cache decision tree to gradually determine the specific cache strategy for a resource.

  • Whether to use caching
    • No: no – the store
    • Is this:
      • Whether to enable negotiation cache
        • Is: no – cache
        • no
          • Whether it is cached by the proxy server
            • Is that the public
            • No: private
              • Configure the mandatory cache expiration time
                • Configure Etag or Last-Modified for the negotiated cache.

CDN cache

What is a CDN?

CDN full name is the content delivery network, it is to build on the basis of the existing network virtual intelligent network, relying on the edge server deployed in all over the world, and through the center platform of load balancing, scheduling, and content distribution function module, the user in the request needed to access the contents of the can get to the nearest, in order to reduce the network congestion, improve the response speed of the resource to the user.

Communication flow without USING CDN

  1. Request domain name resolution from a traditional DNS server.
  2. The DNS server returns the server IP address corresponding to the domain name.
  3. Request server content based on server IP.
  4. The server returns a response resource.

Communication flow using CDN

  1. A client requests domain name resolution from a traditional DNS server.
  2. The traditional DNS server assigns the domain name resolution authority to the dedicated DNS server pointed by the CNAME. Therefore, the resolution of the domain name entered by users is completed on the DNS server dedicated to the CDN.
  3. The DNS server dedicated to the CDN sends the IP address of the CDN load balancer to the client.
  4. The browser sends a request to the CDN load balancer again. After comprehensive calculation of the distance between the user’s IP address and the location of the requested resource content, the browser returns the cache server IP address determined by the user.
  5. The browser finally requests resources from the cache server.

Static resources are suitable for using CDN

Static resources refer to resources that can be obtained without the participation of website business server in calculation, including JavaScript script files, style sheet files and images of third-party libraries. These files are characterized by high access frequency, heavy load, low update frequency and not too much coupling with business.

If it is a dynamic resource file, such as THE HTML page rendered by the server side, it needs to be calculated with the data of the server side, so such resources are not suitable to be stored in the CDN cache server.

CDN performance optimization

The following describes only one CDN optimization point: domain name setting.

On the home page of Taobao, the domain name requested by the main site is www.taobao.com, while the domain name of the CDN server requesting static resources is g.alicdn.com and imG.alicdn.com. The reasons for this are as follows:

  1. Avoid requests for static resources that carry unnecessary cookie information.
  2. Consider browser restrictions on concurrent requests under the same domain name.

Common Interview Questions

Question 1: What headers are involved in strong caching?

A: Both expires and cache-control fields are involved. Expires is in HTTP1.0 and cache-Control is in HTTP/1.1.

Question 2: Why not use cache control with Expries now?

A: Because expires based caching is so dependent on local timestamps, it can be wrong to determine cache expiration if the local time on the client is out of sync with the time on the server. Cache-control controls the validity of the response resource in the form of maxage= XXX seconds. This avoids the problem of server and client timestamps being out of sync.

Q3: Strong cache public private no-store no-catch difference? What are the properties of cache-control? What do they mean?

Public: indicates that response resources can be cached by both the client and the proxy server. Private: indicates that response resources can only be cached by the browser. If this parameter is not explicitly specified, the default value is private no-store: indicates that no cache is allowed. Each request requires a new response from the server. No-cache: indicates that negotiation cache is used. Instead of trying to determine whether the cache is expired, each request sends a request directly to the server to validate the cache. Max-age: indicates the expiration time of response resources notified by the server to the client browser. S-maxage: indicates the expiration period of the cache stored on the proxy server. This parameter is valid only when the public attribute is set.

Q4: Is the negotiation cache validated on the client side or server side? How does a negotiated cache verify a hit?

A: On the server side, the server side compares the last modification time of the file with the time carried by the client. If the time is the same, the cache is matched. Negotiated caches exist in two forms. One is last-Modified. When a client requests a target resource for the first time, the server returns a response header containing last-Modified and the timestamp of the last modification of the resource, as well as cache-control: No-cache: when the client requests the resource again, it carries an IfmodifiedSince field. If the time corresponding to this field is compared with the timestamp of the target resource and there is no change, 304 status code is returned. The other is negotiation cache based on Etag. The data that the server will return to the client is hashed by the Etag module to generate a string, which is similar to the file fingerprint. If the value of ifNoneMatch field in the request header of the client is consistent with the value calculated in the first step, 304 will be returned. If not, the latest data is returned along with the eTAG header and cache-control: no-cache.

Q5: Why does the negotiated cache have last-Modified,Etag?

A: Last-modified is determined by the last modified timestamp of the requested resource. It may be that the file name has been modified, but the file content has not been modified, so the timestamp will be updated. As a result, the negotiation cache judgment is invalid and the existing complete resource is requested, which is a waste of network bandwidth. It is also possible that the speed of file modification is millisecond, but the unit of last-Modified is second, and the resource modification may not be identified. Etag is not a complete replacement of Last-Modified, but only a supplement. The problem of ETAG is that the server needs to perform ETAG calculation on file resources, which requires extra computing overhead. If the size of resources is relatively large, the process of eTAG generation may affect the performance of the server. So that’s why negotiated caches have both last-Modified and ETAG.

Q6: What is the difference between a negotiated cache and a strong cache?

The same

Both read resources from the client cache.

The difference between

  1. If the browser hits a strong cache, there is no need to send a request to the server, and the negotiated cache is ultimately up to the server to decide whether to use the cache, i.e. there is one communication between the client and the server.
  2. A hit cache in Chrome returns a status code of 200, whereas a negotiated cache returns a status code of 304.

Q7. Which has a higher priority: Expires or cache-Control? How to set no cache?

A: Expires is a product of HTTP/1.0 and cache-control is a product of HTTP/1.1, and cache-control takes precedence over Expires if both exist. No Cache is set through cache-control: no-store.

Question 8: What is the request header for LastModified?

last-modified-since.

Q9: What is the priority order of the cache?

A: Cache-Control > Expires > Etag > last-Modified.