11

We have an api endpoint that redirects to another server. It's called via XHR and seems to work fine in most browsers, except for Safari (specifically on iOS).

The error I'm getting in the console is: Cross-origin redirection denied by Cross-Origin Resource Sharing policy

We have CORS on the page that does the redirection and on the other server. The redirection page sets:

Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: false

The other server has:

Access-Control-Allow-Origin: *

How can I allow redirection in the CORS policy?

Noodles
  • 900
  • 4
  • 14
  • 30
  • If the cross-origin XHR works in other browsers but not in Safari, it sounds like a bug in Safari that’s making it non-conforming to the current CORS requirements specified in https://fetch.spec.whatwg.org/. So please consider filing a bug at https://bugs.webkit.org/ – sideshowbarker Nov 21 '16 at 01:48
  • It probably is a bug with Safari, but I was hoping there was a workaround. – Noodles Nov 21 '16 at 02:01
  • check this http://stackoverflow.com/questions/5750696/how-to-get-a-cross-origin-resource-sharing-cors-post-request-working?rq=1 – nmanikiran Nov 24 '16 at 04:50
  • Thanks, but nothing mentioned there seems to work. – Noodles Nov 24 '16 at 22:37
  • Please refer to this link http://stackoverflow.com/questions/16824661/cors-request-not-working-in-safari . Hope it helps. I faced the same issue with an ASP .net website. We were unable to resolve it at that time. – Nevermore Nov 29 '16 at 10:58

7 Answers7

11

The W3C specification and other authoritative sources directly forbid wildcard Access-Control-Allow-Origin when used with Access-Control-Allow-Credentials: true.

Note: The string "*" cannot be used for a resource that supports credentials.

https://www.w3.org/TR/cors/#resource-requests

Important note: when responding to a credentialed request, server must specify a domain, and cannot use wild carding.

https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials

If credentials mode is "include", then Access-Control-Allow-Origin cannot be *.

https://fetch.spec.whatwg.org/#cors-protocol-and-credentials

Further steps

Because your question lacks details, let's make some definitions:

  • domain-a is the domain of your client code
  • domain-b is the domain of the endpoint where your make request to
  • domain-c is the domain where the request is finally redirected to

First of all, I think, you want to make a workaround. As long as you told all endpoint are under your control, so you either:

  • Make direct request from domain-a to domain-c (or even make a conditional request, if redirect depends on parameter)
  • Expose another endpoint in your backend that wraps request to domain-b

It is also important to report a bug to WebKit tracker if it really violates the spec. To easier reproduce the case I made a CherryPy app, which you could attach to the report. The step to run it:

  1. Add "127.0.0.1 domain-a domain-b domain-c" to your /etc/hosts
  2. Put the following code into corsredirect.py
  3. Run these commands in terminal

     virtualenv -p python3 venv
     . venv/bin/activate
     pip install cherrypy
     python corsredirect.py
    
  4. Point your browser to http://domain-a:8080 and push the button

There's the app.

#!/usr/bin/env python3
'''
Add localhost aliases in /etc/hosts for "domain-a", "domain-b", "domain-c".

The flow is: [domain-a] --CORS-GET--> [domain-b] --redirect--> [domain-c].
Open as http://domain-a:8080/
'''


import cherrypy


def cors():
  cherrypy.response.headers['Access-Control-Allow-Origin'] = '*'

cherrypy.tools.cors = cherrypy._cptools.HandlerTool(cors)


class App:

  @cherrypy.expose
  def index(self):
    return '''<!DOCTYPE html>
      <html>
      <head>
      <meta content='text/html; charset=utf-8' http-equiv='content-type'>
      <title>CORS redirect test</title>
      </head>
      <body>
        <button>make request</button>
        <script type='text/javascript'>
          document.querySelector('button').onclick = function()
          {
            var xhr = new XMLHttpRequest();
            xhr.open('GET', 'http://domain-b:8080/redirect', true);
            xhr.onload = function()
            {
              var text = xhr.responseText;
              console.log('success', text);
            };
            xhr.onerror = function()
            {
              console.error('failure');
            };
            xhr.send();
          };
        </script>
      </body>
      </html>
    '''

  @cherrypy.expose
  @cherrypy.config(**{'tools.cors.on': True})
  def redirect(self):
    raise cherrypy.HTTPRedirect('http://domain-c:8080/endpoint')

  @cherrypy.expose
  @cherrypy.config(**{'tools.cors.on': True})
  @cherrypy.tools.json_out()
  def endpoint(self):
    return {'answer': 42}


