This is a draft, expect sudden changes and come back later
Writing browser clients
The problem with direct browser-to-API communication is that you normally cannot identify yourself with a
User-Agent header, and risk being blocked. Normally the request will include an
Referer header, which may be enough to get you out of trouble if it contains a reachable domain name with contact info (i.e. not
This usually works, e.g. for images which does not contain time information:
Since the request will usually include a
Referer header identifying the client website, this is sufficient identification for TOS purposes. However, if
Referer is missing (this can be configured in HTML) the users of the site are liable to getting throttled.
Some requests don’t trigger a CORS preflight. Those are called “simple requests” in this article, though the Fetch spec (which defines CORS) doesn’t use that term. A “simple request” is one that meets all the following conditions (source):
One of the allowed methods:
Apart from the headers automatically set by the user agent (for example,
User-Agent, or the other headers defined in the Fetch spec as a “forbidden header name”), the only headers which are allowed to be manually set are those which the Fetch spec defines as a “CORS-safelisted request-header”, which are:
The only allowed values for the
No event listeners are registered on any
XMLHttpRequestUploadobject used in the request; these are accessed using the
ReadableStreamobject is used in the request.
CORS Simple Requests are incompatible with any form of HTTP authentication (Basic Auth, OAUTH2, static bearer tokens etc). This means that you cannot use the Frost API with simple requests, and in the future possibly also api.met.no if we decide to implement some form of authentication.
Unlike “simple requests” (discussed above), “preflighted” requests first send an HTTP request by the
OPTIONS method to the resource on the other domain, to determine if the actual request is safe to send. Cross-site requests are preflighted like this since they may have implications to user data.
The following is an example of a request that will be preflighted:
const xhr = new XMLHttpRequest(); xhr.open('POST', 'https://bar.other/resources/post-here/'); xhr.setRequestHeader('X-PINGOTHER', 'pingpong'); xhr.setRequestHeader('Content-Type', 'application/xml'); xhr.onreadystatechange = handler; xhr.send('<person><name>Arun</name></person>');
The example above creates an XML body to send with the
POST request. Also, a non-standard HTTP
X-PINGOTHER request header is set. Such headers are not part of HTTP/1.1, but are generally useful to web applications. Since the request uses a Content-Type of
text/xml, and since a custom header is set, this request is preflighted.
When responding to a credentialed request, the server must specify an origin in the value of the
Access-Control-Allow-Origin header, instead of specifying the “
Since it is impossible for api.met.no to list all the thousands of different sites using the Weather API, we cannot include your website in the
Access-Control-Allow-Origin header (the list would simply be too long). Because of this, you cannot use the Frost API with authentication either.
The only fully supported solution is for you to set up a local CORS proxy which adds the necessary identification (
User-Agent) or authentication header. At this point is you might as well implement local caching at the same time, which will save you time later if your application starts to generate significant traffic.
For more information, see Setting up your own caching proxy server using Nginx.