Werkzeug: Support CORS

Created on 3 Nov 2011  ·  16Comments  ·  Source: pallets/werkzeug

It would be nice to support CORS [1][2]

even static files need to support it

[1] http://www.w3.org/TR/cors/
[2] http://enable-cors.org/

Most helpful comment

Also I want to close this since I don't think we need to add every middleware to werkzeug.

All 16 comments

Werkzeug already supports CORS in that you can set the headers on the responses you return in your WSGI application. I'm not sure what else can or should be done within Werkzeug to "support CORS", can you elaborate on that?

I'm not the OP, but since CORS is pretty much a boilerplate header injection, it might perhaps be a good idea to have it as a setting/parameter. Something along the lines of this snippet, although here-notably there is no OPTIONS, so the preflight will be problematic...

    if 'cors' in config and config['cors'] is True:
        response.headers.add('Access-Control-Allow-Origin', '*')
        response.headers.add('Access-Control-Allow-Methods',
                             'GET,PUT,POST,DELETE,PATCH')
        response.headers.add('Access-Control-Allow-Headers',
                             'Content-Type, Authorization')

...you know the python mantra: "batteries included"... ;)

But CORS is more than allowing *, especially when you use cookies and stuff. Personally, I use a 20 LoC WSGI middleware to add CORS headers and answer preflight request. It's not that hard.

@posativ - Care to share on a gist maybe? :)

In a very limited form:

from werkzeug.datastructures import Headers

class CORSMiddleware(object):
    """Add Cross-origin resource sharing headers to every request."""

    def __init__(self, app, origin):
        self.app = app
        self.origin = origin

    def __call__(self, environ, start_response):

        def add_cors_headers(status, headers, exc_info=None):
            headers = Headers(headers)
            headers.add("Access-Control-Allow-Origin", self.origin)
            headers.add("Access-Control-Allow-Headers", "Origin, ...")
            headers.add("Access-Control-Allow-Credentials", "true")
            headers.add("Access-Control-Allow-Methods", "GET, POST, PUT, etc")
            headers.add("Access-Control-Expose-Headers", "...")
            return start_response(status, headers.to_list(), exc_info)

        if environ.get("REQUEST_METHOD") == "OPTIONS":
            add_cors_headers("200 Ok", [("Content-Type", "text/plain")])
            return [b'200 Ok']

        return self.app(environ, add_cors_headers)

# usage
app = CORSMiddleware(make_app(...), "http://example.org/")

You may extend this class to include custom headers, credentials, wild-cards.

@posativ - Thank you for this, I'll try it early next week when I'll resume work on that project. :)

Need to bump this up.

Relative question http://stackoverflow.com/questions/19382431/set-header-on-werkzeug-exception

Related line: https://github.com/pallets/werkzeug/blob/master/werkzeug/debug/__init__.py#L297

the start_response('500 INTERNAL SERVER ERROR', makes it hard to add CORS header like Access-Control-Allow-Origin: *. But curiously we already have X-XSS-Protection: 0 there.

I have Flask-CORS enabled, been debugging a tricky remote ajax API call in Chrome, when Flask has an exception, I can not see the debug content because of CORS. Can we add this header in debug model?

Why would you want to set a CORS header on the debugger? And why can't you do that with a wrapping middleware?

Also I want to close this since I don't think we need to add every middleware to werkzeug.

@unititaker

Why would you want to set a CORS header on the debugger?

Because it's a tricky scenario invoked by cross-domain AJAX request, in can only debug it in Chrome DevTool after series of javascript event triggering, if no CORS header, the Chrome DevTool does not display any content at all. I cant see nothing. I can only stare at a blank page with 500 server error.

And why can't you do that with a wrapping middleware?

because wrapped middleware works with only NORMAL (proper returned Flask application ) pages, but does not work at all when Werkzeug exception debugger happens.

Here's the "wrapping middleware" you referring to, https://github.com/corydolphin/flask-cors/issues/67 you can see we cant do much about it unless lower level Werkzeug stuff was tweaked.

I presume @untitaker did not take a look at the source code I linked? Here's a screenshot:

qq20160420-3

@lambdaq now you are being mean to @untitaker

any middleware that does wrap around a debug wrapped application can change the start_response behaviour - even for the debugger - so the code you pasted and linked seems completely irrelevant,
since a cors middle-ware wrapping around could just change it

perhaps im missing your exact problem, due to different perspective/understanding please outline the exact use-case that seems broken/impossible

The situation is made a bit tricky because the debugger middleware is applied when calling app.run. Still you can work around this by applying both your CORS and the debugger middleware manually. IMO this is fine for such an edgecase, but if you have concrete proposals for changing the debugger middleware in a way that doesn't compromise security, please do suggest them. I don't know of any easy changes though.

Also I am well aware of the code in Werkzeug. I guess that's why I'm maintaining it?

sorry, I apologize for my language, Werkzeug is awesome project.

I guess I just have to monkey edit these start_response() lines and add CORS header bruteforce for my dev environment, it's more convenient than a hairy middleware.

Also you might consider adding extra JS code instead that redirects to the debugger if a 500 response comes back.

@untitaker JS code can do nothing, if there's 500 error, you can only tell its http status code is 500, the traceback from the debugger page is what I am after.

also the js code is hairy compiled reactjs with tons of stuff, developed by another team, it takes 300s+ to npm run dev. Not easy to tweak or add few debug lines.

I think #1699 resolves this. Whilst Werkzeug could do more than is in #1699, I think it should just provide accessor methods (like for cache control) and allow tools built on it (e.g. Flask-CORS or Quart-CORS) to do CORS logic.

Was this page helpful?
0 / 5 - 0 ratings