# Flask — User Guide

## 1. Quick Start

Flask is a lightweight Python web framework that provides the essentials for building web applications — routing, request handling, templating, and a development server — without imposing database, form validation, or authentication choices on you.

**Key features:**

- Decorator-based URL routing with variable rules and HTTP method support
- Jinja2 templating with autoescaping and template inheritance
- Built-in development server with automatic reloading and interactive debugger
- Secure cookie-based sessions signed with `itsdangerous`
- Blueprints for modular application structure
- CLI powered by Click with extensible commands
- Async view support via `async def` handlers
- Signals for decoupled event notification (via `blinker`)
- Class-based views with `View` and `MethodView`
- Comprehensive testing utilities with `FlaskClient` and `FlaskCliRunner`

**Prerequisites:** Python 3.9+

**Install and run:**

```bash
pip install flask
```

```python
from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello, World!"
```

```bash
flask --app hello run
```

```text
 * Serving Flask app 'hello'
 * Debug mode: off
 * Running on http://127.0.0.1:5000
```

> [!TIP]
> Add `--debug` to enable the interactive debugger and automatic code reloading: `flask --app hello run --debug`

**What to try next:** See Installation for all install methods, Configuration for essential settings, or jump to Core Usage for routing and view patterns.

## 2. Installation

### System Requirements

| Requirement | Minimum |
|---|---|
| Python | 3.9+ |
| OS | Linux, macOS, Windows |

### Install Methods

=== "pip"
    ```bash
    pip install flask
    ```

=== "pip (with async support)"
    ```bash
    pip install "flask[async]"
    ```

=== "uv"
    ```bash
    uv add flask
    ```

=== "conda"
    ```bash
    conda install -c conda-forge flask
    ```

=== "From source"
    ```bash
    git clone https://github.com/pallets/flask.git
    cd flask
    pip install -e .
    ```

> [!IMPORTANT]
> To use `async def` view functions, install the `async` extra: `pip install "flask[async]"`. This pulls in `asgiref`. Without it, async views raise `RuntimeError: Install Flask with the 'async' extra in order to use async views.`

### Core Dependencies

Flask automatically installs these dependencies:

| Package | Purpose |
|---|---|
| `Werkzeug` | WSGI toolkit — request/response handling, routing, dev server |
| `Jinja2` | Template engine with autoescaping |
| `MarkupSafe` | Escapes untrusted input for safe HTML rendering |
| `itsdangerous` | Cryptographically signs session cookies |
| `click` | CLI framework powering the `flask` command |
| `blinker` | Signal support for decoupled event notification |

### Post-Install Verification

```bash
flask --version
```

```text
Python 3.13.x
Flask 3.1.x
Werkzeug 3.1.x
```

### Install Troubleshooting

**Q: `flask` command not found after install?**

Ensure the Python scripts directory is in your `PATH`. If using a virtual environment, activate it first:

```bash
python -m venv .venv
source .venv/bin/activate  # Linux/macOS
.venv\Scripts\activate     # Windows
pip install flask
```

**Q: `ModuleNotFoundError: No module named 'flask'`?**

You may have multiple Python installations. Verify with:

```bash
python -c "import flask; print(flask.__version__)"
```

**Q: Import errors from `markupsafe` or `jinja2`?**

Upgrade all dependencies together:

```bash
pip install --upgrade flask werkzeug jinja2 markupsafe
```

## 3. Configuration

Flask uses uppercase Python attributes as configuration keys. Set them on the `app.config` object, which behaves like a dictionary.

### Essential Configuration Keys

