Detailed browser caching

Detailed browser caching

Interface request overview

First request

Without caching, the server returns 200, and adds Expires or Cache-Control fields in the response header for strong caching, and adds Last-Modified or Etag fields to negotiate caching

On the second request

  • If the cache has not expired, take the number directly from the cache and return 200
  • If the cache expires, the negotiation cache will be triggered. The request header contains if-modified-since or if-none-match. After the server passes the verification, it will return 304 if it hits the cache. The browser uses the cached data and returns if there is no hit 200, and return to the new resource

demo verification

Talk is cheap, show me the code.

Do not make any changes to the request header

Create a simple server

index.js

const http = reqire('http')
http.createServer((req, res) => {
    const {url, headers} = req //  url   headers
    if (url === '/') {
      res.end(`
        <html>
          html update Time ${new Date()}
          <script src = '/main.js'></script>
        </html>
      `)
    } else if (url === '/main.js') {
        const content = `document.writeln('<br/> JS Update Time: ${new Date()}')`
        res.statusCode = 200
        res.end('content')
    }
    
}).listen(3000, () => {
    console.log('Http is running at 3000 port')
})
 

Start the service through node index.js, and visit localhost:3000 in the browser, open network, cancel Disable cache and refresh the browser, you will find that the update time of html and js is synchronized, and the status code returned by the request on the right is 200

Strong cache

Expires

To trigger the browser's caching, you only need to add the Expires field in the response header. The value of Expires is Greenwich Mean Time, which can be in the future or in the past.

const getGMTTime = (time = Date.now()) => {
  return new Date(time).toGMTString()
}
 

index.js

const http = reqire('http')
http.createServer((req, res) => {
    const {url, headers} = req //  url   headers
    if (url === '/') {
      res.end(`
        <html>
          html update Time ${getGMTTime()}
          <script src = '/main.js'></script>
        </html>
      `)
    } else if (url === '/main.js') {
        const content = `document.writeln('<br/> JS Update Time: ${getGMTTime()}')`
        // 
        res.setHeader('Expires', getGMTTime(Date.now() + 10 * 1000)) // 10 
        res.statusCode = 200
        res.end('content')
    }
    
}).listen(3000, () => {
    console.log('Http is running at 3000 port')
})
 

  • We can see that there is an additional Expires field in the response header, and we can see that after refreshing the browser, the JS update has been maintained at 14:57:49 and the html update time is 14:57:56, and you can see The request status code of js is 200 and the data source is from memory cache.
  • Refresh again after 10 seconds, the browser will send the request again

note

  • The Expires field is something of http 1.0. Now browsers mostly use http 1.1, you can use the Cache-Control field
  • The value of Expires is an absolute time. If the browser time is not synchronized with the server time, and the browser time is set to a very future time, then a shorter expiration time is useless
  • After the cache expires, regardless of whether the resources on the server are changed, the server will re-read the resources and return them to the browser.

Cache-Control

For the first two problems above, we use Cache-Control to solve

Cache-Control has many optional values, the more commonly used ones are max-age, no-cache, no-store, must-revalidate

max-age: The value of max-age is relative time, in seconds. For example, max-age=10 is equivalent to telling the browser that the resource expires in 10 seconds. One thing to note is that the starting point for calculating max-age time is the creation time of the response message (that is, the Date field, which is the time when it leaves the server), not the time when the client receives the message, which means that it includes the link The time spent by all nodes in the transmission process. no-store: Caching is not allowed. It is used for some data that changes very frequently. no-cache: It can be cached, but before using it, you must go to the server to verify whether it is out of date and whether there is the latest version. must-revalidate: If the cache does not expire, you can continue to use it, but if you want to use it after it expires, you must go to the server for verification.

const http = reqire('http')
http.createServer((req, res) => {
    const {url, headers} = req //  url   headers
    if (url === '/') {
      res.end(`
        <html>
          html update Time ${getGMTTime()}
          <script src = '/main.js'></script>
        </html>
      `)
    } else if (url === '/main.js') {
        const content = `document.writeln('<br/> JS Update Time: ${getGMTTime()}')`
        // 
        res.setHeader('Cache-Control', 'max-age=10, no-cahce') // 10 
        res.statusCode = 200
        res.end('content')
    }
    
}).listen(3000, () => {
    console.log('Http is running at 3000 port')
})

 
> Ctrl + F5   'Cache-Control: no-cache'
>  ' '  ' '   'from disk cache',     'Cache-Control' , 

 

Negotiation cache

Last-Modified

The http protocol defines a series of'conditional requests' beginning with'If'

Commonly used are: if-modified-since and if-none-match, the first response message needs to be provided with'Last-Modified' and'Etag' in advance, and then the original in the cache can be brought into the second request. Value to verify whether the resource is up to date. If the resource has not changed, the server will respond with a '304 Not Modified', indicating that the cache is still valid, and the browser can update the validity period, and then use the cache with confidence.

Last-Modified is the last modification time of the file.

const http = reqire('http')
http.createServer((req, res) => {
    const {url, headers} = req //  url   headers
    if (url === '/') {
      res.end(`
        <html>
          html update Time ${getGMTTime()}
          <script src = '/main.js'></script>
        </html>
      `)
    } else if (url === '/main.js') {
        const content = `document.writeln('<br/> JS Update Time: ${getGMTTime()}')`
        // 
        res.setHeader('Cache-Control', 'max-age=5, no-cahce') // 10 
        res.setHeader('Last-Modified', new Date())
        if(new Date(headers['if-modified-since']).getTime() + 10 * 1000 > Date.now()) {
            res.statusCode = 304 //    304
            res.end()
            return
        }
        res.statusCode = 200
        res.end('content')
    }
    
}).listen(3000, () => {
    console.log('Http is running at 3000 port')
})

 

Using Last-Modified will have the following problems

  • A file is modified multiple times in one second
  • The file is updated regularly, but the content has not changed

ETag

Identify resource changes more accurately

ETag has "strong" and "weak" points. Strong ETag requires that resources must match exactly at the byte level. Weak ETag has a "W/" mark in front of the value. It only requires that the resource has no semantic change, but some parts of it may have changed (such as the order of tags in HTML). Adjustment, or a few more spaces).

The working principle of Etag After Etag is generated on the server, the client judges the request to verify whether the resource is modified through the condition of If-Match or If-None-Match. Our common use is to use If-None-Match. The process of requesting a file It may be as follows: A new request client initiates an HTTP GET request for a file (css, image, js); the server processes the request and returns the file content and a bunch of headers (including Etag, such as "2e681a-6-5d044840"), http header status The code is 200.

The second time the same user requests this file, the client initiates an HTTP GET request for a file at a time. Note that at this time, the client sends an If-None-Match header at the same time. This header will include the Etag of the last file (for example, " 2e681a- 6-5d044840"), at this time the server judges the Etag sent and the Etag calculated by itself, so If-None-Match is False (none match is false, it is a match, that is, the two Etags are the same, that is to say The file is not changed), 200 is not returned, 304 is returned, and the client continues to use the local cache;

Note. When the server sets Cache-Control: max-age and Expires, they will be used at the same time, which means that the server can return after checking the modification time and Etag when the If-Modified-Since and If-None-Match are completely matched. 304.