patchetag

The browser doesn't send If-Match header on PATCH request


The browser (Chrome / Firefox) doesn't send If-Match request header in PATCH request to already cached resource.

I made a test web page with 2 buttons, one for GET request the other for PATCH and the script which sends requests using Fetch API.

    document.getElementById('get-btn').addEventListener('click', async () => {
        await fetch("http://test.local/api/v1/test", {
            method: 'GET',
            headers: {
                'Accept': 'application/hal+json',
                'Authorization': 'Bearer XXXXX',
            }
        });
    });
    document.getElementById('patch-btn').addEventListener('click', async () => {
        await fetch("http://test.local/api/v1/test", {
            method: 'PATCH',
            headers: {
                'Accept': 'application/hal+json',
                'Authorization': 'Bearer XXXXX',
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({})
        });
    });

The API endpoint always returns an Etag response header for GET method. The Etag value is some md5 hash (e.g. "ffsd8fsd9adsf"). Test scenario:

  1. First click on button that initiates the GET request - server returns 200 with an Etag response header.
  2. Second click on button that initiates the GET request - browser sends If-None-Match request header with an etag value - server returns 304 with an Etag response header.
  3. First click on button that initiates the PATCH request - browser doesn't send If-Match request header with an etag value - server returns 428 Precondition Required.

Note: Access-Control-Allow-Headers allows If-Match.

Now, my question: Is it me responsible for sending If-Match request header in PATCH request or I'm missing something that prevents browser from adding If-Match request header in same way as it automatically adds If-None-Match for GET request ?
If it's my responsibility to include If-Match header then what is an explanation for inconsistent behavior of browsers that they add If-None-Match to GET request but don't add If-Match to PATCH request ?


Solution

  • Your code, not the browser, is responsible for adding the If-Match header. That's because whether or not to do so is a question of application logic.

    The PATCH standard mentions this (and the same is true for PUT and POST):

    A PATCH request can be issued in such a way as to be idempotent, which also helps prevent bad outcomes from collisions between two PATCH requests on the same resource in a similar time frame.... Clients using this kind of patch application SHOULD use a conditional request such that the request will fail if the resource has been updated since the client last accessed the resource. For example, the client can use a strong ETag in an If-Match header on the PATCH request.

    There are also cases where patch formats do not need to operate from a known base-point (e.g., appending text lines to log files, or non- colliding rows to database tables), in which case the same care in client requests is not needed.

    The browser doesn't know what kind of application you have, so it would be inappropriate to automatically add the header just because it has a record of the current ETag.

    By contrast, the conditional request logic behind If-None-Match has nothing to do with application logic; it's just a performance enhancement. If the conditional request is successful the client already has the same resource that the server would have sent, so there's no reason to send it again. So it's safe for the browser to handle this for you.