| Key | Type | Default | Description |
|---|---|---|---|
| `SECRET_KEY` | `str \| None` | `None` | Signs session cookies. **Must be set** for sessions to work. |
| `DEBUG` | `bool \| None` | `None` | Enables debug mode. Typically set via `FLASK_DEBUG` env var. |
| `TESTING` | `bool` | `False` | Enable test mode. Exceptions propagate instead of being handled. |
| `SERVER_NAME` | `str \| None` | `None` | Host and port for URL generation outside requests (e.g. `localhost:5000`). |
| `APPLICATION_ROOT` | `str` | `"/"` | Path prefix when the app is mounted at a sub-path. |
| `MAX_CONTENT_LENGTH` | `int \| None` | `None` | Maximum request body size in bytes. Returns 413 if exceeded. |
| `PREFERRED_URL_SCHEME` | `str` | `"http"` | URL scheme for `url_for` when outside a request. |

### Session Cookie Configuration

| Key | Type | Default | Description |
|---|---|---|---|
| `SESSION_COOKIE_NAME` | `str` | `"session"` | Name of the session cookie. |
| `SESSION_COOKIE_DOMAIN` | `str \| None` | `None` | Domain to set on the cookie. |
| `SESSION_COOKIE_PATH` | `str \| None` | `None` | Path scope of the cookie. |
| `SESSION_COOKIE_HTTPONLY` | `bool` | `True` | Prevent JavaScript access to the cookie. |
| `SESSION_COOKIE_SECURE` | `bool` | `False` | Only send cookie over HTTPS. |
| `SESSION_COOKIE_SAMESITE` | `str \| None` | `None` | SameSite attribute (`"Lax"`, `"Strict"`, `"None"`). |
| `SESSION_COOKIE_PARTITIONED` | `bool` | `False` | Enable partitioned cookies (CHIPS). |
| `PERMANENT_SESSION_LIFETIME` | `timedelta` | 31 days | How long a permanent session lasts. |
| `SESSION_REFRESH_EACH_REQUEST` | `bool` | `True` | Re-send the cookie on every response to refresh the expiration. |

### Setting Configuration

```python
# Direct assignment
app.config["SECRET_KEY"] = "change-me-in-production"
app.config["MAX_CONTENT_LENGTH"] = 16 * 1024 * 1024  # 16 MB

# From a Python file
app.config.from_pyfile("settings.py")

# From environment variables with FLASK_ prefix
app.config.from_prefixed_env()
# FLASK_SECRET_KEY=supersecret -> app.config["SECRET_KEY"]

# From JSON or TOML files
import json
app.config.from_file("config.json", load=json.load)

import tomllib
app.config.from_file("config.toml", load=tomllib.load, text=False)

# From a Python object or import path
app.config.from_object("yourapplication.default_config")
```

> [!WARNING]
> Only uppercase keys are loaded by `from_object`, `from_pyfile`, and `from_mapping`. Lowercase keys are silently ignored — this is by design, allowing you to use lowercase variables as local helpers in config files.

### Environment Variables

| Variable | Purpose |
|---|---|
| `FLASK_APP` | Module or import path to locate the application (e.g. `myapp` or `myapp:create_app`). |
| `FLASK_DEBUG` | Set to `1` to enable debug mode. Overrides the `DEBUG` config key. |
| `FLASK_SKIP_DOTENV` | Set to `1` to skip loading `.env` and `.flaskenv` files. |

> [!TIP]
> Create a `.flaskenv` file in your project root for development defaults. Flask loads it automatically when `python-dotenv` is installed. `.env` takes precedence over `.flaskenv`, and the actual environment takes precedence over both.

See the Admin Guide for the complete configuration reference including all keys, `TRUSTED_HOSTS`, template auto-reload, and advanced session settings.

## 4. Core Usage

### Defining Routes

Register URL rules with the `@app.route()` decorator. Each route maps a URL pattern to a Python function:

```python
@app.route("/")
def index():
    return "Hello, World!"

@app.route("/users/<username>")
def show_user(username):
    return f"User: {username}"

@app.route("/posts/<int:post_id>")
def show_post(post_id):
    return f"Post #{post_id}"
```

**URL variable converters:**

| Converter | Description | Example |
|---|---|---|
| `string` | Default — matches text without slashes | `<username>` |
| `int` | Matches positive integers | `<int:id>` |
| `float` | Matches positive floating-point values | `<float:price>` |
| `path` | Matches text including slashes | `<path:subpath>` |
| `uuid` | Matches UUID strings | `<uuid:item_id>` |

