CORS MiddlewareΒΆ
Cross-Origin Resource Sharing (CORS) controls which external domains can make requests to your API. Cello's CORS middleware is implemented in Rust and handles preflight OPTIONS requests automatically.
Quick StartΒΆ
from cello import App
app = App()
# Allow all origins (development)
app.enable_cors()
# Allow specific origins (production)
app.enable_cors(origins=["https://example.com", "https://app.example.com"])
How CORS WorksΒΆ
When a browser makes a cross-origin request, it follows this flow:
Browser Cello Server
β β
βββ Preflight OPTIONS βββββββββββ β
β β β Rust CORS middleware checks origin
ββββ 200 + CORS headers ββββββββ β
β β
βββ Actual GET/POST request βββββ β
β β β CORS headers added to response
ββββ Response + CORS headers βββ β
Preflight Requests
Browsers send a preflight OPTIONS request for non-simple requests (those with custom headers, non-standard methods, or JSON content type). Cello handles these automatically in Rust without reaching your Python handler.
ConfigurationΒΆ
Default ConfigurationΒΆ
Default behavior:
| Setting | Default |
|---|---|
| Origins | ["*"] (all origins) |
| Methods | GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD |
| Headers | Content-Type, Authorization, Accept, Origin, X-Requested-With |
| Credentials | false |
| Max Age | 86400 seconds (24 hours) |
Restricted OriginsΒΆ
# Only allow specific domains
app.enable_cors(origins=[
"https://example.com",
"https://app.example.com",
"https://admin.example.com"
])
Wildcard and Credentials
When origins is set to ["*"], browsers will not send cookies or authorization headers. If your API requires credentials, you must list specific origins.
Preflight HandlingΒΆ
Cello automatically responds to OPTIONS preflight requests in Rust. The response includes:
Access-Control-Allow-Origin-- the matched origin or*Access-Control-Allow-Methods-- allowed HTTP methodsAccess-Control-Allow-Headers-- allowed request headersAccess-Control-Max-Age-- how long browsers should cache the preflight result
OPTIONS /api/users HTTP/1.1
Origin: https://example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400
Common PatternsΒΆ
API with Frontend SPAΒΆ
app = App()
# Allow the SPA origin
app.enable_cors(origins=["https://myapp.example.com"])
@app.get("/api/data")
def get_data(request):
return {"items": [1, 2, 3]}
Public APIΒΆ
app = App()
# Allow any origin to call the API
app.enable_cors()
@app.get("/api/public/status")
def status(request):
return {"status": "ok"}
Multiple EnvironmentsΒΆ
import os
app = App()
if os.environ.get("ENV") == "production":
app.enable_cors(origins=[
"https://app.example.com",
"https://admin.example.com"
])
else:
# Development -- allow everything
app.enable_cors()
Blueprint-Level CORSΒΆ
Apply CORS to specific route groups using blueprints:
from cello import Blueprint
# Public API -- open CORS
public = Blueprint("/api/public")
# Internal API -- no CORS (same-origin only)
internal = Blueprint("/api/internal")
app.register_blueprint(public)
app.register_blueprint(internal)
# CORS only applies to public routes when configured at the blueprint level
PerformanceΒΆ
CORS processing in Cello is extremely efficient:
| Operation | Time |
|---|---|
| Origin check | ~10ns |
| Preflight response | ~50ns |
| Header injection | ~20ns |
Preflight responses are handled entirely in Rust and never reach your Python handlers.
Next StepsΒΆ
- Middleware Overview - Full middleware system
- Security Headers - Additional security headers
- Authentication - Auth middleware