Preflight Access-Control-Expose-Headers ignored in CORS on HTTP locally

Question:

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.

Asked By: Michal Charemza

||

Answers:

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)

Answered By: jub0bs
Categories: questions Tags: , , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.