### HTTP Methods

```python
@app.route("/login", methods=["GET", "POST"])
def login():
    if request.method == "POST":
        return do_login()
    return show_login_form()
```

Shorthand decorators for common methods:

```python
@app.get("/items")
def list_items():
    return get_all_items()

@app.post("/items")
def create_item():
    return save_item(request.json)

@app.put("/items/<int:id>")
def update_item(id):
    return modify_item(id, request.json)

@app.delete("/items/<int:id>")
def delete_item(id):
    return remove_item(id)

@app.patch("/items/<int:id>")
def patch_item(id):
    return partial_update(id, request.json)
```

> [!NOTE]
> Flask automatically adds `HEAD` and `OPTIONS` methods to routes. Use `flask routes --all-methods` to see these in the route listing.

### Request Data

Access incoming request data through the `request` proxy:

```python
from flask import request

@app.route("/search")
def search():
    query = request.args.get("q", "")          # Query string: /search?q=flask
    page = request.args.get("page", 1, int)    # With type conversion
    return f"Searching for: {query}, page {page}"

@app.route("/submit", methods=["POST"])
def submit():
    name = request.form["name"]                # Form data
    file = request.files["avatar"]             # Uploaded file
    data = request.json                        # JSON body (parsed)
    raw = request.data                         # Raw body bytes
    return "OK"
```

### Response Types

View functions can return several types that Flask converts to a proper HTTP response:

| Return Type | Behavior |
|---|---|
| `str` | Becomes the response body (UTF-8 encoded) |
| `bytes` | Becomes the response body directly |
| `dict` or `list` | Automatically serialized to JSON |
| `tuple` | `(body, status)`, `(body, headers)`, or `(body, status, headers)` |
| `Response` | Returned as-is |
| Generator/Iterator | Streamed as a response |

```python
@app.route("/json")
def json_example():
    return {"message": "hello", "status": "ok"}  # Auto-JSON

@app.route("/custom")
def custom_response():
    return "Created", 201, {"X-Custom": "value"}

@app.route("/explicit")
def explicit_response():
    from flask import make_response
    resp = make_response("Hello")
    resp.headers["X-Custom"] = "value"
    return resp
```

### Using `jsonify`

For more control over JSON responses, use `jsonify`:

```python
from flask import jsonify

@app.route("/api/users")
def api_users():
    users = [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}]
    return jsonify(users)
```

### Templates (Jinja2)

Render HTML templates from the `templates/` folder:

```python
from flask import render_template

@app.route("/hello/<name>")
def hello(name):
    return render_template("hello.html", name=name)
```

Template file `templates/hello.html`:

```html
<!doctype html>
<html>
  <body>
    <h1>Hello, {{ name }}!</h1>
  </body>
</html>
```

Templates have automatic access to `request`, `session`, `g`, `config`, `url_for()`, and `get_flashed_messages()`.

> [!TIP]
> Use `stream_template()` instead of `render_template()` when generating large pages to send output incrementally to the client without buffering the entire response in memory.

### Redirects and Errors

```python
from flask import redirect, url_for, abort

@app.route("/old-page")
def old_page():
    return redirect(url_for("new_page"))

@app.route("/admin")
def admin():
    if not is_admin():
        abort(403)  # Forbidden
    return "Admin Panel"
```

### URL Building with `url_for`

Generate URLs by endpoint name instead of hardcoding paths:

```python
from flask import url_for

url_for("index")                          # "/"
url_for("show_user", username="alice")    # "/users/alice"
url_for("static", filename="style.css")  # "/static/style.css"
url_for("index", _external=True)         # "http://localhost:5000/"
url_for("index", _anchor="section")      # "/#section"
```

### Flash Messages

Display one-time messages to users across redirects:

