Skip to main content
Back to blog
April 28, 2026|8 min read

Flask & FastAPI Security Guide 2026: HTTPS, CORS, Pydantic & Rate Limiting

Flask security requires assembling the right extensions — the micro-framework ships with minimal defaults. This guide covers Flask-Talisman, Flask-CORS, and FastAPI's built-in security utilities.

ZeriFlow Team

1,089 words

Flask & FastAPI Security Guide 2026: HTTPS, CORS, Pydantic & Rate Limiting

Flask security starts with a blank slate — Flask's philosophy is to stay out of your way, which means security is your responsibility from the first line of code. FastAPI is more opinionated about validation, but its security configuration still requires deliberate setup. This guide covers both frameworks comprehensively.

Before starting, run a free scan at ZeriFlow to get an instant view of your current live security posture across 80+ checks.


1. Flask-Talisman: Security Headers in One Extension

Flask-Talisman wraps Flask's response pipeline and sets HTTP security headers including Content Security Policy and HTTPS enforcement.

bash
pip install flask-talisman
python
from flask import Flask
from flask_talisman import Talisman

app = Flask(__name__)

csp = {
    'default-src': "'self'",
    'script-src':  "'self'",
    'style-src':   ["'self'", 'https://fonts.googleapis.com'],
    'font-src':    ["'self'", 'https://fonts.gstatic.com'],
    'img-src':     ["'self'", 'data:', 'https:'],
}

Talisman(
    app,
    force_https=True,
    strict_transport_security=True,
    strict_transport_security_max_age=31536000,
    strict_transport_security_include_subdomains=True,
    strict_transport_security_preload=True,
    content_security_policy=csp,
    frame_options='DENY',
    referrer_policy='strict-origin-when-cross-origin',
    feature_policy={
        'geolocation': "'none'",
        'microphone':  "'none'",
        'camera':      "'none'",
    },
)

For FastAPI, use starlette-csrf and a custom middleware:

python
from fastapi import FastAPI
from fastapi.middleware.httpsredirect import HTTPSRedirectMiddleware
from fastapi.middleware.trustedhost import TrustedHostMiddleware

app = FastAPI()
app.add_middleware(HTTPSRedirectMiddleware)
app.add_middleware(TrustedHostMiddleware, allowed_hosts=['yourdomain.com', '*.yourdomain.com'])

2. CORS Configuration

Flask-CORS:

bash
pip install flask-cors
python
from flask_cors import CORS

CORS(
    app,
    origins=['https://yourdomain.com', 'https://app.yourdomain.com'],
    methods=['GET', 'POST', 'PUT', 'DELETE'],
    allow_headers=['Content-Type', 'Authorization'],
    supports_credentials=True,
    max_age=86400,
)

FastAPI built-in CORS:

python
from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=['https://yourdomain.com'],
    allow_credentials=True,
    allow_methods=['*'],
    allow_headers=['Authorization', 'Content-Type'],
    max_age=86400,
)

Never set allow_origins=['*'] on an authenticated API. Wildcard origins bypass the same-origin policy and allow any website to read your API responses in the user's browser.


3. Pydantic Validation in FastAPI

FastAPI uses Pydantic models for request validation. This is one of FastAPI's strongest security features — all input is validated and typed before your handler runs.

python
from fastapi import FastAPI, HTTPException, status
from pydantic import BaseModel, EmailStr, field_validator
import re

class UserCreate(BaseModel):
    name:     str
    email:    EmailStr
    password: str

    @field_validator('name')
    @classmethod
    def name_must_be_clean(cls, v: str) -> str:
        if len(v) < 2 or len(v) > 100:
            raise ValueError('Name must be 2-100 characters')
        if not re.match(r'^[a-zA-Z\s\-]+$', v):
            raise ValueError('Name contains invalid characters')
        return v.strip()

    @field_validator('password')
    @classmethod
    def password_strength(cls, v: str) -> str:
        if len(v) < 12:
            raise ValueError('Password must be at least 12 characters')
        if not re.search(r'[A-Z]', v):
            raise ValueError('Password must contain uppercase letter')
        if not re.search(r'[0-9]', v):
            raise ValueError('Password must contain a digit')
        return v

@app.post('/users', status_code=status.HTTP_201_CREATED)
async def create_user(payload: UserCreate):
    # payload is fully validated at this point
    hashed = hash_password(payload.password)
    # ...

For Flask, use marshmallow or pydantic directly:

python
from pydantic import BaseModel, EmailStr, ValidationError

