41

I need to make a CORS post request. I need to use fetch because axios's response is already processed to json.

But in fetch response, the headers is empty. But I don't think this is the server problem since axios response headers have the values that I want.

Any trick?

    fetch('http://localhost:9876/test/sample-download', {
        method: 'post',
        headers: {},
        body: {}
    })
        .then(response => {
            console.log(response);
        })
        .catch(err => console.error(err));

    axios.post('http://localhost:9876/test/sample-download', {}, {})
        .then(response => console.log(response))
        .catch(error => console.error(error));
VLAZ
  • 26,331
  • 9
  • 49
  • 67
william
  • 7,284
  • 19
  • 66
  • 106
  • Have you tried to construct a request object using `var myRequest = new Request(url, opts)` and then passing it to `fetch(myRequest)`? What does `console.log(response.headers)` show then? – Alexander Jan 24 '18 at 03:15
  • @Alexander I just tried. no luck . `let myReq = new Request('http://localhost:9876/test/sample-download', { method: 'post', headers: {}, body: JSON.stringify({}), cache: 'no-cache' }); fetch(myReq) .then(response => { console.log(response); }) .catch(err => console.error(err));` – william Jan 24 '18 at 16:22
  • 2
    I suspect it's because the Headers class instance that fetch returns is an iterable, as opposed to a plain object like axios returns. An iterable's data isn't viewable from the console, you have to iterate it and console.log each element, like `response.headers.forEach(console.log)`. Try that maybe? – ccnokes Jan 24 '18 at 21:00
  • @ccnokes Thanks. that solved my issue. If you could post that as an answer, I can mark it. – william Jan 24 '18 at 21:32

6 Answers6

91

The Headers class instance that fetch returns is an iterable, as opposed to a plain object like axios returns. Some iterable's data, such as Headers or URLSearchParams, aren't viewable from the console, you have to iterate it and console.log each element, like:

fetch('http://localhost:9876/test/sample-download', {
    method: 'post',
    headers: {},
    body: {}
})
.then(response => {
  // Inspect the headers in the response
  response.headers.forEach(console.log);
  // OR you can do this
  for(let entry of response.headers.entries()) {
    console.log(entry);
  }
})
.catch(err => console.error(err));
ccnokes
  • 6,885
  • 5
  • 47
  • 54
  • 1
    Hopefully Chrome changes the display of Headers in dev tools to display like Map or Set does. Those types give helpful previews of the entries. They also have an `[[Entries]]` property, so maybe that's the difference. – ccnokes Nov 06 '19 at 20:09
  • This and the answer from @AndyTheEntity helped immensely! Thank you! – Robson William Nov 06 '21 at 19:24
  • the .forEach(console.log) part spams my console with duplicates of the same headers – Welcor Apr 16 '23 at 18:09
85

Headers are limited for CORS requests. See https://stackoverflow.com/a/44816592/2047472

(Use access-control-expose-headers to allow exposing headers to requests from a different origin.)

AndyTheEntity
  • 3,396
  • 1
  • 22
  • 19
37

Although the correct answer is the one from @AndyTheEntity for me is a little bit incomplete.

CORS requests have limitations and show only a subset of headers:

  • Cache-Control
  • Content-Language
  • Content-Type
  • Expires
  • Last-Modified
  • Pragma

In order to show more headers on the response, the server has to add a header to allow more extra headers.

For example, after a POST request if a new resource is created you should return a 201 CREATED response and add a Location header. If you need to accept CORS also you need to add the next headers (on the server response):

Location: $url-of-created-resource
Access-Control-Expose-Headers: Location

With this you will see on the client the header Location.

If you need to send an specific header (like a SESSION_ID), then you need to add the next header in the request:

Access-Control-Request-Headers: SESSION_ID
SESSION_ID: $current_session_id

I hope this cover all needs to handle request/response using CORS.

Carlos Verdes
  • 3,037
  • 22
  • 20
  • 2
    also `Access-Control-Expose-Headers: *` to expose all headers or `Access-Control-Expose-Headers: Header1, Header2` to expose multiple. [MDN docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers) – undefined Aug 04 '21 at 08:26
15

To get a specific header property you can use the following:

response.headers.get(yourProperty)
Stephen King
  • 581
  • 5
  • 18
  • 31
4

Another option is to use Object.fromEntries(), for instance, Object.fromEntries(response.headers) or Object.fromEntries(response.headers.entries()). This method transforms a list of key-value pairs into an object.

fetch("https://www.google.com").then(response => console.log(Object.fromEntries(response.headers)))
//-> Promise <pending>

// console.log output:
{
    "alt-svc": "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"",
    "bfcache-opt-in": "unload",
    "cache-control": "private, max-age=0",
    "content-encoding": "br",
    "content-length": "41556",
    "content-security-policy": "upgrade-insecure-requests",
    "content-type": "text/html; charset=UTF-8",
    "date": "Wed, 04 Aug 2021 08:09:30 GMT",
    "expires": "-1",
    "server": "gws",
    "strict-transport-security": "max-age=31536000",
    "x-frame-options": "SAMEORIGIN",
    "x-xss-protection": "0"
}

Note: This is currently not supported in Internet Explorer and Opera Android, see browser compatibility on MDN docs.

undefined
  • 1,019
  • 12
  • 24
0

this one from another answer here spams my console with duplicates of the .headers content:

response.headers.forEach(console.log)

But this one works, it creates a map:

console.log(...response.headers)

or for a better readable console output check this answer: https://stackoverflow.com/a/70821115/4394435

Welcor
  • 2,431
  • 21
  • 32