Back to Blog
Python

Python FastAPI Headers, Cookies, Forms, and File Uploads

python fastapi headers cookies forms and file uploads: Learn how to read headers and cookies, handle form data, and process file uploads in FastAPI with typed paramete...

FastAPIPythonHTTP HeadersFile UploadsWeb Development
Illustration of FastAPI request handling showing a document being uploaded through a gateway with header and cookie labels

python fastapi headers cookies forms and file uploads requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.

FastAPI provides typed parameters for every kind of request data, and headers, cookies, forms, and file uploads all follow the same dependency-injection pattern. The difference is which function you import and how the data arrives in the request. This article covers the syntax and runtime behavior for each, including how to combine them in a single endpoint.

Reading Headers with Header() and request.headers

When you declare a parameter with Header(), FastAPI reads the incoming HTTP header and converts the name to lowercase with underscores. For example, the X-Token header becomes the parameter x_token:

from fastapi import FastAPI, Header app = FastAPI() @app.get("/items") def read_items(x_token: str = Header(...)): return {"token": x_token}

The Header() function handles case-insensitive header names and converts hyphens to underscores automatically. This conversion makes header names valid Python identifiers, so you can use them as normal parameters with type validation.

If you need raw access to headers without conversion, use the Request object directly:

from fastapi import FastAPI, Request app = FastAPI() @app.get("/items") def read_items(request: Request): return {"user_agent": request.headers.get("user-agent")}

request.headers is a Headers object that behaves like a case-insensitive dictionary. Use Header() when you want automatic name conversion and validation, and request.headers when you need dynamic header names or want to iterate over all headers. The two approaches are not mutually exclusive; a single endpoint can use both.

Reading and Setting Cookies

Cookies are read with the Cookie() function, which works similarly to Header():

from fastapi import FastAPI, Cookie app = FastAPI() @app.get("/profile") def read_profile(session_id: str = Cookie(default=None)): return {"session_id": session_id}

FastAPI reads the cookie from the Cookie request header and passes the value to the parameter. The default=None makes the cookie optional, which is important because not every client sends a session cookie. Without a default, FastAPI returns a 422 validation error when the cookie is missing.

To set a cookie in a response, declare a Response parameter and call set_cookie:

from fastapi import FastAPI, Response app = FastAPI() @app.post("/login") def login(response: Response): response.set_cookie( key="session_id", value="abc123", httponly=True, secure=True, samesite="lax", max_age=3600, path="/" ) return {"message": "logged in"}

The set_cookie method accepts the standard cookie attributes: httponly, secure, samesite, max_age, path, and domain. When you inject Response as a parameter, FastAPI populates it with the response that will be sent, and any modifications you make are merged into the final response. This is the simplest way to set cookies while still returning a normal JSON body.

Handling Form Data with Form()

Form data arrives in the request body as application/x-www-form-urlencoded or multipart/form-data. FastAPI requires the python-multipart package to parse either encoding:

pip install python-multipart

Declare form fields with Form():

from fastapi import FastAPI, Form app = FastAPI() @app.post("/submit") def submit_form(name: str = Form(...), email: str = Form(...)): return {"name": name, "email": email}

Unlike JSON body parameters, form fields must be declared with Form() explicitly. FastAPI does not infer form fields from plain type annotations; a bare name: str parameter is treated as a query parameter, which causes a 422 error when the client sends a form body. Each Form() parameter maps to one field in the form body, and the same type validation rules apply as for query parameters.

Handling File Uploads with UploadFile

File uploads use File() together with the UploadFile type:

from fastapi import FastAPI, File, UploadFile app = FastAPI() @app.post("/upload") async def upload_file(file: UploadFile = File(...)): contents = await file.read() return {"filename": file.filename, "size": len(contents)}

UploadFile provides an async interface with read(), write(), seek(), and close(). The file is stored in a SpooledTemporaryFile, which keeps small files in memory and spills larger ones to disk automatically. This design avoids loading the entire file into memory at once, which matters for large uploads.

The UploadFile object also exposes filename, content_type, and headers, which are useful for validation before reading the content. For synchronous endpoints, you can access the underlying file object directly:

@app.post("/upload") def upload_file(file: UploadFile = File(...)): data = file.file.read() return {"filename": file.filename, "size": len(data)}

The sync approach is fine for small files, but the async read() method is preferred in async endpoints because it does not block the event loop.

Combining Forms and Files in One Request

A single endpoint can accept both form fields and file uploads. When you mix Form() and File() parameters, FastAPI automatically uses multipart/form-data encoding for the request body:

from fastapi import FastAPI, File, Form, UploadFile app = FastAPI() @app.post("/documents") async def create_document( title: str = Form(...), description: str = Form(default=""), file: UploadFile = File(...), ): contents = await file.read() return { "title": title, "description": description, "filename": file.filename, "size": len(contents), }

The form fields and the file are sent in the same multipart/form-data body. The order of parameters does not matter; FastAPI maps each Form() parameter to a text field and each File() parameter to a file part. You can also include multiple UploadFile parameters if the client sends several files in one request.

Security and Operational Considerations

File uploads require attention to size and content validation. FastAPI does not impose a default size limit on uploads, so a large file can consume significant memory or disk space. Instead of reading the entire file into memory, stream it to disk in chunks:

import shutil @app.post("/upload") async def upload_file(file: UploadFile = File(...)): with open(f"uploads/{file.filename}", "wb") as out_file: shutil.copyfileobj(file.file, out_file) return {"filename": file.filename}

shutil.copyfileobj copies the file in fixed-size chunks, avoiding a full read into memory. You should also validate file.content_type and the file extension before storing, since clients can send arbitrary data regardless of the declared type. The python-multipart dependency is required for both form and file endpoints; without it, FastAPI raises an error at startup when it detects Form() or File() parameters.

Cookie security follows standard web rules. Setting httponly=True prevents JavaScript from reading the cookie, which reduces the impact of XSS attacks. The secure flag forces transmission over HTTPS only and should be enabled in production. The samesite attribute controls whether the cookie is sent with cross-site requests; lax is a reasonable default for session cookies.

Common Pitfalls with These Parameters

One frequent mistake is declaring a form field without Form(). FastAPI treats a plain annotation as a query parameter, and the request fails with a 422 error because the query parameter is missing. Always use Form() for form fields and File() for uploads.

Another issue is mixing File() with Form() in a request that uses application/x-www-form-urlencoded. File uploads require multipart/form-data, and FastAPI selects this encoding automatically when a File() parameter is present. Clients must use the same encoding when sending the request.

For optional files, use UploadFile = File(default=None). The parameter becomes optional, and you must check for None before accessing the file object. The same pattern applies to optional cookies and headers: provide a default value so FastAPI does not reject requests that omit them.

When reading headers with Header(), remember that the conversion to underscores means a header named X-API-Key is accessed as x_api_key. If you forget the conversion and write X-API-Key as the parameter name, FastAPI will not match the header and will return a validation error. The request.headers approach avoids this confusion when you need to work with the original header names.

python fastapi headers cookies forms and file uploads: Pract | RYUSLOG DEV