154

For the following ajax post request for Flask (how can I use data posted from ajax in flask?):

$.ajax({
    url: "http://127.0.0.1:5000/foo", 
    type: "POST",
    contentType: "application/json",
    data: JSON.stringify({'inputVar': 1}),
    success: function( data ) { 
        alert( "success" + data );
    }   
});

I get a Cross Origin Resource Sharing (CORS) error:

No 'Access-Control-Allow-Origin' header is present on the requested resource. 
Origin 'null' is therefore not allowed access. 
The response had HTTP status code 500.

I tried solving it in the two following ways, but none seems to work.

  1. Using Flask-CORS

This is a Flask extension for handling CORS that should make cross-origin AJAX possible.

My pythonServer.py using this solution:

from flask import Flask
from flask.ext.cors import CORS, cross_origin

app = Flask(__name__)
cors = CORS(app, resources={r"/foo": {"origins": "*"}})
app.config['CORS_HEADERS'] = 'Content-Type'

@app.route('/foo', methods=['POST','OPTIONS'])
@cross_origin(origin='*',headers=['Content-Type','Authorization'])
def foo():
    return request.json['inputVar']

if __name__ == '__main__':
    app.run()
  1. Using specific Flask Decorator

This is an official Flask code snippet defining a decorator that should allow CORS on the functions it decorates.

My pythonServer.py using this solution:

from flask import Flask, make_response, request, current_app
from datetime import timedelta
from functools import update_wrapper

app = Flask(__name__)

def crossdomain(origin=None, methods=None, headers=None,
                max_age=21600, attach_to_all=True,
                automatic_options=True):
    if methods is not None:
        methods = ', '.join(sorted(x.upper() for x in methods))
    if headers is not None and not isinstance(headers, basestring):
        headers = ', '.join(x.upper() for x in headers)
    if not isinstance(origin, basestring):
        origin = ', '.join(origin)
    if isinstance(max_age, timedelta):
        max_age = max_age.total_seconds()

    def get_methods():
        if methods is not None:
            return methods

        options_resp = current_app.make_default_options_response()
        return options_resp.headers['allow']

    def decorator(f):
        def wrapped_function(*args, **kwargs):
            if automatic_options and request.method == 'OPTIONS':
                resp = current_app.make_default_options_response()
            else:
                resp = make_response(f(*args, **kwargs))
            if not attach_to_all and request.method != 'OPTIONS':
                return resp

            h = resp.headers

            h['Access-Control-Allow-Origin'] = origin
            h['Access-Control-Allow-Methods'] = get_methods()
            h['Access-Control-Max-Age'] = str(max_age)
            if headers is not None:
                h['Access-Control-Allow-Headers'] = headers
            return resp

        f.provide_automatic_options = False
        return update_wrapper(wrapped_function, f)
    return decorator

@app.route('/foo', methods=['GET','POST','OPTIONS'])
@crossdomain(origin="*")
def foo():
    return request.json['inputVar']

if __name__ == '__main__':
    app.run()

Can you please give some some indication of why that is?

