Troubleshooting
Media & object storage

Fix django-storages Overwriting Files With the Same Name

Fix uploads replacing each other on S3 or R2 with django-storages: understand AWS_S3_FILE_OVERWRITE, generate unique upload_to paths, avoid cache-stale files at the same URL, and recover overwritten objects with bucket versioning.

Problem statement

Two users upload a file called invoice.pdf or image.jpg, and the second upload replaces the first. The first user's record now shows someone else's file. With local FileSystemStorage, Django would have renamed the second file to invoice_a8F3kQ2.pdf. With django-storages on S3, R2, or Google Cloud Storage, it silently overwrote it.

S3Storage sets file_overwrite = True by default (AWS_S3_FILE_OVERWRITE). Checking whether a key exists on object storage costs a network request per upload, so the library skips it and writes straight to the key. If your upload_to path is the same for two uploads, the second one wins.

How one upload replaces anotherUser A uploadsphotos/image.jpgUser B uploadssame keyOverwrite defaultno exists() checkA's record shows Bdata mix-upWith file_overwrite on, identical keys silently replace each other in the bucket.
  1. Generate unique keys in upload_to, for example uploads/<uuid>/<filename> or a UUID filename.
  2. Or set AWS_S3_FILE_OVERWRITE = False (or file_overwrite: False in STORAGES options) to get Django's rename behaviour.
  3. Enable bucket versioning so overwritten objects can be recovered.
  4. Never use user-supplied filenames as the whole key; sanitise and prefix them.
  5. Audit existing records for shared keys after fixing.

Step-by-step solution

1. Confirm the setting and the keys

from django.core.files.storage import default_storage
print(type(default_storage).__name__, getattr(default_storage, "file_overwrite", None))

Then look for duplicates among stored paths:

from django.db.models import Count
Document.objects.values("file").annotate(n=Count("id")).filter(n__gt=1)

Rows sharing a path point at the same object; only the last upload's content survives.

2. Generate unique paths

The most robust fix is to make collisions impossible:

import uuid, os
from django.utils.text import get_valid_filename

def document_path(instance, filename):
    base, ext = os.path.splitext(get_valid_filename(filename))
    return f"documents/{uuid.uuid4().hex}/{base[:80]}{ext.lower()}"

class Document(models.Model):
    file = models.FileField(upload_to=document_path)

Keeping the original (sanitised) name in the path gives users a sensible download name, while the UUID directory makes the key unique. Per-user prefixes (documents/<user_id>/...) also help with lifecycle rules and permissions.

two users upload invoice.pdfupload_to="invoices/"✕both write invoices/invoice.pdf✕second upload replaces the first✕two records, one fileupload_to=uuid path✓invoices/3f2a.../invoice.pdf✓invoices/9c1e.../invoice.pdf✓each record has its own fileA unique key per upload removes the collision without extra requestsdjango-deployment.com
UUID paths also stop people guessing other users' file URLs.

3. Or turn off overwriting

STORAGES = {
    "default": {
        "BACKEND": "storages.backends.s3.S3Storage",
        "OPTIONS": {"bucket_name": "myapp-media", "file_overwrite": False},
    },
    # staticfiles ...
}

Django then calls exists() and appends a random suffix on collision, at the cost of one extra request per upload. This fixes new uploads but does not make paths unguessable.

4. Beware cached stale content

Even intentional overwrites (a user replacing their avatar at avatars/42.jpg) cause trouble: CDNs and browsers cache the old object at that URL. New keys per version avoid cache invalidation entirely; if you must keep a fixed key, set short Cache-Control or invalidate the CDN path.

5. Turn on versioning and recover

S3, R2, and GCS support object versioning. With it enabled, an overwritten object's previous versions remain:

aws s3api put-bucket-versioning --bucket myapp-media --versioning-configuration Status=Enabled
aws s3api list-object-versions --bucket myapp-media --prefix invoices/invoice.pdf

Copy an old version to a new unique key and point the affected record at it. Add a lifecycle rule that expires noncurrent versions after, say, 30 days to control cost.

uploads that never collideupload_to generates a unique keyUUID directory or nameFilenames sanitised and shortenedget_valid_filenamefile_overwrite off where names repeatif keys are not uniqueBucket versioning enabledrecover overwritten objectsRaw user filename as the whole keycollisions and guessable URLsUnique keys plus versioning make an overwrite both unlikely and recoverabledjango-deployment.com
Audit existing rows for shared paths after deploying the fix.

Verification check: upload two files with the same name from different accounts; both records should have different keys and each should download its own content. Confirm versioning with aws s3api get-bucket-versioning --bucket myapp-media.

Explanation

Django's Storage.save() normally calls get_available_name(), which checks exists() and adds a random suffix if the name is taken. S3Storage overrides this when file_overwrite is true and returns the name unchanged, because object stores make existence checks slower and they are racy anyway: two concurrent uploads can both see "not exists" and both write. Generating unique keys in upload_to sidesteps both the cost and the race, which is why it is the recommended pattern for user uploads on object storage.

Edge cases / notes

  • Static files use a separate storage; overwriting there is expected (manifest storage hashes names instead).
  • Google Cloud Storage backend has the same file_overwrite option, also defaulting to true.
  • Long names. S3 keys can be up to 1,024 bytes, but FileField defaults to max_length=100; raise it if paths are long.
  • Private files. Unique keys are not access control; keep private buckets and use signed URLs.

Next steps

FAQ

Why does django-storages overwrite files?

S3Storage defaults to file_overwrite = True to avoid an existence check per upload.

Should I set AWS_S3_FILE_OVERWRITE to False?

It works, but unique keys in upload_to are faster, race-free, and less guessable.

Can I recover an overwritten file?

Only if bucket versioning was enabled before the overwrite.

Does this affect static files?

Static files use their own storage where overwriting is normal; this issue concerns user uploads.

Keep going