# CORS (Cross-Origin Resource Sharing)
CORS or "Cross-Origin Resource Sharing" (opens new window) refers to the situations when a frontend running in a browser has JavaScript code that communicates with a backend, and the backend is in a different "origin" than the frontend.
# CORS Origins
An origin is the combination of protocol (http
, https
), domain (myapp.com
, localhost
, localhost.tiangolo.com
), and port (80
, 443
, 8080
).
So, all these are different origins:
http://localhost
https://localhost
http://localhost:8080
Even if they are all in localhost
, they use different protocols or ports, so, they are different "origins".
# Mechanism steps
Let's say you have a frontend running in your browser at http://localhost:8080
, and its JavaScript is trying to communicate with a backend running at http://localhost
(because we don't specify a port, the browser will assume the default port 80
).
Then, the browser will send an HTTP OPTIONS
request to the backend, and if the backend sends the appropriate headers authorizing the communication from this different origin (http://localhost:8080
) then the browser will let the JavaScript in the frontend send its request to the backend.
To achieve this, the backend must have a list of "allowed origins".
In this case, it would have to include http://localhost:8080
for the frontend to work correctly.
Wildcards
It's also possible to declare the list as "*"
(a "wildcard") to say that all are allowed.
But that will only allow certain types of communication, excluding everything that involves credentials: Cookies, Authorization headers like those used with Bearer Tokens, etc.
So, for everything to work correctly, it's better to explicitly specify the allowed origins.
# Using CORSMiddleware
You can configure it in your FastAPI application using CORSMiddleware
.
- Import
CORSMiddleware
. - Create a list of allowed origins (as strings).
- Add it as a "middleware" to your FastAPI application.
You can also specify whether your backend allows:
- Credentials (Authorization headers, Cookies, etc).
- Specific HTTP methods (
GET
,POST
, etc.) or all of them with the wildcard"*"
. - Specific HTTP headers or all of them with the wildcard
"*"
.
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
import config
app = FastAPI(docs_url=config.documentation_url)
origins = [
"http://localhost",
"https://localhost",
"http://127.0.0.1:8080",
"https://127.0.0.1:8080",
"https://mysite.netlify.app"
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/")
def main():
return {"message": "Hello World"}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
The default parameters used by the CORSMiddleware
implementation are restrictive by default, so you'll need to explicitly enable particular origins, methods, or headers, in order for browsers to be permitted to use them in a Cross-Domain context.
The following arguments are supported:
allow_origins
- A list of origins that should be permitted to make cross-origin requests. E.g.['https://example.org', 'https://www.example.org']
. You can use['*']
to allow any origin.allow_origin_regex
- A regex string to additionally match against origins that should be permitted to make cross-origin requests. e.g.'https://.*\.example\.org'
.allow_methods
- A list of HTTP methods that should be allowed for cross-origin requests. Defaults to['GET']
. You can use['*']
to allow all standard methods.allow_headers
- A list of HTTP request headers that should be supported for cross-origin requests. Defaults to[]
. You can use['*']
to allow all headers. TheAccept
,Accept-Language
,Content-Language
andContent-Type
headers are always allowed for CORS requests.allow_credentials
- Indicate that cookies should be supported for cross-origin requests. Defaults toFalse
. Also,allow_origins
cannot be set to['*']
for credentials to be allowed, origins must be specified.expose_headers
- Indicate any response headers that should be made accessible to the browser. Defaults to[]
.max_age
- Sets a maximum time in seconds for browsers to cache CORS responses. Defaults to600
.
The middleware responds to two particular types of HTTP request:
- CORS preflight requests: These are any
OPTIONS
request withOrigin
andAccess-Control-Request-Method
headers. In this case the middleware will intercept the incoming request and respond with appropriate CORS headers, and either a200
or400
response for informational purposes. - Simple requests: Any request with an
Origin
header. In this case the middleware will pass the request through as normal, but will include appropriate CORS headers on the response.
# Keeping origins in the .env
file
In projects where you're developing with a team and whenever your application is running on several environments (for example locally as development as well as hosted somewhere), it is senseful to keep the list of origins in the .env
file. By doing that, every programmer can keep his/her own list of origins. This is specifically useful when some people in your team are running their front-end on different ports, and thus different origins.
Add a variable in the .env
file containing a string with origins, seperated by for example a comma. In the example below, 5 origins were kept in the .env
file.
DB_USERNAME=root
DB_PASSWORD=1234
DB_HOSTNAME=localhost
DOCS_URL=/docs
ALLOWED_ORIGINS="http://localhost,https://localhost,http://127.0.0.1:8080,https://127.0.0.1:8080,https://mysite.netlify.app"
2
3
4
5
Next, read out this variable in config.py
:
import os
from dotenv import load_dotenv
load_dotenv()
db_username = os.environ.get('DB_USERNAME')
db_password = os.environ.get('DB_PASSWORD')
db_hostname = os.environ.get('DB_HOSTNAME', "localhost")
documentation_url = os.environ.get("DOCS_URL", None)
cors_origins = os.environ.get("ALLOWED_ORIGINS", "*")
2
3
4
5
6
7
8
9
10
Finally, add logic in main.py
to create a list of origins, based on the string coming from the .env
file. We can use the split()
function in Python to achieve this and pass the delimiter as parameter to this function. In our specific case, the delimiter is a comma.
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
import config
app = FastAPI(docs_url=config.documentation_url)
origins = config.cors_origins.split(",")
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/")
def main():
return {"message": "Hello World"}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20