214

I am trying to make a cross origin request using jquery but it keeps being reject with the message

XMLHttpRequest cannot load http://... No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin ... is therefore not allowed access.

I am using flask, heroku, and jquery

the client code looks like this:

$(document).ready(function() {
    $('#submit_contact').click(function(e){
        e.preventDefault();
        $.ajax({
            type: 'POST',
            url: 'http://...',
            // data: [
            //      { name: "name", value: $('name').val()},
            //      { name: "email", value: $('email').val() },
            //      { name: "phone", value: $('phone').val()},
            //      { name: "description", value: $('desc').val()}
            //
            // ],
            data:"name=3&email=3&phone=3&description=3",
            crossDomain:true,
            success: function(msg) {
                alert(msg);
            }
        });
    }); 
});

on the heroku side i am using flask and it is like this

from flask import Flask,request
from flask.ext.mandrill import Mandrill
try:
    from flask.ext.cors import CORS  # The typical way to import flask-cors
except ImportError:
    # Path hack allows examples to be run without installation.
    import os
    parentdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    os.sys.path.insert(0, parentdir)

    from flask.ext.cors import CORS
app = Flask(__name__)

app.config['MANDRILL_API_KEY'] = '...'
app.config['MANDRILL_DEFAULT_FROM']= '...'
app.config['QOLD_SUPPORT_EMAIL']='...'
app.config['CORS_HEADERS'] = 'Content-Type'

mandrill = Mandrill(app)
cors = CORS(app)

@app.route('/email/',methods=['POST'])
def hello_world():
    name=request.form['name']
    email=request.form['email']
    phone=request.form['phone']
    description=request.form['description']

    mandrill.send_email(
        from_email=email,
        from_name=name,
        to=[{'email': app.config['QOLD_SUPPORT_EMAIL']}],
        text="Phone="+phone+"\n\n"+description
    )

    return '200 OK'

if __name__ == '__main__':
    app.run()
Jason
  • 9,408
  • 5
  • 36
  • 36
Lopes
  • 2,257
  • 2
  • 13
  • 7

12 Answers12

369

Here is what worked for me when I deployed to Heroku.

http://flask-cors.readthedocs.org/en/latest/
Install flask-cors by running - pip install -U flask-cors

from flask import Flask
from flask_cors import CORS, cross_origin
app = Flask(__name__)
cors = CORS(app)
app.config['CORS_HEADERS'] = 'Content-Type'

@app.route("/")
@cross_origin()
def helloWorld():
  return "Hello, cross-origin-world!"
abc
  • 83
  • 1
  • 3
  • 9
