How to Write the Same Filename to S3 Without Overwriting

You cannot keep two separate current S3 objects at the exact same bucket and key: a second write normally replaces the current object. To keep the same visible filename without losing data, reject later writes with a conditional request, enable versioning to retain older revisions, or give every upload a unique key while retaining its original filename. The right method depends on whether a later upload should fail, create a revision, or be stored as a separate object.

What the Same Filename Means in Amazon S3

S3 does not use filesystem folders or filenames as its primary identity. An object is identified by:

  • The bucket name
  • The complete object key

For example, these are different objects because their full keys differ:

s3://my-bucket/team-a/report.csv
s3://my-bucket/team-b/report.csv

But these refer to the same object identity:

s3://my-bucket/incoming/report.csv
s3://my-bucket/incoming/report.csv

The / characters in a key create a folder-like display in the console, but they are part of one key string. S3 can therefore store many objects named report.csv under different prefixes, but it cannot store two separate current objects with the same bucket and complete key.

Amazon S3’s PutObject API treats a write to an existing key as a replacement of that object’s current contents unless you use a condition or versioning.

How to Write the Same Filename Without Overwriting

For a fixed S3 path that should be created once, use a conditional write. The first upload succeeds; a later upload to that key is rejected instead of replacing the existing object.

Choose the behavior you need before implementing the upload:

Goal Best Method What Happens on a Later Upload
Keep one stable URL and reject replacements Conditional write with If-None-Match: * The new upload fails safely
Keep a stable URL but retain history S3 Versioning The new object becomes current; the old one remains as a version
Store every upload independently Generate a unique key per upload Each upload gets its own object
Copy or sync from the AWS CLI without replacing destination files AWS CLI --no-overwrite Existing destination objects are skipped

Step 1: Use the Full Object Key as the Identifier

Decide whether the exact path must remain stable.

For example, this is a fixed, single-use key:

incoming/report.csv

If your application uses that key as a stable download URL or a canonical record location, a conditional write is usually the right choice. If the upload is a new submission that must be retained separately, generate a different key instead.

Do not rely on checking whether an object exists and then performing an ordinary upload. Another process can write to the key after your existence check but before your upload begins.

Step 2: Send a Conditional Put Request

Use the If-None-Match: * condition to tell S3 to create the object only when no current object exists at that key.

With the AWS CLI’s low-level S3 API command:

aws s3api put-object \
  --bucket my-bucket \
  --key incoming/report.csv \
  --body ./report.csv \
  --if-none-match "*"

The * condition means “write only if this object does not already exist.” S3 evaluates that condition as part of the write operation, avoiding the race condition created by a separate check-then-upload workflow.

Step 3: Treat a Conditional Failure as an Expected Result

If the key is available, the upload succeeds and your application should save the full key as the object’s permanent identifier.

If the key already exists, S3 rejects the conditional write. Do not retry the same ordinary upload command, because that would defeat the protection. Instead, choose the behavior your application requires:

  1. Report that the object already exists.
  2. Fetch or display the existing object.
  3. Generate a new key for a separate submission.
  4. Ask the user to choose a different destination name.

A fixed-key workflow should normally treat this result as a valid “already submitted” or “already claimed” state, not as a transient upload error.

Step 4: Confirm the Object Exists at the Intended Key

After a successful upload, confirm that the object exists at the exact bucket and key you saved:

aws s3api head-object \
  --bucket my-bucket \
  --key incoming/report.csv

For workflows where object integrity matters, also store a checksum, source record ID, or upload metadata in your application database. The filename alone is not a reliable record identifier.

Use Versioning When Replacement Is Acceptable

Versioning is recovery, not write prevention: it preserves an earlier object version after a new upload, while the newer upload still becomes the current object.

Enable versioning on the bucket before relying on it for recovery. Once versioning is enabled, a later upload to the same key creates another version rather than permanently discarding the earlier contents.

For example, these uploads still use one object key:

incoming/report.csv

After two uploads, the bucket has multiple versions of incoming/report.csv, but only the newest version is returned when a client requests the object without a version ID.

Versioning is useful when you want all of the following:

  • A stable object URL
  • The ability to roll back a bad replacement
  • An audit trail of content changes
  • Recovery from accidental overwrites