class UserSchema(BaseModel):
    name:  str
    email: EmailStr

@app.route('/users', methods=['POST'])
def create_user():
    try:
        data = UserSchema(**request.get_json())
    except ValidationError as e:
        return {'error': e.errors()}, 400
    # data.name and data.email are validated

4. Flask Session Security

Flask's default cookie-based session uses a signed but readable cookie. For sensitive applications, use server-side sessions.

bash
pip install Flask-Session redis
python
from flask import Flask
from flask_session import Session
import redis

app = Flask(__name__)
app.config.update(
    SECRET_KEY          = os.environ['SECRET_KEY'],
    SESSION_TYPE        = 'redis',
    SESSION_REDIS       = redis.from_url(os.environ['REDIS_URL']),
    SESSION_COOKIE_SECURE   = True,
    SESSION_COOKIE_HTTPONLY = True,
    SESSION_COOKIE_SAMESITE = 'Lax',
    PERMANENT_SESSION_LIFETIME = 1800,  # 30 minutes
)
Session(app)

Always regenerate the session on login:

python
from flask import session
session.clear()
session.permanent = True
session['user_id'] = user.id

5. Rate Limiting with Flask-Limiter

bash
pip install Flask-Limiter
python
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address

limiter = Limiter(
    app=app,
    key_func=get_remote_address,
    default_limits=['200 per day', '50 per hour'],
    storage_uri=os.environ['REDIS_URL'],
)

@app.route('/auth/login', methods=['POST'])
@limiter.limit('10 per 15 minutes')
def login():
    # ...
    pass

For FastAPI, use slowapi:

python
from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.util import get_remote_address
from slowapi.errors import RateLimitExceeded

limiter = Limiter(key_func=get_remote_address)
app.state.limiter = limiter
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)

@app.post('/auth/login')
@limiter.limit('10/15minutes')
async def login(request: Request, credentials: LoginSchema):
    # ...
    pass

6. Environment Hardening and Secret Management

python
# Never hardcode secrets
# Bad:
app.secret_key = 'mysecretkey'

# Good:
import os
app.secret_key = os.environ['SECRET_KEY']

# Use python-dotenv for local development only
from dotenv import load_dotenv
load_dotenv()  # Loads .env in development, ignored in production when env vars are set

Use python-decouple for typed environment variables:

python
from decouple import config, Csv

SECRET_KEY  = config('SECRET_KEY')
DEBUG       = config('DEBUG', default=False, cast=bool)
ALLOWED_HOSTS = config('ALLOWED_HOSTS', cast=Csv())

Run ZeriFlow after every production deployment to confirm .env files and debug endpoints are not publicly accessible.


FAQ

### Q: Is Flask or FastAPI more secure by default? A: FastAPI provides better security defaults through mandatory Pydantic validation and async-first architecture. Flask is more flexible but requires more manual security setup. Both can be made equally secure with the right extensions.

### Q: How do I prevent SQL injection in Flask with SQLAlchemy? A: Use SQLAlchemy's ORM or its parameterized text() construct. Never format user input into SQL strings. The ORM handles parameterization automatically for all standard queries.

### Q: Should I use Flask-Login for authentication? A: Flask-Login handles the session management layer (current_user, login_required) but not password hashing or form validation. Combine it with bcrypt or argon2-cffi for passwords and WTForms or Pydantic for validation.

### Q: How do I handle file uploads securely in Flask? A: Use werkzeug.utils.secure_filename(), validate MIME type (not just extension), restrict allowed types, set a max content length via MAX_CONTENT_LENGTH, and store files outside the webroot.

### Q: Does FastAPI automatically handle CSRF? A: No. FastAPI is primarily an API framework — CSRF is relevant when browser cookies are used for authentication. If you use cookie-based sessions, add starlette-csrf middleware. If you use Bearer tokens in headers, CSRF is not applicable.


Conclusion

Flask and FastAPI security in 2026 means assembling the right pieces: Talisman or custom middleware for headers, strict CORS, Pydantic or marshmallow for validation, Redis-backed rate limiting, and server-side sessions for Flask. FastAPI's Pydantic integration is a genuine security advantage — leverage it on every endpoint.

Validate your configuration from the outside with ZeriFlow's free scanner — it checks your headers, TLS grade, and exposed endpoints in seconds, giving you the same view an attacker would have before launching an attack.

Ready to check your site?

Run a free security scan in 30 seconds.

Related articles

Keep reading