A simple same origin fetch("fetch.xml")
fails here with the console error message
Access to fetch at 'http://127.0.0.1:8000/fetch.xml' from origin 'null' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header has a value 'http://127.0.0.1:8000' that is not equal to the supplied origin. Have the server send the header with a valid value, or, if an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
The corresponding network headers are
General
Request URL: http://127.0.0.1:8000/fetch.xml
Referrer Policy: origin
Response Headers
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Origin: http://127.0.0.1:8000
Access-Control-Max-Age: 86400
Content-Security-Policy: sandbox allow-scripts
Content-Type: text/xml
Date: Sun, 16 Aug 2020 06:20:43 GMT
Referrer-Policy: origin
Server: SimpleHTTP/0.6 Python/3.8.5
Request Headers
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7
Cache-Control: no-cache
Connection: keep-alive
DNT: 1
Host: 127.0.0.1:8000
Origin: null
Pragma: no-cache
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36
As you can see the browser (here Chrome) says Origin = null, although the origin is the same origin. I can also reproduce this with a real domain instead of the ip address, the origin of the fetch is always null according to the browser.
If I change the fetch statement to fetch("fetch.xml", { mode: "same-origin" })
the browser still assumes null to be the origin, but the error message changes to
Fetch API cannot load http://127.0.0.1:8000/fetch.xml. Request mode is "same-origin" but the URL's origin is not same as the request origin null.
How can I get the browser to work with the right origin and not null for the fetch statement? I played around with the referrer-policy as you can see. I do not want Access-Control-Allow-Origin to include null or *, as this seems too open (basically disabling CORS).
Remark: I am not accessing the files by file://
URLs! This is a real web server and the origin is still null! So this question is not the same as the duplicate stackoverflow has linked to.
Minimum Reproducing Example
Simple server including everything if you want to test it quickly (simply run it with python3):
#!/usr/bin/env python3
from http.server import HTTPServer, HTTPStatus, SimpleHTTPRequestHandler, test
import sys
from urllib.parse import urlparse
import os
class TestRequestHandler (SimpleHTTPRequestHandler):
def do_GET(self):
"""Serve a GET request."""
url = urlparse(self.path)
if url.path == '/' or url.path == '/index.html': # index.html
f = self.send_head('text/html')
self.wfile.write('''<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<script type="text/javascript" src="control.js"></script>
</head>
<body>
</body>
</html>'''.encode('utf-8'))
elif url.path == '/control.js': # control.js will try to fetch the fetch.xml
f = self.send_head('application/javascript')
self.wfile.write('''function init() {
fetch("fetch.xml", { mode: "same-origin" }).then(r => r.text()).then(t => console.log(t));
}
window.addEventListener("load", init);'''.encode('utf-8'))
elif url.path == '/fetch.xml': # fetch.xml
f = self.send_head('text/xml')
self.wfile.write('''<?xml version="1.0" encoding="UTF-8"?>
<root>
<test>Hi</test>
</root>'''.encode('utf-8'))
def send_head(self, ctype):
self.send_response(HTTPStatus.OK)
self.send_header('Content-Type', ctype)
self.send_header('Access-Control-Allow-Origin', 'http://127.0.0.1:8000')
self.send_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
self.send_header('Access-Control-Max-Age', 86400)
self.send_header('Referrer-Policy', 'origin')
self.send_header('Content-Security-Policy', 'sandbox allow-scripts')
SimpleHTTPRequestHandler.end_headers(self)
if __name__ == '__main__':
test(TestRequestHandler, HTTPServer, port=int(sys.argv[1]) if len(sys.argv) > 1 else 8000)
If you prefer to put it on your own server with different files:
index.html
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<script type="text/javascript" src="control.js"></script>
</head>
<body>
</body>
</html>
control.js
function init() {
fetch("fetch.xml", { mode: "same-origin" }).then(r => r.text()).then(t => console.log(t));
}
window.addEventListener("load", init);
fetch.xml
<?xml version="1.0" encoding="UTF-8"?>
<root><test>Hello</test></root>
The server should be set up to send the following headers (adapt the ip/domain):
Access-Control-Allow-Origin: http://127.0.0.1:8000
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Max-Age: 86400
Referrer-Policy: origin
Content-Security-Policy: sandbox allow-scripts