If we make a fetch from an arbitrary web-site, that will probably fail.
The core concept here is origin – a domain/port/protocol triplet.
Cross-origin requests – those sent to another domain (even a subdomain) or protocol or port – require special headers from the remote side. That policy is called “CORS”: Cross-Origin Resource Sharing.
For instance, let’s try fetching http://example.com:
try {
await fetch('http://example.com');
} catch(err) {
alert(err); // Failed to fetch
}
Fetch fails, as expected.
Why? A brief history
Because cross-origin restrictions protect the internet from evil hackers.
Seriously. Let’s make a very brief historical digression.
For many years a script from one site could not access the content of another site.
That simple, yet powerful rule was a foundation of the internet security. E.g. a script from the page hacker.com could not access user’s mailbox at gmail.com. People felt safe.
JavaScript also did not have any special methods to perform network requests at that time. It was a toy language to decorate a web page.
But web developers demanded more power. A variety of tricks were invented to work around the limitation.
Using forms
One way to communicate with another server was to submit a <form> there. People submitted it into <iframe>, just to stay on the current page, like this:
<!-- form target -->
<iframe name="iframe"></iframe>
<!-- a form could be dynamically generated and submited by JavaScript -->
<form target="iframe" method="POST" action="http://another.com/…">
...
</form>
So, it was possible to make a GET/POST request to another site, even without networking methods. But as it’s forbidden to access the content of an <iframe> from another site, it wasn’t possible to read the response.
…Okay, in fact there actually were tricks for that (required special scripts at both remote and our page), but let’s not delve deeper. Nothing good in those for us now.
Using scripts
Another trick was to use a <script src="http://another.com/…"> tag. A script could have any src, from any domain. But again – it was impossible to access the raw content of such script.
If another.com intended to expose data for this kind of access, then a so-called “JSONP (JSON with padding)” protocol was used.
Let’s say we need to get the data from http://another.com this way:
-
First, in advance, we declare a global function to accept the data, e.g.
gotWeather.// 1. Declare the function to process the data function gotWeather({ temperature, humidity }) { alert(`temperature: ${temperature}, humidity: ${humidity}`); } -
Then we make a
<script>tag withsrc="http://another.com/weather.json?callback=gotWeather", please note that the name of our function is itscallbackparameter.let script = document.createElement('script'); script.src = `http://another.com/weather.json?callback=gotWeather`; document.body.append(script); -
The remote server dynamically generates a script that calls
gotWeather(...)with the data it wants us to receive.// The expected answer from the server looks like this: gotWeather({ temperature: 25, humidity: 78 }); -
As the script executes,
gotWeatherruns, and, as it’s our function, we have the data.
That works, and doesn’t violate security, because both sides agreed to pass the data this way. And, when both sides agree, it’s definitely not a hack. There are still services that provide such access, as it works even for very old browsers.
After a while, networking methods appeared, such as XMLHttpRequest.
At first, cross-origin requests were forbidden. But as a result of long discussions, cross-domain requests were allowed, in a way that does not add any capabilities unless explicitly allowed by the server.
Simple requests
There are two types of cross-domain requests:
- Simple requests.
- All the others.
Simple Requests are, well, simpler to make, so let’s start with them.
A simple request is a request that satisfies two conditions:
- Simple method: GET, POST or HEAD
- Simple headers – the only allowed custom headers are:
Accept,