How can I enable CORS in FastAPI?
Asked Answered
M

5

83

I'm trying to enable CORS in this very basic FastAPI example, however it doesn't seem to be working.

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

app.add_middleware(
    CORSMiddleware,
    allow_origins=['*']
)

@app.get('/')
def read_main():
    return {'message': 'Hello World!'}

This is the response I get:

curl -v http://127.0.0.1:8000
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 8000 (#0)
> GET / HTTP/1.1
> Host: 127.0.0.1:8000
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< date: Fri, 08 Jan 2021 19:27:37 GMT
< server: uvicorn
< content-length: 26
< content-type: application/json
<
* Connection #0 to host 127.0.0.1 left intact
{"message":"Hello World!"}*
Mt answered 8/1, 2021 at 19:33 Comment(16)
It seems to be working. You are allowing requests from every originContamination
@Isabi I don't get Access-Control-Allow-Origin: * in my response though.Mt
Have you tried with a browser or an app? My guess is that curl is not sending the Origin in the headers because it has no well defined origin, so it cannot return it in the headersContamination
I tried with Chrome and Postman. The only headers I get in the response are: content-length, content-type, date and server.Mt
That's strange. Have you tried with the full example? fastapi.tiangolo.com/tutorial/cors/?h=+cors#use-corsmiddlewareContamination
I have tried that too. I've no idea what's going on unfortunately...Mt
I tested the sample code of the official docs and it does not show the CORS when requested from the terminal, but it shows them from javascript running within the browser (Chromium Version 87.0.4280.88 (Official Build) snap (64-bit))Contamination
If CORS is indeed enabled, should the Access-Control-Allow-Origin: * header not be sent with the response?Mt
If it's in the browser in which CORS permissions are mandatory yes, but in the case of an API requests from different sources/domains will be performed, then no. CORS are mainly for security reasons (scripts that perform requests to external resources)Contamination
@Contamination of what use is this CORS if it cant restrict at all times? looks to me like a useless feature as it can be bypassed pretty easily and just does not work, also if behind web server proxy...wont work except if proxy is setup to use CORS alsoArie
@Arie I don't understand your question. Goal of CORS is to support direct access of javascript to third party APIsContamination
or to restrict and only allow from certain origins/domains...yes or no?Arie
@Contamination yes or no? CORS is to control access to endpoint based on origin/domain? yes or no?Arie
@Arie No, CORS is for restricting access to the same domain. I want to be the only one accessing my API from the browser, thus I allow only my domain as origin (though direct API calls, not through browser, are allowed). This ensures more security for my users who navigate via browserContamination
you said No and then agree with what am saying, contradicting there...you just said for restricting access...so if this CORS can be bypassed, then is that security of illusion of security?Arie
as here is written: #65191561 The reponse is only generated if the sender includes origin in the header.Contemptuous
S
123

you can find answer from this:fastapi cors

then this is a very simple code to achieve it:

  1. create a python file and named it main.py.

  2. add code in this file.

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

origins = ["*"]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)


@app.get("/")
async def main():
    return {"message": "Hello World"}

and run this app:

uvicorn main:app  --reload --host 0.0.0.0 --port 8000

if you computer ip is 192.12.12.12

you can check this link and just write a small javascript in html:

<script>
        fetch("http://192.12.12.12:8000/").then((Response) => {
            return Response.json()
        }).then((data) => {
            console.log(data);
        })
    </script>
Swanky answered 3/3, 2021 at 16:14 Comment(10)
not working for me even restarted the serverBoracic
@SunilGarg it shouldn't work. on the link given by yuanzz fastapi.tiangolo.com/tutorial/cors/…, it literally says "allow_origins cannot be set to ['*'] for credentials to be allowed"Official
You can't use [*] as allowed origins while with_credentials is set to trueLucinalucinda
Literal quote from the help page: "Also, allow_origins cannot be set to [*] for credentials to be allowed, origins must be specified."Tendentious
This solution is greatChesnut
As others have said REMOVE allow_methods=["*"], if the origin is a wildcard otherwise it WILL NOT WORKInesinescapable
As per the documentation, it is preferabe to explicitly specify the allowed origins, e.g., origins = ['http://localhost:3000', 'http://127.0.0.1:3000'], instead of using the "*" wildcard, which would allow any origin at the cost of excluding everything that involves credentials from the communication, such as cookies and Authorization headers. See here and here as well.Deform
Please have a look at related answers here and hereDeform
Thanks a lot, this answer was invaluable and it helped us to get our demo ready !Kweisui
The docs explicitly say: allow_credentials - Indicate that cookies should be supported for cross-origin requests. Defaults to False. Also, allow_origins cannot be set to ['*'] for credentials to be allowed, origins must be specified. I know FastAPI will quietly accept * origin and allow_credentials=True, but it will most likely not pass cookies.Resurrectionist
N
12

In my case, CORS not works when pydantic or type problems occured.

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

origins = [
    "http://localhost:3000",
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# works well!
@app.get("/list")
async def main():
    return ["hi", "hello"]

# error cases
from typing import List
from app.nosql.model import Book

@app.get("/error")
async def main():
    # case 1
    ret = mongo_db.engine.find(Book, limit=10) # keyword "await" missing
    return ret  # CORS 500 error
    # case 2
    ret: List[Book] = await mongo_db.engine.find(Book, limit=10) # data was not fit with model Book
    return ret # CORS error
    # case 3
    return ["hi", "hello"] # works well...

Whats the server-side error says? It might be error occurs in server. How about test with new function. (has no error) If server works well.. humm.. sry about that.

Nutriment answered 6/1, 2022 at 16:17 Comment(3)
This isn't an answer. It's better to add comments instead of answers if you've got a similar question. But even then this isn't a minimal working example. So it's not clear what you're trying to add to the conversation here.Conspire
CORS headers are not added when the request ends in an error. Please have a look at this answer for more details.Deform
I have resolved this question, Please have a look at this answerGilliette
S
3

I had a similar issue and just found the solution. Using lots of copypasted code, I was spinning up another FastAPI instance somewhere where CORS was not configured, like this:

app = FastAPI()

app.add_middleware(
    CORSMiddleware,
    allow_origins=['*']
)

... much later somewhere within lots of green code ...
app = FastAPI()
Steamship answered 24/10, 2022 at 13:41 Comment(0)
U
2

Adding expose_headers helped me to fix this

expose_headers=["*"]
Union answered 21/9, 2023 at 19:20 Comment(2)
Where? As an argument to add_middleware()?Conspire
Yes, just like this: app.add_middleware( CORSMiddleware, ..., expose_headers=["*"] ) Also, you can be very specific about the headers you want to allow to avoid security risks!Daron
L
0

If the (browser) client makes a request from a local file (e.g. file://.../main.html), we run into an edge case. The request origin must then be specified as 'null', when requesting with credentials included.

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware, Response

app = FastAPI()
app.add_middleware(
    CORSMiddleware, allow_origins=['null'],
    allow_credentials=True, allow_methods=['*'], allow_headers=['*'])

@app.get('/')
def main():
    return Response('OK', status_code=200)

You can see the origin of the request in the error message of the browser console, if running a chromium-based browser. In Firefox however, the origin is hidden.

Please note that this has security implications, and it is thus not recommended. See this related answer for more details: https://mcmap.net/q/244206/-how-to-enable-cors-in-fastapi-for-local-html-file-loaded-via-a-file-url.

Lashanda answered 21/2 at 16:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.