In an Amazon S3 bucket with versioning enabled, what happens when a client sends a DELETE for an object key without specifying a version ID, and how do you get the object back?
answer
- nothing is erased on delete
- a new current version with no body
- 404 to the client, bytes still billed
- remove the marker by its version ID
- s3:DeleteObject vs s3:DeleteObjectVersion
basics
~20 sS3 erases nothing: it adds a delete marker as the new current version, so plain GETs return 404 while every earlier version stays stored and billed. You restore by deleting the delete marker by its own version ID.
solid answer
~40 sWith versioning enabled, a `DELETE` that omits a version ID is not a destructive operation. S3 inserts a **delete marker** — a zero-byte placeholder version with its own fresh version ID — and makes it the current version of the key. A normal `GetObject` then returns 404 with an `x-amz-delete-marker: true` response header, but the real bytes are untouched as noncurrent versions. To undelete, call `ListObjectVersions` for the key, find the delete marker, and issue `DeleteObject` with `--version-id` pointing at the marker; removing the marker promotes the most recent real version back to current. Permanently destroying data requires a `DELETE` that names a real version ID, which is a different IAM action (`s3:DeleteObjectVersion`) from `s3:DeleteObject`. That split is the whole point: application credentials can "delete" objects without being able to lose them.
code
bash · 16 linesBUCKET=my-bucket
KEY=report.pdf
# Versioned DELETE with no version ID: S3 inserts a delete marker, erases nothing
MARKER=$(aws s3api delete-object --bucket "$BUCKET" --key "$KEY" \
--query VersionId --output text)
# The key now reads as missing
aws s3api head-object --bucket "$BUCKET" --key "$KEY" || echo "404 - shadowed by marker"
# Every real version is still listed
aws s3api list-object-versions --bucket "$BUCKET" --prefix "$KEY"
# Undelete: remove the delete marker by its own version ID
aws s3api delete-object --bucket "$BUCKET" --key "$KEY" --version-id "$MARKER"
aws s3api get-object --bucket "$BUCKET" --key "$KEY" restored.pdfgo deeper
Be able to say plainly that a delete in a versioned bucket adds a delete marker, GETs then return 404, and the object comes back by deleting that marker by its version ID.
Explain the mechanics: the marker is a bodyless version with its own ID, 404 versus 405 on reads, ListObjectVersions versus ListObjectsV2, and that every retained version is billed in full.
Show the operational judgment — split s3:DeleteObject from s3:DeleteObjectVersion across roles, pair versioning with a retention policy so the bucket does not grow forever, and know that versioning alone does not stop a principal that holds version-level delete.
Own the policy: decide which buckets get versioning by default across the estate, what the recovery objective is, who holds break-glass version deletion, and how the retained-version cost is budgeted and charged back.
## The two shapes of DELETE S3 has one `DeleteObject` API but two very different behaviours, decided by whether the request carries a version ID and whether the bucket is versioned. On a bucket with versioning **enabled**, `DELETE /key` with no version ID is a *logical* delete. S3 creates a new version of the key called a **delete marker** and makes it the current version. Nothing is overwritten and nothing is freed. `DELETE /key?versionId=<id>` is the *physical* delete: it removes exactly that one version's bytes, permanently, with no undo. On an unversioned bucket there is only one shape — the object is gone. ## What a delete marker actually is A delete marker is a version of the key like any other. It has a unique version ID, a last-modified timestamp, and an owner; what it does not have is a body. It stores only metadata, so its storage cost is negligible, but it is visible in `ListObjectVersions` and it shadows everything behind it. The response to the deleting call tells you what happened: ```bash aws s3api delete-object --bucket my-bucket --key report.pdf # { "DeleteMarker": true, "VersionId": "L4kqtJlcpXro..." } ``` The `DeleteMarker: true` field and the `x-amz-delete-marker` response header are how a client distinguishes "a marker was inserted" from "a version was destroyed". ## Reading a key that is shadowed by a marker - `GetObject` / `HeadObject` with no version ID → **404 Not Found**, with `x-amz-delete-marker: true` and `x-amz-version-id` naming the marker. - `GetObject` with the *delete marker's* version ID → **405 Method Not Allowed**. You cannot download a marker; it has no body. - `GetObject` with a real noncurrent version's ID → **200**, the original bytes. The data was never gone. - `ListObjectsV2` (the flat listing most tooling uses) does not show the key at all; only `ListObjectVersions` reveals the marker and the versions behind it. That asymmetry explains a common support ticket: the console and the SDK both say the object does not exist, while the storage bill has not moved a cent. ## Undeleting Restoring is simply removing the shadow: ```bash # find the marker aws s3api list-object-versions --bucket my-bucket --prefix report.pdf # delete the marker itself, by its version ID aws s3api delete-object --bucket my-bucket --key report.pdf \ --version-id L4kqtJlcpXro... ``` Once the marker is gone, the next most recent version becomes current again and plain GETs work. The alternative, useful when you want an audit trail rather than a rollback, is to `CopyObject` the old version onto the same key — that creates a brand-new current version whose content is the old one, leaving the marker in history. ## The permission split that makes this useful The protective value of versioning is entirely in IAM. `s3:DeleteObject` allows the logical delete (marker insertion). `s3:DeleteObjectVersion` allows the physical one. Grant an application only the former, keep the latter for a small break-glass role, and a compromised or buggy application literally cannot destroy stored data — it can only hide it. Reading old versions likewise needs `s3:GetObjectVersion`, not just `s3:GetObject`. This is also why "we have versioning on" is not by itself an answer to a ransomware question: if the same principal holds `s3:DeleteObjectVersion`, an attacker can walk the version list and remove every version. Versioning is the mechanism; the permission split, and stronger primitives layered on top of it, are the control. ## Costs and housekeeping Every retained version is billed at the full size of that version in its storage class — S3 stores whole objects, not deltas, so ten uploads of a 1 GB file cost 10 GB. Delete markers themselves are effectively free, but they accumulate. A versioned bucket therefore needs an explicit retention policy (noncurrent-version expiration) or it grows forever; without one, "we deleted that data last year" is usually false. ## Enabling and the states Versioning is a bucket-level setting, off by default, turned on with `PutBucketVersioning` (`Status=Enabled`). It applies only to objects written after it is enabled: objects that already existed get the literal version ID `null` until they are next overwritten. And once enabled, a bucket can only be *suspended*, never returned to a truly unversioned state.
- What status code does a GET return for a key shadowed by a delete marker, and what if you request the marker's version ID directly?A plain `GetObject` returns 404 Not Found, with `x-amz-delete-marker: true` and `x-amz-version-id` identifying the marker. Requesting the marker's own version ID returns 405 Method Not Allowed, because a delete marker has no body to return. Requesting a real noncurrent version's ID returns 200 and the original bytes.
- Which IAM permissions would you grant an application so it can "delete" objects but cannot actually lose data?Grant `s3:PutObject`, `s3:GetObject` and `s3:DeleteObject`, and withhold `s3:DeleteObjectVersion` (and usually `s3:PutBucketVersioning`). The application can then only insert delete markers. Keep version-level deletion in a separate break-glass role so a compromised application credential cannot walk the version list and destroy history.
- Does enabling versioning protect objects that were already in the bucket?Yes, but they carry the literal version ID `null` until they are next overwritten. A pre-existing object has exactly one version, the null one; the first PUT after enabling versioning gives it a real version ID and preserves the null version as noncurrent. So protection starts at enablement, not retroactively.
saying these in an interview costs you the question
- Says a versioned DELETE frees storage immediately
- Thinks S3 stores diffs between versions rather than full copies
- Assumes s3:DeleteObject alone can permanently destroy data
- Claims restoring requires re-uploading from an external backup
- Believes ListObjectsV2 shows delete markers