```python
from flask import flash, get_flashed_messages

@app.route("/save", methods=["POST"])
def save():
    # ... save data ...
    flash("Changes saved successfully!")
    return redirect(url_for("index"))
```

In the template:

```html
{% for message in get_flashed_messages() %}
  <div class="alert">{{ message }}</div>
{% endfor %}
```

### Static Files

Place static files (CSS, JavaScript, images) in the `static/` folder. Flask serves them at `/static/` by default:

```html
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
```

### Sessions

Flask provides signed cookie-based sessions. Data stored in `session` is cryptographically signed but not encrypted — users can read but not modify it:

```python
from flask import session

app.config["SECRET_KEY"] = "your-secret-key"

@app.route("/login", methods=["POST"])
def login():
    session["user_id"] = user.id
    session.permanent = True  # Lasts PERMANENT_SESSION_LIFETIME (default: 31 days)
    return redirect(url_for("index"))

@app.route("/logout")
def logout():
    session.clear()
    return redirect(url_for("index"))
```

> [!WARNING]
> The `SECRET_KEY` **must** be set for sessions to work. Without it, Flask raises `RuntimeError: The session is unavailable because no secret key was set.` Use a cryptographically random value in production.

## 5. Common Tasks

### Creating an Application Factory

Use a factory function to create and configure the app. This is the recommended pattern for larger applications:

```python
# myapp/__init__.py
from flask import Flask

def create_app(test_config=None):
    app = Flask(__name__, instance_relative_config=True)
    app.config.from_mapping(
        SECRET_KEY="dev",
    )

    if test_config is None:
        app.config.from_pyfile("config.py", silent=True)
    else:
        app.config.from_mapping(test_config)

    @app.route("/hello")
    def hello():
        return "Hello, World!"

    return app
```

Run it with:

```bash
flask --app myapp run
```

### Organizing with Blueprints

Split a large application into reusable modules:

```python
# myapp/auth.py
from flask import Blueprint, render_template, redirect, url_for

auth_bp = Blueprint("auth", __name__, url_prefix="/auth")

@auth_bp.route("/login", methods=["GET", "POST"])
def login():
    return render_template("auth/login.html")

@auth_bp.route("/logout")
def logout():
    return redirect(url_for("index"))
```

Register in your app factory:

```python
def create_app():
    app = Flask(__name__)
    from .auth import auth_bp
    app.register_blueprint(auth_bp)
    return app
```

> [!NOTE]
> Blueprint names cannot be empty or contain dots. Attempting `Blueprint("", ...)` or `Blueprint("my.bp", ...)` raises `ValueError`.

### Custom Error Pages

```python
@app.errorhandler(404)
def page_not_found(e):
    return render_template("404.html"), 404

@app.errorhandler(500)
def server_error(e):
    return render_template("500.html"), 500

# Handle custom exception types
@app.errorhandler(ValueError)
def handle_value_error(e):
    return {"error": str(e)}, 400
```

### File Uploads

```python
from flask import request
import os

UPLOAD_FOLDER = "/path/to/uploads"
app.config["MAX_CONTENT_LENGTH"] = 16 * 1024 * 1024  # 16 MB

@app.route("/upload", methods=["POST"])
def upload_file():
    file = request.files["file"]
    if file.filename:
        file.save(os.path.join(UPLOAD_FOLDER, file.filename))
        return "File uploaded", 201
    return "No file selected", 400
```

### Sending Files

```python
from flask import send_file, send_from_directory

@app.route("/download/<filename>")
def download(filename):
    return send_from_directory("uploads", filename, as_attachment=True)
```

### Using Class-Based Views

For more structured request handling, use `MethodView`:

```python
from flask.views import MethodView

class UserAPI(MethodView):
    def get(self, user_id):
        if user_id is None:
            return get_all_users()
        return get_user(user_id)

    def post(self):
        return create_user(request.json)

    def put(self, user_id):
        return update_user(user_id, request.json)

    def delete(self, user_id):
        return delete_user(user_id)

user_view = UserAPI.as_view("users")
app.add_url_rule("/users/", defaults={"user_id": None}, view_func=user_view, methods=["GET"])
app.add_url_rule("/users/", view_func=user_view, methods=["POST"])
app.add_url_rule("/users/<int:user_id>", view_func=user_view, methods=["GET", "PUT", "DELETE"])
```

