You create a logs folder in the S3 console, then code listing the objects finds a zero-byte object named logs/. Why did creating a folder produce an object? A console folder view and a stored S3 object key are different concepts.
Why is an S3 folder not a physical directory?
In a general purpose S3 bucket, the primary stored resources are objects. Each has a key that serves as its name within the bucket. logs/app.log is one object key containing a slash, rather than a file inside a filesystem directory named logs. The console groups keys with a shared logs/ prefix and displays them as a folder.
logs/ and logs/app.log are distinct keys. logs-old/ has a different prefix even though its first four letters are the same.
Why does an empty folder appear as a zero-byte object?
Imagine these three keys in one bucket:
logs/: a zero-byte marker created when you make an emptylogsfolder in the consolelogs/app.log: a log object displayed inside thelogsfolderlogs-old/app.log: a separate object displayed underlogs-old
Creating an empty folder through the console produces a zero-byte object whose key ends in /. The AWS S3 folder documentation explains this marker and the flat structure of general purpose buckets. If you upload logs/app.log directly, the console can show a logs folder without a separate logs/ marker because the shared prefix is enough.
Deleting only the logs/ marker does not necessarily remove the folder view while logs/app.log remains. If neither key remains, there is no object preserving that empty folder name. Do not treat the folder icon and the zero-byte object as the same thing.
How do prefixes logs and logs/ differ?
A prefix filter tests whether a key begins with those exact characters. With these three keys, logs also matches logs-old/app.log. logs/ limits the result to the marker and the log object. The slash keeps a different set of logs out of the query.
keys = ["logs/", "logs/app.log", "logs-old/app.log"]
print([key for key in keys if key.startswith("logs")])
print([key for key in keys if key.startswith("logs/")])
print([key for key in keys if key.startswith("logs/") and key != "logs/"])['logs/', 'logs/app.log', 'logs-old/app.log']
['logs/', 'logs/app.log']
['logs/app.log']This code checks key-string matching without contacting S3. A real object-listing request with Prefix set to logs/ can include the logs/ marker, so distinguish it if you want only file-like objects. That matters when cleaning a folder or counting files by prefix.
Key takeaways
In a general purpose S3 bucket, a console “folder” is a view grouped by an object-key prefix. Creating an empty folder in the console can create a logs/ zero-byte marker; child objects can make the folder appear without it. Search with logs/ rather than logs when the slash is the intended boundary, and distinguish the marker from the actual log objects.

