1) If only headers are needed, 'HEAD' should be always preferred over 'GET' because of a simple but not widely known detail: Even if you use 'GET' and immediately abort on readyState === 2 (as suggested by other answers), you will have already received not only the headers, but the full first chunk of information (headers + part of the body) that can vary in size, but usually transfer size will be at least doubled unnecessarily. Using 'HEAD' instead, you can be sure that only headers will be transferred.
2) Content-Length header must be exposed by 'Access-Control-Expose-Headers' to be accessible client-side. If you are dealing with multiple origin resources and you are not sure if Content-Length has been exposed, to prevent exceptions, you can check that, inside an event handler, like this (or other many different ways):
let contentLength = null;
if (checkHeaders(e.target, ['*','Content-Length'])) {
// YOU CAN ACCESS HEADER
contentLength = parseInt(e.target.getResponseHeader("Content-Length"));
} else {
// YOU CAN NOT ACCESS HEADER
console.log('Content-Length NOT AVAILABLE');
}
function checkHeaders(request, headers) {
return (headers.some(function (elem) {
return (request.getResponseHeader("Access-Control-Expose-Headers").includes(elem));
}));
}
3) Content-Length header IS NOT forbidden when any type of encoding is applied (as suggested in some comments). But, be careful that Content-Length will be usually the size of the decoded body (even if it should not). This can be prevented in many different ways, but it is a server-side consideration.