Fix RequestDataTooBig and TooManyFieldsSent in Django
Fix Django RequestDataTooBig, TooManyFieldsSent, and TooManyFilesSent errors: what each upload limit measures, raising limits safely per endpoint, and proxy limits.
Problem statement
A form submission, JSON API call, or admin bulk edit fails with a 400 Bad Request, and the logs show one of:
django.core.exceptions.RequestDataTooBig: Request body exceeded settings.DATA_UPLOAD_MAX_MEMORY_SIZE.
django.core.exceptions.TooManyFieldsSent: The number of GET/POST parameters exceeded settings.DATA_UPLOAD_MAX_NUMBER_FIELDS.
django.core.exceptions.TooManyFilesSent: The number of files exceeded settings.DATA_UPLOAD_MAX_NUMBER_FILES.
These are deliberate protections. Without them, a single request with a huge body or hundreds of thousands of form fields could consume a worker's memory and CPU. Django raises them as SuspiciousOperation subclasses, which produce a 400 response and are logged under django.security.RequestDataTooBig or similar logger names.
They are different from:
- Nginx
413 Request Entity Too Large: the proxy rejected the body before Django saw it - Upload size validation in your form: application rules you wrote
FILE_UPLOAD_MAX_MEMORY_SIZE: a threshold for streaming uploads to disk, not a limit that rejects requests
Quick answer
- Identify which limit fired from the exception name.
RequestDataTooBig: the non-file part of the request body exceededDATA_UPLOAD_MAX_MEMORY_SIZE(2.5 MB by default). File uploads inmultipart/form-datado not count toward it; large JSON bodies and big form fields do.TooManyFieldsSent: more thanDATA_UPLOAD_MAX_NUMBER_FIELDS(1000 by default) parameters, common in large admin changelist edits and formsets.TooManyFilesSent: more thanDATA_UPLOAD_MAX_NUMBER_FILES(100 by default) files in one request.- Prefer fixing the request shape (pagination, batching, direct-to-storage uploads) over raising global limits.
- If you raise a limit, raise it moderately and consider per-view handling instead of global changes.
- Keep proxy limits (
client_max_body_size) consistent with Django's limits.
Step-by-step solution
1. Find the exception and the endpoint
sudo journalctl -u gunicorn --since "1 hour ago" | grep -E "RequestDataTooBig|TooManyFieldsSent|TooManyFilesSent" -B2 | head -20
These exceptions are logged as security warnings. If your LOGGING configuration silences django.security, you may only see 400 responses in access logs. Add a handler for django.security at WARNING or above so they are visible.
2. Understand what each setting measures
| Setting | Default | What it counts |
|---|---|---|
DATA_UPLOAD_MAX_MEMORY_SIZE | 2621440 (2.5 MB) | request body size excluding file upload data; checked when accessing request.body or request.POST |
DATA_UPLOAD_MAX_NUMBER_FIELDS | 1000 | number of GET and POST parameters |
DATA_UPLOAD_MAX_NUMBER_FILES | 100 | number of files in a multipart/form-data request |
FILE_UPLOAD_MAX_MEMORY_SIZE | 2621440 (2.5 MB) | uploads larger than this are streamed to a temporary file instead of kept in memory |
The DATA_UPLOAD_* settings can be set to None to disable the check, which removes the protection entirely and is not recommended for public endpoints.
3. RequestDataTooBig with JSON APIs
A JSON body counts fully toward DATA_UPLOAD_MAX_MEMORY_SIZE because it is read into memory as request.body. A client sending a 5 MB JSON array fails with the default settings.
Better options than raising the limit globally:
- Batch the data: send records in chunks of a few hundred.
- Upload a file instead: accept a
multipart/form-dataupload of a JSON or CSV file, which Django streams to disk, and process it in a background task. - Upload directly to object storage: issue a presigned URL, let the client upload, and send only the object key to Django.
If a specific integration genuinely needs a larger body, raise the limit to a reasoned value:
DATA_UPLOAD_MAX_MEMORY_SIZE = 10 * 1024 * 1024 # 10 MB
This applies to every request. Worker memory use for concurrent large requests grows accordingly.
4. RequestDataTooBig with forms that include files
For multipart/form-data, file contents are not counted, but every non-file field is. Forms that embed base64-encoded images in hidden fields, large rich-text content, or serialized state in a hidden input can exceed the limit. Send binary data as real file fields instead of base64 strings; base64 is about a third larger and counts toward the limit.
5. TooManyFieldsSent in the admin
Admin changelists with list_editable and large pages, or inline formsets with many rows, submit several fields per row. For example, 200 rows with 6 editable fields each, plus management form fields, exceeds 1000.
Fix the request shape first:
class ProductAdmin(admin.ModelAdmin):
list_editable = ["price", "stock"]
list_per_page = 50
For inlines, limit rows with max_num and extra, or edit large collections in a dedicated view with pagination.
If the form legitimately needs more fields (for example a bulk import confirmation screen used by staff), raise the limit moderately:
DATA_UPLOAD_MAX_NUMBER_FIELDS = 5000
The limit also covers query string parameters on GET requests, so extremely long filter URLs can trigger it too.
6. TooManyFilesSent
Multi-file upload widgets that submit hundreds of files in one request hit DATA_UPLOAD_MAX_NUMBER_FILES. Upload files in smaller batches from the client, or use direct-to-storage uploads that send one file per request. Raise the limit only if batches of that size are an intended feature.
7. Handling the error per view
Because the check happens when request.POST, request.FILES, or request.body is accessed, a view can catch it and return a clear message instead of a generic 400:
from django.core.exceptions import RequestDataTooBig
from django.http import JsonResponse
def import_records(request):
try:
payload = request.body
except RequestDataTooBig:
return JsonResponse(
{"detail": "Payload too large. Send at most 1000 records per request."},
status=413,
)
# ... parse and process payload ...
Middleware that reads request.POST or request.body before your view (for example logging or CSRF processing on POST requests) can trigger the exception earlier. CsrfViewMiddleware reads request.POST for form submissions, so for form posts the exception typically occurs before the view runs. Catching it in the view is most reliable for JSON APIs exempt from form-based CSRF.
8. Keep proxy limits consistent
Nginx rejects bodies larger than client_max_body_size (1 MB by default) with 413 before Django sees them:
server {
client_max_body_size 25m;
}
Set it per location if only upload endpoints need large bodies:
location /api/imports/ {
client_max_body_size 50m;
proxy_pass http://unix:/run/myapp/gunicorn.sock;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
}
A CDN or load balancer in front may impose its own maximum as well. For large file uploads, Django's DATA_UPLOAD_MAX_MEMORY_SIZE usually does not need to change, because file content does not count toward it; the proxy limit is the one to raise.
9. Verify
Generate a request just above and just below the limits in staging:
python3 -c "import json; print(json.dumps([{'id': i, 'name': 'x' * 100} for i in range(20000)]))" > /tmp/big.json
ls -lh /tmp/big.json
curl -s -o /dev/null -w "%{http_code}\n" -X POST -H "Content-Type: application/json" \
--data-binary @/tmp/big.json https://staging.example.com/api/imports/
Verification check:
- requests within your intended limits succeed
- oversized requests return a clear
400or413response, not a500 django.securitywarnings are logged and visible- Nginx limits are equal to or larger than the largest body you intend Django to accept
Explanation
Django parses request bodies lazily and enforces size and count limits during parsing, so a malicious or buggy client cannot force a worker to hold arbitrarily large data in memory or iterate over enormous numbers of fields. The defaults suit typical web forms. When legitimate traffic hits them, the usual cause is a request design that sends too much at once, and changing that design is safer than turning protections off.
Edge cases / notes
Large GET query strings
Very long URLs can also be rejected by Nginx (414 Request-URI Too Large) or by browsers before Django's field count applies.
Streaming request bodies
Django reads request bodies through the WSGI input stream. For very large uploads, streaming directly to object storage from the client is more efficient than any server-side setting.
DRF parsers
Django REST Framework's parsers read request.body or the stream through Django, so the same limits apply to API requests.
Designing large imports without raising limits
When a feature needs to accept large amounts of data, a staged design keeps web requests small and workers safe:
- Upload: the browser uploads a CSV or JSON file directly to object storage using a presigned URL, or to Django as a normal file field (file content does not count toward
DATA_UPLOAD_MAX_MEMORY_SIZE). - Register: the client sends a small request with the object key; Django creates an
ImportJobrow with statusqueued. - Process: a Celery task streams the file, validates rows in batches, and writes results with
bulk_createin chunks. - Report: the job stores counts and errors; the UI polls a status endpoint or receives an email.
import csv
import io
from django.core.files.storage import default_storage
def iter_csv_rows(storage_key, batch_size=1000):
with default_storage.open(storage_key, "rb") as raw:
text = io.TextIOWrapper(raw, encoding="utf-8", newline="")
batch = []
for row in csv.DictReader(text):
batch.append(row)
if len(batch) >= batch_size:
yield batch
batch = []
if batch:
yield batch
Each batch is validated and saved independently, so memory use stays flat regardless of file size, and a single bad row does not have to fail the whole import.
Logging security exceptions visibly
These exceptions are reported through loggers under django.security. Make sure they reach your logs without flooding error trackers:
LOGGING["loggers"]["django.security"] = {
"handlers": ["console"],
"level": "WARNING",
"propagate": False,
}
A sudden increase in RequestDataTooBig warnings can mean a client release started sending larger payloads, or that someone is probing your endpoints with oversized requests.
Common mistakes
- Setting
DATA_UPLOAD_MAX_MEMORY_SIZE = Noneglobally to "fix" one import endpoint. - Confusing
FILE_UPLOAD_MAX_MEMORY_SIZEwith a maximum upload size. - Sending images as base64 strings in form fields.
- Raising Django limits while Nginx still rejects the request with
413.
Next steps
- Fix proxy rejections with Fix Nginx 413 Request Entity Too Large for Django Uploads.
- Move large uploads off your servers with Store Django Media Files on Cloudflare R2.
- If uploads fail when writing to disk, see Fix Permission Denied on Django Media Uploads.
FAQ
What is the default DATA_UPLOAD_MAX_MEMORY_SIZE in Django?
2.5 MB (2621440 bytes). It limits the request body size excluding file upload data, and is checked when Django reads request.body or parses request.POST.
Does DATA_UPLOAD_MAX_MEMORY_SIZE limit file uploads?
No. File contents in multipart/form-data requests are not counted. It limits JSON bodies, form fields, and other non-file data.
How do I fix TooManyFieldsSent in the Django admin?
Reduce the number of fields per request by lowering list_per_page for editable changelists or limiting inline rows. Raise DATA_UPLOAD_MAX_NUMBER_FIELDS moderately only if the form needs it.
Why does Django return 400 instead of 413 for large requests?
These exceptions are SuspiciousOperation subclasses, which Django turns into 400 Bad Request. You can catch them in a view and return 413 with a clearer message.