0

It is unclear whether my RESTful CORS responses are ever retrieved from the cache. They all had the 200 status code but never 304; even though there is no change in the request, the response, and the If-None-Match and Etag headers.

The response headers are as follows:

Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: http://www.acme.com
Access-Control-Max-Age: 86400
Cache-Control: no-cache
Pragma: no-cache
Vary: Cookie, Origin

While the preflight requests are no longer invoked on a per-request basis, all other XHR calls are returning 200 HTTP response.

This problem only happens for cross-origin requests.

Edit: Following is a redacted excerpt:

import express from 'express';
import cors from 'cors';

const server = express();
server
    .set( 'etag', 'strong' )
    .use( cors({
        credentials: true,
        maxAge: 86400,
        origin: 'http://www.acme.com'
    }) )
    .use(( req, res, next ) => {
        res.setHeader( 'Cache-Control', 'no-cache' );
        res.setHeader( 'Pragma', 'no-cache' );
        res.setHeader( 'Vary', `Cookie, ${ res.get( 'Vary' ) }` );
        next();
    })
    ...
Stephen Isienyi
  • 1,292
  • 3
  • 17
  • 29
  • The Access-Control-Max_Age is already in the headers. As for the Cache-control header, `no-cache` is an abbreviation for `max-age=0, must-revalidate` which means "cache the response but always confirm with the server before using the cached response" – Stephen Isienyi Aug 08 '22 at 17:45
  • 1
    According to the definition of `no-cache` (thanks for that!), the client should send an `If-None-Match: ` header with every subsequent request. But this leads to a 304 response only if the server implements ETag handling. Is that the case? Can you share the relevant code? – Heiko Theißen Aug 09 '22 at 05:53
  • Also, does the response contain an `ETag` header? Without that, the browser cannot send an `If-None-Match` header. Or does the response contain a `Last-Modified` header? – Heiko Theißen Aug 09 '22 at 06:19
  • The client sends the If-None-Match: on every request. The If_None_Match request header equals the Etag response header. The response header does not contain the Last-Modified header though. – Stephen Isienyi Aug 09 '22 at 14:16
  • Can you share the relevant server code that implements ETag handling? – Heiko Theißen Aug 09 '22 at 15:07
  • Yes, I have now appended the snippet. – Stephen Isienyi Aug 09 '22 at 17:22

1 Answers1

1

Seems to be a known issue of Chrome.

When processing a conditional request where the response matches the ETag, express sets status 304 and returns an empty payload, see here. You can verify this with

> curl -v -H "If-None-Match:..." http://server/path
< HTTP/1.1 304 Not Modified
< X-Powered-By: Express
< Access-Control-Allow-Origin: *
< Cache-Control: no-cache
< ETag: "..."

This server behavior is independent of whether the request is cross-origin or not.

When the Chrome browser receives this response, it takes the payload from its cache and displays it in the network trace, together with status 304. However, in case of a cross-origin request, it displays status 200 instead. But this is an artifact of the browser alone, and has got nothing to do with the server.

Heiko Theißen
  • 12,807
  • 2
  • 7
  • 31