### Request Hooks

Execute code before and after requests:

```python
@app.before_request
def require_login():
    if request.endpoint != "login" and "user_id" not in session:
        return redirect(url_for("login"))

@app.after_request
def add_security_headers(response):
    response.headers["X-Content-Type-Options"] = "nosniff"
    return response

@app.teardown_request
def close_db(exception):
    db = g.pop("db", None)
    if db is not None:
        db.close()
```

### Async Views

With the `async` extra installed, use `async def` for views:

```python
@app.route("/async-data")
async def async_data():
    data = await fetch_data_async()
    return {"data": data}
```

### Streaming Responses

```python
from flask import stream_with_context, Response

@app.route("/stream")
def stream():
    def generate():
        for i in range(100):
            yield f"data: {i}\n\n"
    return Response(stream_with_context(generate()), content_type="text/event-stream")
```

### Custom CLI Commands

Register custom commands on the Flask CLI:

```python
import click

@app.cli.command("init-db")
def init_db_command():
    """Initialize the database."""
    init_db()
    click.echo("Database initialized.")
```

```bash
flask --app myapp init-db
```

```text
Database initialized.
```

### Writing Tests

```python
import pytest
from myapp import create_app

@pytest.fixture
def app():
    app = create_app({"TESTING": True, "SECRET_KEY": "test"})
    yield app

@pytest.fixture
def client(app):
    return app.test_client()

def test_hello(client):
    response = client.get("/hello")
    assert response.status_code == 200
    assert b"Hello" in response.data

def test_json_post(client):
    response = client.post("/api/items", json={"name": "test"})
    assert response.status_code == 201
```

> [!TIP]
> Use `app.test_client()` as a context manager to access request context locals after the response: `with app.test_client() as c: rv = c.get("/"); assert request.path == "/"`

### Testing CLI Commands

```python
def test_init_db(app):
    runner = app.test_cli_runner()
    result = runner.invoke(args=["init-db"])
    assert "Database initialized" in result.output
```

## 6. Troubleshooting

### Error: `Could not locate a Flask application`

**Cause:** The `flask` command cannot find your app. It looks for `app.py` or `wsgi.py` in the current directory, or uses `FLASK_APP` / `--app`.

**Solution:**

1. Ensure your app file is named `app.py` or `wsgi.py`, or specify it explicitly:
   ```bash
   flask --app myapp run
   ```
2. If using a factory, the function must be named `create_app` or `make_app`, or specify it:
   ```bash
   flask --app "myapp:create_app()" run
   ```
3. Ensure your working directory contains the application module.

**Prevention:** Always use `flask --app <module>` explicitly in production scripts.

### Error: `The session is unavailable because no secret key was set`

**Cause:** You accessed `session` without configuring `SECRET_KEY`.

**Solution:**

```python
app.config["SECRET_KEY"] = "a-random-secret-key"
```

**Prevention:** Set `SECRET_KEY` early in your app factory or config file.

### Error: `RuntimeError: Install Flask with the 'async' extra`

**Cause:** You used an `async def` view function without installing `asgiref`.

**Solution:**

```bash
pip install "flask[async]"
```

### Error: `View function mapping is overwriting an existing endpoint function`

**Cause:** Two routes share the same endpoint name (function name by default).

**Solution:** Give each route a unique function name, or set the `endpoint` argument:

```python
@app.route("/page1")
def page_one():
    return "Page 1"

@app.route("/page2")
def page_two():  # Not 'page' again
    return "Page 2"
```

### Error: `TemplateNotFound`

**Cause:** Flask cannot find the template file. By default, templates are loaded from the `templates/` folder relative to your application's root path.

**Solution:**

