Configuration
Configure the upload provider and settings in your settings.py:
All upload settings live under the single HABARICO_UPLOADS namespace (mirrors
HABARICO_PREFERENCES and ADMIN_REST_FRAMEWORK):
HABARICO_UPLOADS = {
# Ordered list of providers to enable: any of "s3", "tus". Default [] (chunked upload
# disabled, base64 only - fully backward compatible). For each field the first entry whose
# provider supports that field's storage wins, so earlier entries have priority.
# Unknown names raise ImproperlyConfigured at startup.
"ENABLED_PROVIDERS": ["s3", "tus"],
# File-size threshold in bytes at/above which chunked upload is used; default 5 MB
"CHUNK_THRESHOLD": 5_242_880,
# Size of a single chunk/part in bytes; default 5 MB (S3 clamps to its 5 MB minimum)
"CHUNK_SIZE": 5_242_880,
# Largest file in bytes an upload may claim; None (the default) applies no limit of your own
"MAX_UPLOAD_SIZE": None,
# Content types refused at `initiate` by default, regardless of what a field's own allowlist
# derives to. Override to change the list, or set to an empty collection to disable the floor
# project-wide. See [Content-type allowlisting](./frontend-contract.md#content-type-allowlisting).
"DANGEROUS_CONTENT_TYPES": {"text/html", "application/xhtml+xml", "image/svg+xml"},
# S3 configuration (used when "s3" is enabled); the bucket/region/endpoint/credentials come
# from the target field's django-storages S3 backend - only upload knobs live here.
"S3": {
"TMP_PREFIX": "tmp/uploads/", # Temporary files prefix (within the storage location)
"PRESIGN_EXPIRY": 3600, # Presigned URL expiry in seconds
},
# TUS configuration (used when "tus" is enabled); requires "ENDPOINT" to be available.
"TUS": {
"ENDPOINT": "https://tusd.example.com/files/", # TUS server endpoint
"TMP_PREFIX": "tmp/uploads/", # Temporary files prefix
"TMP_STORAGE": None, # Django storage alias holding the finished upload; None = default_storage
},
}
| Key | Default | What it does |
|---|---|---|
ENABLED_PROVIDERS | [] | Ordered provider list; [] disables chunked upload entirely |
CHUNK_THRESHOLD | 5_242_880 | File size in bytes from which the chunked path is taken |
CHUNK_SIZE | 5_242_880 | Size of one chunk / multipart part |
MAX_UPLOAD_SIZE | None | Largest file (bytes) an upload may claim; None applies no limit of your own. See Upload size limits |
DANGEROUS_CONTENT_TYPES | {"text/html", "application/xhtml+xml", "image/svg+xml"} | Content types refused by default; override or empty at your own risk. See Content-type allowlisting |
S3["TMP_PREFIX"] | "tmp/uploads/" | Prefix of the temporary object inside the field's storage |
S3["PRESIGN_EXPIRY"] | 3600 | Lifetime of a presigned URL, in seconds |
TUS["ENDPOINT"] | None | tus server URL; without it the tus provider is unavailable |
TUS["TMP_PREFIX"] | "tmp/uploads/" | Prefix the tus server writes under |
TUS["TMP_STORAGE"] | None | Django storage alias the finished upload is read from; None uses default_storage |
Upload size limits
The S3 provider presigns one URL per part, and the part count follows the size the client claims
for a file nobody has uploaded yet. It therefore refuses outright, before anything is created in
S3, any upload that would need more than providers.s3.MAX_PARTS — 10 000 parts, S3's own
multipart limit. That ceiling is not configurable: an upload past it could not be completed by S3
either way.
MAX_UPLOAD_SIZE is your own policy on top of that, and it is off by default. Left None, the
effective ceiling is MAX_PARTS * CHUNK_SIZE — roughly 48.8 GiB at the default chunk size. Set it
when your files are known to be far smaller:
HABARICO_UPLOADS = {"MAX_UPLOAD_SIZE": 104_857_600} # 100 MiB
A field may also carry a MaxFileSizeValidator of its own (see
Per-field size limits). The two do not override
each other — initiate applies whichever of the two is stricter, so MAX_UPLOAD_SIZE stays the
project-wide ceiling no field can raise.
Either limit raises UploadTooLarge, which initiate answers with 422 Unprocessable Entity like
every other upload error — the same status the Ruby library answers with; the frontend shows the message on the file's row. The limit is a policy,
not a guarantee — nothing verifies the claimed size against the bytes that actually arrive, so
pair it with the lifecycle rule in
S3 temporary file cleanup.
The S3 and TUS blocks are merged key by key with their defaults, so setting only
TUS["ENDPOINT"] keeps the default TMP_PREFIX. The top level merges the same way: every
key left out of HABARICO_UPLOADS falls back to its default individually.