2
  • did you figure out? I run into the exact same issue :(
    – shao
    Commented Dec 7, 2014 at 3:44
  • This is an old question, but just to be sure: Did you restart your Flask server? I was also wondering why I got the same error even thought everything was exactly as they should. Turns out, you have to restart the server in order for it to really take effect Commented May 6, 2017 at 22:22

15 Answers 15

162

You can get the results with a simple:

@app.route('your route', methods=['GET'])
def yourMethod(params):
    response = flask.jsonify({'some': 'data'})
    response.headers.add('Access-Control-Allow-Origin', '*')
    return response
7
  • 2
    Simple and effective
    – Anthony
    Commented Dec 24, 2016 at 4:00
  • 7
    Brilliant! I agree, simple and effective. IMO Should be accepted answer
    – PALEN
    Commented Jun 29, 2017 at 23:15
  • 6
    @Salvador Dali - Do you know what is proper way of allowing cross origin if I am rendering a template instead of just a json object? i.e. last line of code in yourMethod is: return render_template('template.html',some_var = response)
    – Matteo
    Commented Jul 13, 2017 at 16:27
  • 4
    Could this be used in a post method? I tried for a file upload method and I failed.
    – W.Leto
    Commented Jan 14, 2019 at 11:53
  • 3
    @Matteo It's been 4 years, but you can do something like this: response = make_response(render_template('index.html', foo=42)), response.headers.add('Access-Control-Allow-Origin', '*')
    – oeter
    Commented May 18, 2021 at 9:48
144

Well, I faced the same issue. For new users who may land at this page. Just follow their official documentation.

Install flask-cors

pip install -U flask-cors

then after app initialization, initialize flask-cors with default arguments:

from flask import Flask
from flask_cors import CORS

app = Flask(__name__)
CORS(app)

@app.route("/")
def helloWorld():
   return "Hello, cross-origin-world!"
7
  • 11
    This gives the following error for me Access to XMLHttpRequest at 'my_domain' from origin '127.0.0.1:5000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The 'Access-Control-Allow-Origin' header contains multiple values '127.0.0.1:5000, *', but only one is allowed. Commented Jul 19, 2019 at 11:58
  • Doesn't work for me. stackoverflow.com/questions/59652099/… Commented Jan 8, 2020 at 20:44
  • Works for me too Tks!
    – Jose
    Commented Apr 13, 2020 at 19:37
  • how to set Access-Control-Max-Age for flask_cors? Commented Sep 18, 2020 at 8:33
  • Worked for me!! Thank you. This should be the accepted answer!
    – mattyb
    Commented Mar 5, 2021 at 22:38
88

It worked like a champ, after bit modification to your code

# initialization
app = Flask(__name__)
app.config['SECRET_KEY'] = 'the quick brown fox jumps over the lazy   dog'
app.config['CORS_HEADERS'] = 'Content-Type'

cors = CORS(app, resources={r"/foo": {"origins": "http://localhost:port"}})

@app.route('/foo', methods=['POST'])
@cross_origin(origin='localhost',headers=['Content- Type','Authorization'])
def foo():
    return request.json['inputVar']

if __name__ == '__main__':
   app.run()

I replaced * by localhost. Since as I read in many blogs and posts, you should allow access for specific domain

2
  • 13
    In case anyone is using blueprints, you need to add the CORS() to each blueprint, for example: my_blueprint = Blueprint('my_bp_name', name, url_prefix="/my-prefix") CORS(my_blueprint)
    – BLang
    Commented May 6, 2020 at 19:22
  • 1
    See the one-liner below as well: stackoverflow.com/a/46637194/3559330
    – mattyb
    Commented Mar 5, 2021 at 22:38
9

Might as well make this an answer. I had the same issue today and it was more of a non-issue than expected. After adding the CORS functionality, you must restart your Flask server (ctrl + c -> python manage.py runserver, or whichever method you use)) in order for the change to take effect, even if the code is correct. Otherwise the CORS will not work in the active instance.

Here's how it looks like for me and it works (Python 3.6.1, Flask 0.12):

factory.py:

from flask import Flask
from flask_cors import CORS  # This is the magic


def create_app(register_stuffs=True):
    """Configure the app and views"""
    app = Flask(__name__)
    CORS(app)  # This makes the CORS feature cover all routes in the app

    if register_stuffs:
        register_views(app)
    return app


def register_views(app):
    """Setup the base routes for various features."""
    from backend.apps.api.views import ApiView
    ApiView.register(app, route_base="/api/v1.0/")

views.py:

from flask import jsonify
from flask_classy import FlaskView, route


class ApiView(FlaskView):
    @route("/", methods=["GET"])
    def index(self):
        return "API v1.0"

    @route("/stuff", methods=["GET", "POST"])
    def news(self):
        return jsonify({
            "stuff": "Here be stuff"
        })

In my React app console.log:

Sending request:
GET /stuff
With parameters:
null
bundle.js:17316 Received data from Api:
{"stuff": "Here be stuff"}
9

I might be a late on this question but below steps fixed the issue

from flask import Flask
from flask_cors import CORS

app = Flask(__name__)
CORS(app)
7

Note that setting the Access-Control-Allow-Origin header in the Flask response object is fine in many cases (such as this one), but it has no effect when serving static assets (in a production setup, at least). That's because static assets are served directly by the front-facing web server (usually Nginx or Apache). So, in that case, you have to set the response header at the web server level, not in Flask.

For more details, see this article that I wrote a while back, explaining how to set the headers (in my case, I was trying to do cross-domain serving of Font Awesome assets).

Also, as @Satu said, you may need to allow access only for a specific domain, in the case of JS AJAX requests. For requesting static assets (like font files), I think the rules are less strict, and allowing access for any domain is more accepted.

7

Note: The placement of cross_origin should be right and dependencies are installed. On the client side, ensure to specify kind of data server is consuming. For example application/json or text/html

For me the code written below did magic

from flask import Flask,request,jsonify
from flask_cors import CORS,cross_origin
app=Flask(__name__)
CORS(app, support_credentials=True)
@app.route('/api/test', methods=['POST', 'GET','OPTIONS'])
@cross_origin(supports_credentials=True)
def index():
    if(request.method=='POST'):
     some_json=request.get_json()
     return jsonify({"key":some_json})
    else:
        return jsonify({"GET":"GET"})


if __name__=="__main__":
    app.run(host='0.0.0.0', port=5000)
1
  • 3
    why do you do the same thing(support_credentials=True) in cross_origin? Commented Jan 6, 2020 at 19:36
4

My fix was

@app.after_request
def handle_options(response):
    response.headers["Access-Control-Allow-Origin"] = "*"
    response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE, OPTIONS"
    response.headers["Access-Control-Allow-Headers"] = "Content-Type, X-Requested-With"

    return response
0
3

I used decorator given by Armin Ronacher with little modifications (due to different headers that are requested by the client).And that worked for me. (where I use angular as the requester requesting application/json type).

