javascriptfetch-api

Fetching API json data alert: Content Security Policy: The page’s settings blocked the loading of a resource at


I've been playing with this API link that allows me to find the duration of a trip between addresses in the format of latitude and longitude. When I paste this link on my browser I get a JSON object and therefore "it is working" (no credentials, etc).

The challenge arises when I would like to fetch the data from the API:

fetch('http://router.project-osrm.org/trip/v1/driving/-43.20940000000002,-22.911;-48.84870000000001,-26.3045?overview=false')
  .then(response => {
    return response.json()
  })
  .then(data => {
    // Work with JSON data here
    console.log(data)
  })
  .catch(err => {
    // Do something for an error here
  })

And I get this error message in the firefox console:

Content Security Policy: The page’s settings blocked the loading of a resource at http://router.project-osrm.org/trip/v1/driving/-43.20940000000002,-22.911;-48.84870000000001,-26.3045?overview=false (“connect-src”).

After trying multiples different method I finally gave up and came here seeking for help. Therefore, could someone explain me what is wrong with the request? Also, why does it works when I paste on my browser but not when I do it by fetch? Just one more, I've realized that my posted code snipped in this question returns me the data I would like to receive. Why in this context it works?


Solution

  • It is failing because of a Content Security Policy, or CSP, which is enforcing a policy for connect-src, which limits domains you can connect to for WebSockets, EventSource, and XHR (which is what fetch() implements). The error you see says exactly this, although maybe not in the most clear to read manner.

    What this means, is that on the page where you are trying to run this code, there is either a <meta http-equiv="Content-Security-Policy" content="..."> tag somewhere in the <head>, or the request to GET the page has a Content-Security-Policy header. Furthermore, the actual CSP string will have a connect-src value in it, or some value that sets connect-src like default-src. For example, if you see connect-src 'self';, that would mean that you are prohibited/blocked from using fetch() with any other domain other than the site where it is running (same-origin).

    Use dev tools inspector to look for the meta tag, and the network requests panel to check for the header. The only solution to your problem is to either remove or modify this tag/header to allow the router.project-osrm.org domain to be connected to, either explicitly by adding the domain as an allowed domain, or implicitly, by allowing any domain to be connected to for content-src (more risky).

    For understanding how CSP is implemented, please take a look at Google's web fundamentals page on the topic. I also highly recommend content-security-policy.com/ as a quick reference.


    Why does this work elsewhere?

    Your code works here on StackOverflow, because SO does not have a CSP connect-src policy. Same thing when you try it in your browser's console in a blank tab or on most sites. However, I can fake it to prove that this is the issue, by slightly modifying your snippet and injecting a CSP meta-tag:

    // Add CSP meta tag that will block cross-origin AJAX
    var cspMetaTag = document.createElement('meta');
    cspMetaTag.setAttribute('http-equiv', 'Content-Security-Policy');
    cspMetaTag.setAttribute('content', "connect-src 'self';");
    document.querySelector('head').appendChild(cspMetaTag);
    
    fetch('http://router.project-osrm.org/trip/v1/driving/-43.20940000000002,-22.911;-48.84870000000001,-26.3045?overview=false')
        .then(response => {
            return response.json()
        })
        .then(data => {
            // Work with JSON data here
            console.log(data)
        })
        .catch(err => {
            // Do something for an error here
            console.log(err.toString());
        });

    Open your browser console when running the above snippet to see the actual CSP blocking message in your browser.