Save the version ID returned by each successful upload if your application needs to retrieve a precise historical revision. You can retrieve a particular version explicitly:

aws s3api get-object \
  --bucket my-bucket \
  --key incoming/report.csv \
  --version-id VERSION_ID \
  ./recovered-report.csv

Versioning increases retained storage because old versions remain until you delete them or apply a lifecycle policy. It also does not stop a bad upload from becoming current, so use a conditional write instead when replacement must never happen.

Avoid using an S3 event and a cleanup function as overwrite protection. By the time an event-driven function runs, the new object may already be current, and asynchronous cleanup does not provide the same guarantee as rejecting the write at upload time.

Store Multiple Uploads With Unique Keys

If every upload must be retained as an independent object, do not reuse a fixed S3 key. Generate a unique key for each upload and keep the original filename as metadata or in your database.

A practical pattern is:

uploads/UNIQUE-ID/report.csv

The user still sees report.csv, while your application stores the full generated key as the actual object identifier.

Here is a Python example using a UUID in the S3 key:

from pathlib import Path
from uuid import uuid4
import boto3
source = Path("report.csv")
bucket = "my-bucket"
key = f"uploads/{uuid4()}/{source.name}"
s3 = boto3.client("s3")
s3.upload_file(
    str(source),
    bucket,
    key,
    ExtraArgs={
        "Metadata": {
            "original-filename": source.name
        }
    },
)
print(f"Uploaded to s3://{bucket}/{key}")

After the upload succeeds:

  1. Save key in the database record for that upload.
  2. Use the saved key for later downloads and deletes.
  3. Display original-filename or your database filename field to users.

A UUID makes collisions extremely unlikely, but a generated name is not a substitute for server-enforced uniqueness when duplicate creation would be harmful. Use a conditional write as well if your system has a strict create-once requirement.

Use AWS CLI No-Overwrite for Copies and Syncs

AWS CLI version 2.33.0 and later supports --no-overwrite with cp, mv, and sync. It is useful for shell scripts, backups, exports, and manual transfers where an existing destination object should be left unchanged.

To copy one file without replacing an existing destination object:

aws s3 cp ./report.csv s3://my-bucket/incoming/report.csv --no-overwrite

To upload a directory while preserving destination objects that are already present:

aws s3 sync ./exports s3://my-bucket/incoming/ --no-overwrite

After the transfer, verify a destination object directly:

aws s3api head-object \
  --bucket my-bucket \
  --key incoming/report.csv

Use this option when the CLI is the upload client and skipping existing destination objects is the desired behavior. For an application-level rule that must hold regardless of which client writes to the bucket, use conditional requests and consider a bucket policy that requires conditional write headers.

Confirm the Behavior and Prevent Future Replacements

Before deploying the workflow, test the exact condition your application depends on.

  • Upload an object to the target key.
  • Attempt a second conditional upload to that same key.
  • Confirm that the second attempt is rejected rather than changing the object.
  • Confirm that a separately generated key creates a separate object.
  • If versioning is enabled, confirm that prior versions remain available.
  • Save full object keys in application records rather than saving only filenames.

For a fixed URL that must never be replaced, make conditional creation part of the upload API contract. For a document history or mutable artifact, enable versioning and save version IDs. For user uploads, generated keys are usually the simplest design because they avoid collisions before they happen.

Frequently Asked Questions

Does S3 allow duplicate files?

Yes, S3 can store identical content under different object keys, and a versioned bucket can retain multiple versions of one key. It cannot store two separate current objects with the same bucket and complete key. Objects named report.csv can coexist when their prefixes differ, such as a/report.csv and b/report.csv.

What are the naming rules for S3 files?

An S3 object key is the full identifier within a bucket, not just the final filename. Prefixes such as uploads/2026/report.csv are part of the key, and S3 does not treat them as true directories. Store and compare the complete key in your application, especially when different users can upload files with the same visible filename.

Does S3 copy overwrite an existing object?

Yes, copying to an existing destination key normally makes the copied object the current object at that key. Use aws s3 cp --no-overwrite with AWS CLI version 2.33.0 or later when a CLI copy must leave an existing destination unchanged. If you need both copies to remain independently addressable, copy to a distinct destination key instead.