I created this repository to investigate why my custom headers were not being sent to the client when the response code is 304
.
I had previously read answers to this question, which explains why headers are not sent on a 304
response. However, I noticed something strange. I ran the tests below against the same application (observing x-test-header
, which I generate randomly for every request), first directly to the application, then via Apache reverse proxy. Interestingly, they behaved the same. I received a different header on both cases even when I receive a 304
status code
const chaiHttp = require('chai-http');
const chai = require('chai');
const assert = chai.assert;
const server = require('../main');
chai.use(chaiHttp);
suite("chai etag support", () => {
test("repeating a request to /data with if-none-match should give 304", async () => {
let res = await chai
.request(server)
.get('/data');
const etag = res.get('etag');
res = await chai
.request(server)
.get('/data')
.set('if-none-match', etag);
assert.equal(res.status, 304);
});
});
function behaviourTest(server, suiteName) {
suite(suiteName, function() {
test('Two subsequent GET /data requests should give the same etag', async () => {
const firstRequest = await chai
.request(server)
.get('/data');
const secondRequest = await chai
.request(server)
.get('/data');
assert.notEqual(secondRequest.get('x-test-header'), firstRequest.get('x-test-header'), "x-test-headers should be different");
assert.equal(secondRequest.get('etag'), firstRequest.get('etag'), "Etags should be the same");
const etag = secondRequest.get('etag');
const thirdRequest = await chai
.request(server)
.get('/data')
.set('if-none-match', etag);
assert.equal(thirdRequest.status, 304);
assert.notEqual(thirdRequest.get('x-test-header'), secondRequest.get('x-test-header'), "x-test-headers should be different");
});
});
}
behaviourTest(process.env.URL, 'Without reverse proxy');
behaviourTest(process.env.REVERSE_PROXY_URL, 'With reverse proxy');
The above got me to assume that Apache is sending the headers every time. I then ran basically the same tests but inside an HTML page. Only then did the requests via the reverse proxy fail to send an updated x-test-header
value. Or maybe it got sent, and the browser ignored it and used the value from the cache.
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Etag Test</title>
<style>
.full-screen {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.vh-align {
display: flex;
align-items: center;
justify-content: center;
}
#div-results {
width: 360px;
min-height: 400px;
font-family: 'Courier New', Courier, monospace;
border: 1px solid #ccc;
border-radius: 2px;
overflow-y: auto;
background-color: black;
color: white;
padding: 20px;
}
.red-text {
color: red;
}
.green-text {
color: green;
}
</style>
</head>
<body>
<div class="full-screen vh-align">
<div id="div-results">
TEST RESULTS
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chai/4.3.6/chai.min.js" integrity="sha512-sP7hp2cpEWsmOHQPc6taNuXFmvG59BhI3YS3tOvZ+nDEqOyyORLiY3nBjLkWgCNI8SFZ5koUyiBO42Q3wzZDrQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.27.2/axios.min.js" integrity="sha512-odNmoc1XJy5x1TMVMdC7EMs3IVdItLPlCeL5vSUPN2llYKMJ2eByTTAIiiuqLg+GdNr9hF6z81p27DArRFKT7A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script>
function getHeadersFromFetchAPIResponse(res) {
const keys = Array.from(res.headers.keys());
const headers = {};
for (let i in keys) {
const key = keys[i]
headers[key] = res.headers.get(key);
}
return headers;
}
async function test(url, suiteName) {
let icon, message = '';
try {
const firstRes = await fetch(`${url}/data`);
const secondRes = await fetch(`${url}/data`);
assert.equal(firstRes.headers.get('etag'), secondRes.headers.get('etag'),);
assert.notEqual(firstRes.headers.get('x-test-header'), secondRes.headers.get('x-test-header'));
icon = tick;
} catch (err) {
icon = xMark;
message = `<br>${tab}${String(err)}`;
console.log(err);
}
document.getElementById('div-results').innerHTML += `<br>${icon} ${suiteName}${message}`;
}
const { assert } = chai;
const proxiedUrl = window.prompt("Reverse proxied url");
const url = window.prompt("URL without proxy");
const tick = '<span class="green-text">✓</span>';
const xMark = '<span class="red-text">✗</span>';
const tab = ' ';
(async () => {
await test(url, "No reverse proxy");
await test(proxiedUrl, "Reverse proxy");
})();
</script>
</body>
</html>
Why am I getting different behaviors in Node instead of the browser?