Dan Rasmuson
  • 5,705
  • 5
  • 29
  • 45
  • 116
    Plus 1 for hello cross origin world! – Simon Nicholls Oct 11 '18 at 08:28
  • 1
    it was the only solution that works for me. Thanks! – psc37 Jan 02 '20 at 17:14
  • 2
    You're a life savior! Worked like a charm. – Rohit Swami Jan 10 '20 at 08:46
  • Hi! Could you help me to figure out what happens in my case? I wrote a simple API using Python/Flask, without even a view for it. I reached it by ```curl``` commands. Now I wrote a short html page and trying to make a request with JS fetch() method to my API which based on Heroku and I have the CORS error. After I applied your code, my terminal started to respond to me with HTML code (HTTP/1.1 503 Service Unavailable) instead of the JSON. What could be an error here? Thank you!! – NBash Mar 30 '20 at 09:46
  • 17
    Before anyone copies this code into their application, please [check out the documentation](https://flask-cors.readthedocs.io/en/3.0.7/) because only **some** of these lines are needed. – rovyko Apr 05 '20 at 20:53
  • Hey Daniel, looks like you are not using the "cors" variable anywhere, can you please explain why? – dpacman Nov 23 '20 at 09:05
  • 6
    Yeah, echoing @rovyko there are several overlapping features being used in this snippet, so check the docs. For me, `from flask_cors import CORS` followed by `CORS(app)` was enough – data princess Oct 11 '21 at 14:47
  • if you only want to expose only few of URL for CORS then only add decorator - @cross_origin(), other parts - cors = CORS(app) are only needed if you want to allow all routes for cross origin... – V.J. Jun 02 '22 at 14:24
  • I dont need the line > app.config['CORS_HEADERS'] = 'Content-Type' – Nam G VU Oct 17 '22 at 05:47
85

I've just faced the same issue and I came to believe that the other answers are a bit more complicated than they need to be, so here's my approach for those who don't want to rely on more libraries or decorators:

A CORS request actually consists of two HTTP requests. A preflight request and then an actual request that is only made if the preflight passes successfully.

The preflight request

Before the actual cross domain POST request, the browser will issue an OPTIONS request. This response should not return any body, but only some reassuring headers telling the browser that it's alright to do this cross-domain request and it's not part of some cross site scripting attack.

I wrote a Python function to build this response using the make_response function from the flask module.

def _build_cors_preflight_response():
    response = make_response()
    response.headers.add("Access-Control-Allow-Origin", "*")
    response.headers.add("Access-Control-Allow-Headers", "*")
    response.headers.add("Access-Control-Allow-Methods", "*")
    return response

This response is a wildcard one that works for all requests. If you want the additional security gained by CORS, you have to provide a whitelist of origins, headers and methods.

This response will convince your (Chrome) browser to go ahead and do the actual request.

The actual request

When serving the actual request you have to add one CORS header - otherwise the browser won't return the response to the invoking JavaScript code. Instead the request will fail on the client-side. Example with jsonify

response = jsonify({"order_id": 123, "status": "shipped"})
response.headers.add("Access-Control-Allow-Origin", "*")
return response

I also wrote a function for that.

def _corsify_actual_response(response):
    response.headers.add("Access-Control-Allow-Origin", "*")
    return response

allowing you to return a one-liner.

Final code

from flask import Flask, request, jsonify, make_response
from models import OrderModel

flask_app = Flask(__name__)

@flask_app.route("/api/orders", methods=["POST", "OPTIONS"])
def api_create_order():
    if request.method == "OPTIONS": # CORS preflight
        return _build_cors_preflight_response()
    elif request.method == "POST": # The actual request following the preflight
        order = OrderModel.create(...) # Whatever.
        return _corsify_actual_response(jsonify(order.to_dict()))
    else:
        raise RuntimeError("Weird - don't know how to handle method {}".format(request.method))

def _build_cors_preflight_response():
    response = make_response()
    response.headers.add("Access-Control-Allow-Origin", "*")
    response.headers.add('Access-Control-Allow-Headers', "*")
    response.headers.add('Access-Control-Allow-Methods', "*")
    return response

def _corsify_actual_response(response):
    response.headers.add("Access-Control-Allow-Origin", "*")
    return response
Niels B.
  • 5,912
  • 3
  • 24
  • 44
  • Thanks @Niels B. so much, you saved my time. I have added cors configuration before but not correctly set it up. – Günay Gültekin Mar 28 '19 at 06:17
  • 3
    This is by far the best answer on this CORS issue on Flask. Worked like a charm! Thanks @Niels – Chandra Kanth Apr 15 '19 at 08:01
  • Thank you for your very detailed explanation!! This was very helpful! – jones-chris Jun 03 '19 at 00:06
  • Use many solutions, including CORS and yours, but all of them do not work for aws(follow this example--https://aws.amazon.com/getting-started/projects/build-serverless-web-app-lambda-apigateway-s3-dynamodb-cognito/module-4/), do anyone know what is going on? – StereoMatching Sep 10 '19 at 12:36
  • This solution is really simple yet elegant! Thanks, you really saved my time. – Gerry Jan 03 '20 at 20:25
  • No reason why "prelight_response" is called "prelight" rather than "preflight", is there? – Fred Zimmerman Aug 27 '21 at 19:05
  • That's a typo. Didn't notice it. – Niels B. Aug 28 '21 at 12:25
  • 1
    I have used this solution and it worked flawlessly. So my question is: why the flask_cors package exists if simply adding this header is enough? – dsenese Oct 06 '21 at 18:50
  • Thanks for this answer, in my case, I put the OPTION-request-handling in a "@app.before_request" hooks so all my resolvers can be shielded from handling the OPTION requests. – Ken Nguyen Nov 08 '21 at 08:00
  • Thanks! @Niels B. in "The actual request" first code snippet first line: ```response = jsonify({"order_id": 123, "status": "shipped"}``` The jsonify function is not closed with ")" which is a syntax error. – vanya Jul 16 '22 at 12:36
  • @vanya Nicely spotted. I never actually ran the exact code in the answer, so that's probably how I could miss the syntax error. The line is fixed now. – Niels B. Jul 17 '22 at 13:08
67

OK, I don't think the official snippet mentioned by galuszkak should be used everywhere, we should concern the case that some bug may be triggered during the handler such as hello_world function. Whether the response is correct or uncorrect, the Access-Control-Allow-Origin header is what we should concern. So, it is very simple, just like the snippet bellow:

# define your bluprint
from flask import Blueprint
blueprint = Blueprint('blueprint', __name__)

# put this sippet ahead of all your bluprints
# blueprint can also be app~~
@blueprint.after_request 
def after_request(response):
    header = response.headers
    header['Access-Control-Allow-Origin'] = '*'
    # Other headers can be added here if needed
    return response

# write your own blueprints with business logics
@blueprint.route('/test', methods=['GET'])
def test():
    return "test success"

That is all~~

zhangqy
  • 853
  • 7
  • 10
  • This also helped me for a small project with basic CRUD operations. No need anything fancy, just bypass the error :) – Narshe Aug 25 '19 at 18:38
  • Solution OK for me but needed to add: header['Access-Control-Allow-Headers']='Content-Type' – MR_1204 Sep 29 '21 at 14:54
50

If you want to enable CORS for all routes, then just install flask_cors extension (pip3 install -U flask_cors) and wrap app like this: CORS(app).

That is enough to do it (I tested this with a POST request to upload an image, and it worked for me):

from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
CORS(app) # This will enable CORS for all routes

Important note: if there is an error in your route, let us say you try to print a variable that does not exist, you will get a CORS error related message which, in fact, has nothing to do with CORS.

Billal Begueradj
  • 20,717
  • 43
  • 112
  • 130
  • 2
    Thanks a lot! This simple and general solution allowed me to call my API from my React web code without the CORS block anymore. – Sebastian Diaz Jun 08 '20 at 16:02
  • 5
    Thank you ! The important note part saved me quite a lot of time. – Gabriel Nov 13 '20 at 12:24
  • 2
    Thank you! Your note is GOLD. – matisa Apr 28 '21 at 16:11
  • 4
    Exactly this. I still got a CORS error, but when I ran `heroku logs --tail` I saw a **module import error**. Then I added `Flask-Cors==3.0.10` to the `requirements.txt` file, and it worked. I did not have to restart the dynos. BTW you can run `pip freeze > requirements.txt` to get all the module requirements automatically. – Alaa M. Aug 10 '21 at 13:05
22

I resolved this same problem in python using flask and with this library. flask_cors in file init.py:

#pip install flask_cors

from flask_cors import CORS

app = Flask(__name__)
CORS(app)
cors = CORS(app, resource={
    r"/*":{
        "origins":"*"
    }
})

and its all.

Reference: https://flask-cors.readthedocs.io/en/latest/

Pedro Orozco
  • 359
  • 2
  • 5
  • While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - [From Review](/review/low-quality-posts/26658446) – Jason Aller Jul 12 '20 at 19:02
  • thnks for say it... i wil made better the nex time. – Pedro Orozco Mar 10 '21 at 15:37
  • The CORS parameter seems to be `resources`, not `resource` when initializing via the constructor (per the documentation). – amucunguzi Oct 18 '21 at 10:35
  • For some reason, CROS didn't work for me, the specified configurations do not take effect. – JAD May 17 '22 at 14:18
  • This must be the accepted answer – Nam G VU Oct 17 '22 at 05:47
9

Improving the solution described here: https://stackoverflow.com/a/52875875/10299604

With after_request we can handle the CORS response headers avoiding to add extra code to our endpoints:

    ### CORS section
    @app.after_request
    def after_request_func(response):
        origin = request.headers.get('Origin')
        if request.method == 'OPTIONS':
            response = make_response()
            response.headers.add('Access-Control-Allow-Credentials', 'true')
            response.headers.add('Access-Control-Allow-Headers', 'Content-Type')
            response.headers.add('Access-Control-Allow-Headers', 'x-csrf-token')
            response.headers.add('Access-Control-Allow-Methods',
                                'GET, POST, OPTIONS, PUT, PATCH, DELETE')
            if origin:
                response.headers.add('Access-Control-Allow-Origin', origin)
        else:
            response.headers.add('Access-Control-Allow-Credentials', 'true')
            if origin:
                response.headers.add('Access-Control-Allow-Origin', origin)

        return response
    ### end CORS section
Frank Escobar
  • 648
  • 5
  • 15
  • This can end up your system in CORS attack. Instead use `Access-Control-Allow-Origin: *` – PaxPrz Feb 02 '21 at 15:34
  • @Pax can you explain a little more? – Frank Escobar Feb 06 '21 at 18:16
  • If response has `Access-Control-Allow-Credentials: true`, then the wildcard operator cannot be used on any of the response headers like `Access-Control-Allow-Origin`. So browser plays a safe side if both wildcard is used along with allow-credentials. – PaxPrz Feb 07 '21 at 02:07
  • [Here](https://stackoverflow.com/a/19744754/5611227) is another answer with better explaination. – PaxPrz Feb 07 '21 at 02:10
  • From the above solutions, this option is what solved it for me. – JAD May 17 '22 at 14:18
7

You'd first need to install flask-cors. You can do this as follows:

pip install flask-cors

Once this is installed, you can use it in your Flask app as follows:

  1. If you need to enable CORS for all routes:
from flask_cors import CORS

app = Flask(__name__)
CORS(app)
  1. If you want to enable CORS only for specific routes, you can pass the resources parameter to the CORS function. For example,
CORS(app, resources={r"/api/*": {"origins": "*"}})

In this example, This code will enable CORS only for routes that start with /api/ and will allow requests from any origin. You can customize the resources parameter to match your needs.

For more information, please read the documentation

S. Tiss
  • 139
  • 1
  • 9
5

All the responses above work okay, but you'll still probably get a CORS error, if the application throws an error you are not handling, like a key-error, if you aren't doing input validation properly, for example. You could add an error handler to catch all instances of exceptions and add CORS response headers in the server response

So define an error handler - errors.py:

from flask import json, make_response, jsonify
from werkzeug.exceptions import HTTPException

# define an error handling function
def init_handler(app):

    # catch every type of exception
    @app.errorhandler(Exception)
    def handle_exception(e):

        #loggit()!          

        # return json response of error
        if isinstance(e, HTTPException):
            response = e.get_response()
            # replace the body with JSON
            response.data = json.dumps({
                "code": e.code,
                "name": e.name,
                "description": e.description,
            })
        else:
            # build response
            response = make_response(jsonify({"message": 'Something went wrong'}), 500)

        # add the CORS header
        response.headers['Access-Control-Allow-Origin'] = '*'
        response.content_type = "application/json"
        return response

then using Billal's answer:

from flask import Flask
from flask_cors import CORS

# import error handling file from where you have defined it
from . import errors

app = Flask(__name__)
CORS(app) # This will enable CORS for all routes
errors.init_handler(app) # initialise error handling 
Edrich
  • 175
  • 1
  • 8
4

Try the following decorators:

@app.route('/email/',methods=['POST', 'OPTIONS']) #Added 'Options'
@crossdomain(origin='*')                          #Added
def hello_world():
    name=request.form['name']
    email=request.form['email']
    phone=request.form['phone']
    description=request.form['description']

    mandrill.send_email(
        from_email=email,
        from_name=name,
        to=[{'email': app.config['QOLD_SUPPORT_EMAIL']}],
        text="Phone="+phone+"\n\n"+description
    )

    return '200 OK'

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

This decorator would be created as follows:

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


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

You can also check out this package Flask-CORS

galuszkak
  • 513
  • 4
  • 25
Newtt
  • 6,050
  • 13
  • 68
  • 106
  • still not working. I already tried that and I also used the Flask-CORS package. I think Flask-CORS is built on top of that – Lopes Aug 31 '14 at 23:40
2

My solution is a wrapper around app.route:

def corsapp_route(path, origin=('127.0.0.1',), **options):
    """
    Flask app alias with cors
    :return:
    """

    def inner(func):
        def wrapper(*args, **kwargs):
            if request.method == 'OPTIONS':
                response = make_response()
                response.headers.add("Access-Control-Allow-Origin", ', '.join(origin))
                response.headers.add('Access-Control-Allow-Headers', ', '.join(origin))
                response.headers.add('Access-Control-Allow-Methods', ', '.join(origin))
                return response
            else:
                result = func(*args, **kwargs)
            if 'Access-Control-Allow-Origin' not in result.headers:
                result.headers.add("Access-Control-Allow-Origin", ', '.join(origin))
            return result

        wrapper.__name__ = func.__name__

        if 'methods' in options:
            if 'OPTIONS' in options['methods']:
                return app.route(path, **options)(wrapper)
            else:
                options['methods'].append('OPTIONS')
                return app.route(path, **options)(wrapper)

        return wrapper

    return inner

@corsapp_route('/', methods=['POST'], origin=['*'])
def hello_world():
    ...
yurzs
  • 81
  • 7
0

If you can't find your problem and you're code should work, it may be that your request is just reaching the maximum of time heroku allows you to make a request. Heroku cancels requests if it takes more than 30 seconds.

Reference: https://devcenter.heroku.com/articles/request-timeout

double-beep
  • 5,031
  • 17
  • 33
  • 41
Lennart
  • 67
  • 1
  • 10
0

To enable CORS for specific origins only, using flask-cors:

cors = CORS(app, origins=['http://localhost:3000', 'https://example.com'])

Ref official doc for more info on this: https://flask-cors.corydolphin.com/en/latest/api.html#extension

imran3
  • 424
  • 1
  • 4
  • 10