javascriptphpcachingfetch-apietag

How to let the browser or PHP cache a fetch() request?


This is basically the opposite of fetch(), how do you make a non-cached request?.

Let's say we have client-side:

<div onclick="fetch('/data.php').then(r => r.text()).then(s => console.log(s));">Click me</div>

and server-side in PHP:

<?php echo "hello"; /* in reality, this can be much more data 
                       and/or updated later with other content */ ?>

When clicking multiple times on Click me, the request is done multiple times over the network with request response code "200".

Thus, no caching has been done by the browser / by PHP:

enter image description here

How to prevent this data to be transferred multiple times if it is in fact not needed here?

Potential solution: Since /data.php can be updated on the server, the server could serve the data requests with a datetime metadata timestamp, that I could store in a client-side JS variable timestamp. Then before doing the next fetch request for /data.php, I could do a preliminary request

fetch('/get_last_modification_date.php').(r => r.json()).then(data => {
    if (data['timestamp'] != timestamp)
        fetch('/data.php')...       // if and only if new data is here
});

Thus, we only do a fetch('/data.php')... if the timestamp of this data is more recent than the one we already had.

This would work, but it seems we are manually doing something that could be done by the browser+PHP cache mechanism.

How to ask PHP and the browser to do all this caching for us?


Solution

  • fetch() behaves the same as a regular HTTP request, so you can just apply the standard HTTP rules regarding caching. For example you can use the If-Modified-Since mechanism, that I will explain.

    When a server returns the following headers for a resource:

    Cache-Control: no-cache
    Last-Modified: <date in RFC2616 format>
    

    then, on subsequent requests, the browser will send an If-Modified-Since header with the Last-Modified date. If the server returns a 304 (Not Modified) status, then the browser will use the cached version of the resource.

    Remark: despite its name, the no-cache directive doesn't mean that the resource can't be cached; it just means that the browser must validate it with the server before using it.

    To illustrate this, I will assume that you want to send the contents of some data.txt file:

    <?php
    // Get the modification time of the resource
    $dataTime = filemtime('data.txt');
    
    if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']))
    {
        $sinceTime = strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']);
        if($sinceTime==$dataTime)
        {
            // The browser already has the latest version of the resource
            header('HTTP/1.1 304 Not Modified');
            exit;
        }
    }
    
    // Send the resource
    header('Cache-Control: no-cache');
    header('Last-Modified: '.gmdate('D, d M Y H:i:s T', $dataTime));
    header('Content-Type: text/plain; charset=UTF-8');
    readfile('data.txt');
    ?>