if __name__ == '__main__':
  config = {
    'global' : {
      'server.socket_host' : '127.0.0.1',
      'server.socket_port' : 8080,
      'server.thread_pool' : 8
    }
  }
  cherrypy.quickstart(App(), '/', config)
saaj
  • 23,253
  • 3
  • 104
  • 105
  • I have also tried it with `Access-Control-Allow-Credentials: false` but it doesn't fix this problem. On my production code I have this set to false already. – Noodles Nov 29 '16 at 22:31
  • @Noodles To make it clear that the `Access-Control-Allow-Credentialsy value is not the problem, tou might want to consider updating the example code in the question to use `Access-Control-Allow-Credentials: false` and not use `Access-Control-Allow-Credentials: true`. Because as-is it’s a bug. – sideshowbarker Nov 29 '16 at 23:31
  • @Noodles I've updated my answer. Can you verify that Safari fails the request in the app? – saaj Nov 30 '16 at 17:27
3

Enabling HTTPS on both servers has solved the problem for me.

gray75c
  • 39
  • 2
1

I was running into a similar problem hosting my API on a Heroku app. On Chrome and Firefox, requests to my API from another domain were working perfectly, but on Safari, I was getting a frustrating "Cross-origin redirection denied by Cross-Origin Resource Sharing policy".

After some researching, it seems there might be a bug in Safari that prevents some redirects with CORS. I was able to get around this by requesting the Heroku app directly (myapp.herokuapp.com/api) instead of to my domain (mydomain.com/api).

If you've got a setup where there are redirects to your API, requesting the root domain directly might help.

Community
  • 1
  • 1
Rafi
  • 565
  • 1
  • 4
  • 15
1

If you can change your url, you can get a try with a proxy pass configuration on your Apache. The call will look like to be on the same domain but it will not. https://httpd.apache.org/docs/current/fr/mod/mod_proxy.html

Michael
  • 1,063
  • 14
  • 32
1

Did you try?

Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: "Origin, X-Requested-With, Content-Type, Accept"
Access-Control-Allow-Methods: *

For more check : http://enable-cors.org/

Nicholas K
  • 15,148
  • 7
  • 31
  • 57
Nur Rony
  • 7,823
  • 7
  • 38
  • 45
1

For just about anything other than a "Simple request", the authoritative sources deny wildcard for the "Access-Control-Allow-Origin" header and require you to explicitly set the header "Access-Control-Allow-Headers".

Here is what Mozilla states for "Simple request":

The only allowed methods are:

  • GET
  • HEAD
  • POST

Apart from the headers set automatically by the user agent (e.g. Connection, User-Agent, etc.), the only headers which are allowed to be manually set are:

  • Accept
  • Accept-Language
  • Content-Language
  • Content-Type

The only allowed values for the Content-Type header are:

  • application/x-www-form-urlencoded
  • multipart/form-data
  • text/plain

When your request doesn't meet the "Simple Request" requirements you probably fall into "Preflighted Requests".

methods other than GET, HEAD or POST. Also, if POST is used to send request data with a Content-Type other than application/x-www-form-urlencoded, multipart/form-data, or text/plain, e.g. if the POST request sends an XML payload to the server using application/xml or text/xml, then the request is preflighted.

As for credentialed requests -

Important note: when responding to a credentialed request, server must specify a domain, and cannot use wild carding. Access-Control-Allow-Origin: *

Since you didn't provide the actual HTTP requests and responses from your page and servers I will have to make some assumptions. I am assuming that your page is loaded under the domain foo.example and that your API is under the same foo.example domain and that your "other" server is on the domain bar.example.

You likely need to setup your page to make a redirect request to the "other" server with these Headers:

Access-Control-Request-Method: GET, OPTIONS
Access-Control-Request-Headers: x-requested-with, Content-Type, CUSTOM-HEADER

You then likely need to setup your "other" server to respond to an Options request with:

Access-Control-Allow-Origin: https://foo.example
Access-Control-Allow-Methods: GET, OPTIONS
Access-Control-Allow-Headers: x-requested-with, Content-Type, CUSTOM-HEADER

Then your page should be able to finish the request.

0

You need to set the "Access-Control-Allow-Methods" header.

Access-Control-Allow-Methods: *
user3674603
  • 56
  • 1
  • 6