1. Check the file exists at `templates/<name>.html` relative to your app module.
2. For blueprints, templates are at `<blueprint_package>/templates/`.
3. Enable `EXPLAIN_TEMPLATE_LOADING` to see where Flask looks:
   ```python
   app.config["EXPLAIN_TEMPLATE_LOADING"] = True
   ```

### Error: `413 Request Entity Too Large`

**Cause:** The request body exceeds `MAX_CONTENT_LENGTH`.

**Solution:** Increase the limit or handle the error:

```python
app.config["MAX_CONTENT_LENGTH"] = 64 * 1024 * 1024  # 64 MB
```

### Error: `BuildError` when using `url_for`

**Cause:** The endpoint does not exist, or required URL variable values were not provided.

**Solution:** Verify the endpoint name matches your view function or `endpoint=` argument. For blueprint routes, prefix with the blueprint name: `url_for("auth.login")`.

### Werkzeug compatibility issues (#5961)

**Cause:** Flask tests may break when Werkzeug updates change internal behavior (e.g., IDNA handling).

**Solution:** Pin compatible Werkzeug versions in your requirements or let Flask's dependency constraints handle it:

```text
flask>=3.1
werkzeug>=3.1,<4.0
```

> [!WARNING]
> Always test your application after upgrading Werkzeug. Flask and Werkzeug are developed together but minor version bumps can occasionally change behavior.

> [!CAUTION]
> Do not use `flask run` or `app.run()` in production. The development server is not designed for security or performance. See the FAQ for production deployment options.

## 7. FAQ

**Q: How do I run Flask in production?**

Do not use `flask run` or `app.run()` in production. Use a production WSGI server such as Gunicorn, Waitress, or uWSGI. See the Admin Guide for deployment details.

```bash
pip install gunicorn
gunicorn -w 4 "myapp:create_app()"
```

**Q: How do I enable debug mode?**

Set the `FLASK_DEBUG` environment variable or use the `--debug` flag:

```bash
export FLASK_DEBUG=1
flask --app myapp run
# or
flask --app myapp run --debug
```

**Q: How do I serve the app on all network interfaces?**

```bash
flask run --host 0.0.0.0
```

**Q: How do I change the default port?**

```bash
flask run --port 8080
```

**Q: How do I use HTTPS during development?**

Generate an ad-hoc certificate (requires the `cryptography` package):

```bash
pip install cryptography
flask run --cert adhoc
```

Or provide your own certificate and key:

```bash
flask run --cert cert.pem --key key.pem
```

**Q: How do I access the app inside the interactive shell?**

```bash
flask --app myapp shell
```

This opens a Python REPL with `app`, `g`, and any registered shell context processors pre-loaded.

**Q: Can I use `.env` files for configuration?**

Yes, install `python-dotenv`:

```bash
pip install python-dotenv
```

Flask automatically loads `.flaskenv` and `.env` files from the project directory. `.env` overrides `.flaskenv`. You can also specify a custom env file:

```bash
flask --env-file .env.local run
```

**Q: How do I list all registered routes?**

```bash
flask routes
```

```text
Endpoint  Methods  Rule
--------  -------  ---------
index     GET      /
static    GET      /static/<path:filename>
```

Use `--sort rule` to sort by URL pattern, or `--all-methods` to include HEAD and OPTIONS.

**Q: How do I handle CORS?**

Flask does not include CORS handling. Use the `flask-cors` extension:

```bash
pip install flask-cors
```

```python
from flask_cors import CORS
CORS(app)
```

**Q: What is the `g` object?**

`g` is a per-request namespace for storing data during request processing. It is commonly used to hold database connections or authenticated user objects. Data stored in `g` is available only during the current request and cleared automatically afterward.

**Q: How do I register CLI commands from extensions or plugins?**

Extensions can register commands via the `flask.commands` entry point group in their `pyproject.toml`. Flask discovers and loads these automatically.

---

_Generated May 2026 from pallets/flask at 2ac8988. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
