I'm having trouble getting Access-Control-Expose-Headers
from a preflight OPTIONS
for a PUT
request working in a local development environment - the browser seems to ignore it and doesn't expose the header listed to Javascript code.
Below is an example site that you can run locally, and look in the console to see that null
is output rather than the my-value
header value returned from the server.
In /etc/hosts
add two domains:
127.0.0.1 locala.test
127.0.0.1 localb.test
save the below python program to cors.py
, and run it by python cors.py
from http.server import BaseHTTPRequestHandler, HTTPServer
class MyServer(BaseHTTPRequestHandler):
# Serves the website that makes the CORS request
# View on http://locala.test:8080/
def do_GET(self):
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write('''
<html>
<head><title>CORS Example</title></head>
<body>
<p>Web page that makes a CORS request</p>
<script>
fetch('http://localb.test:8080/', {
method: 'PUT'
}).then((response) => {
// Outputs null, when it should output x-my-custom
console.log(response.headers.get('x-my-custom'));
})
</script>
</body>
</html>
'''.encode('utf-8'))
# To be accessed via http://localb.test:8080/
def do_OPTIONS(self):
self.send_response(204)
self.send_header("Access-Control-Allow-Origin", "http://locala.test:8080")
self.send_header("Access-Control-Allow-Methods", "PUT")
self.send_header("Access-Control-Expose-Headers", "x-my-custom")
self.end_headers()
# To be accessed via http://localb.test:8080/
def do_PUT(self):
self.send_response(200)
self.send_header("Access-Control-Allow-Origin", "http://locala.test:8080")
self.send_header("x-my-custom", 'my-value')
self.end_headers()
webServer = HTTPServer(('127.0.0.1', 8080), MyServer)
print('Running. Press CTRL+C to stop.')
try:
webServer.serve_forever()
except KeyboardInterrupt:
pass
webServer.server_close()
print('Stopped')
and then view it in http://locala.test:8080 in a browser.
The Access-Control-Expose-Headers
header belongs, not in the response to a preflight request, but in the response to the associated actual request (i.e. the PUT
request, in your case). Specifying that header in a preflight response has no effect.
For an authoritative source about this, see the relevant section of the Fetch standard:
An HTTP response to a CORS request that is not a CORS-preflight request can also include the following header:
Access-Control-Expose-Headers
(my emphasis)
I recommend you do not implement CORS yourself by manually setting CORS response headers; as you've experienced, doing so, unless you're intimately familiar with the CORS protocol, is error-prone. Instead, rely on some (good) CORS middleware library, if you can find one.