The code is slightly modified at below places,

from flask import jsonify

@app.route('/my_service', methods=['POST', 'GET','OPTIONS'])
@crossdomain(origin='*',headers=['access-control-allow-origin','Content-Type'])
def my_service():
    return jsonify(foo='cross domain ftw')

jsonify will send a application/json type, else it will be text/html. headers are added as the client in my case request for those headers

 const httpOptions = {
      headers: new HttpHeaders({
        'Content-Type': 'application/json',
        'Access-Control-Allow-Origin':'*'
      })
    };
    return this.http.post<any>(url, item,httpOptions)
1
2

I think the problem is with preflighted requests. It seems there's no effective way to disable the automatic answer to OPTIONS request if you're using @route @put @patch @delete decorators. my workaround is the following snippet at the start of the api (before any decorated function)

def option_todo(id): 
  return '', 204 
 
app.add_url_rule('/', view_func=option_todo, provide_automatic_options=False, methods=['OPTIONS']) 
app.add_url_rule(r'/<path:path>', view_func=option_todo, provide_automatic_options=False, methods=['OPTIONS']) 

@app.after_request
def after_request(response):
  response.headers['Access-Control-Allow-Methods']='*'
  response.headers['Access-Control-Allow-Origin']='*'
  response.headers['Vary']='Origin'
  return response
1

This is a pretty old and well-covered question already, but figured I'd contribute what I think is the simplest solution of the lot:

@app.after_request
def after_request(response: Response) -> Response:
    response.access_control_allow_origin = "*"
    return response
1
  • doesn't seem to work with POST requests
    – Noname
    Commented Mar 16, 2022 at 17:32
1

My issue was a preflight one, but I had to add to app.py

...

@app.after_request
def after_request(response: Response) -> Response:
    response.access_control_allow_credentials = True
    return response

0

I encountered this issue today, and none of the answers worked for me.

I set the cross_origin() decorator as follows:

@app.route('/', methods = ['POST'])
@cross_origin()
def index():

Then I did:

  1. Open the Flask Python file in the IDE.
  2. Right mouse-click on the file.
  3. Click: Run Python File in Terminal

The problem for me was running the Flask Python file with CTRL+ALT+N. The "Run Code" button in VSCode, I shouldn't have ran the Flask server that way. Because of that I assume CORS didn't load properly.

Also, my print()s didn't always appear, because of that so it was also hard to debug stuff. This happened because by running the app via CTRL+ALT+N, VSCode focusses on the OUTPUT window instead of the TERMINAL window.

Also, print()s that did appear in the OUTPUT window, don't support emojis like the TERMINAL window does. So my app crashed for the longest time until I figured it all out.

Stupid mistake on my part though, should've known better. Hope this helps others!

-1

I struggled a lot with something similar. Try the following:

  1. Use some sort of browser plugin which can display the HTML headers.
  2. Enter the URL to your service, and view the returned header values.
  3. Make sure Access-Control-Allow-Origin is set to one and only one domain, which should be the request origin. Do not set Access-Control-Allow-Origin to *.

If this doesn't help, take a look at this article. It's on PHP, but it describes exactly which headers must be set to which values for CORS to work.

CORS That Works In IE, Firefox, Chrome And Safari

1
  • 2
    1) Headers are from HTTP connections, not HTML documents 2) You can just open up your devtools on any modern browser to display the headers of the connection under the 'Network' tab without installing any extension 3) You might need to use * in Access-Control-Allow-Origin if you don't want to to set a custom header for every request and you want for example to create a Public API Commented Jan 13, 2021 at 22:27
-1

A cross origin POST request with Content-Type application/json is considered to be unsafe. As a result, before handling the actual POST request, some additional preflight OPTIONS request is send to the server. The server needs to be able to handle that preflight request correctly. Otherwise the POST request fails.

Also see https://javascript.info/fetch-crossorigin#unsafe-requests for a detailed description of the headers associated with preflight requests.

In addition to the flask application, the headers and response codes might be influenced by server configuration files like apache .htaccess files.

Authorization issues or https vs http issues might cause failing requests, too.

Bypassing OPTIONS from authentication might help:

<LimitExcept OPTIONS POST>
  Require valid-user
</LimitExcept>

If password protection is enabled, include credentials in options of fetch request:

var credentials = btoa("user:password");

let getOptions = {
  headers: {
    "Authorization" : `Basic ${credentials}` 
  }
};
await fetch(url, options)

Also see:

https://docs.hetzner.com/de/konsoleh/account-management/development/cors/

https://javascript.info/fetch-crossorigin#unsafe-requests

https://serverfault.com/questions/684855/disable-authentication-for-http-options-method-preflight-request

https://stackoverflow.com/a/48172785/2876079

Why are cross-origin POST requests with Content-Type 'application/json' considered to be unsafe?

Why is my header not included when requesting a page with http instead of https protocol?

Not the answer you're looking for? Browse other questions tagged or ask your own question.