An S3 bucket receives many kinds of uploads. Using S3 Event Notifications, how do you trigger a function only for .jpg objects written under the uploads/ prefix, and what kinds of matching are not possible?
answer
- only two rules exist
- anchored at the start and the end
- nothing in the middle can match
- case-sensitive literal comparison
- overlapping configs are rejected
basics
~20 sAttach a Filter with two key FilterRules on the notification configuration: prefix uploads/ and suffix .jpg. S3 only supports these two literal, case-sensitive string rules — there are no wildcards, no regular expressions, and no filtering on tags, size, or metadata.
solid answer
~40 sS3's notification configuration takes a `Filter` on the object key with at most one `prefix` rule and one `suffix` rule, so `prefix: uploads/` plus `suffix: .jpg` is exactly the supported shape. Both are literal, case-sensitive string comparisons on the full key — `.JPG` will not match, and there is no way to express a wildcard in the middle of a key such as `uploads/*/raw/*.jpg`. You also cannot filter on object size, object tags, user metadata, or the uploading principal. If you need richer matching, enable EventBridge notifications on the bucket instead and let the rule's event pattern do content-based filtering on any field in the event. One extra constraint bites people: two configurations that share an event type and whose key filters overlap are rejected outright by S3.
code
bash · 19 linesaws s3api put-bucket-notification-configuration \
--bucket ingest-bucket \
--notification-configuration '{
"LambdaFunctionConfigurations": [
{
"Id": "jpg-uploads",
"LambdaFunctionArn": "arn:aws:lambda:us-east-1:111122223333:function:Thumbnailer",
"Events": ["s3:ObjectCreated:*"],
"Filter": {
"Key": {
"FilterRules": [
{ "Name": "prefix", "Value": "uploads/" },
{ "Name": "suffix", "Value": ".jpg" }
]
}
}
}
]
}'go deeper
Recall that filtering happens with exactly two rules — prefix and suffix — and be able to write the pair for a concrete example without reaching for a wildcard.
Explain that both rules are literal, case-sensitive comparisons on the full key, and name what is unmatchable: middles, multiple prefixes, tags, size and content.
Show that key layout is the routing table: design prefixes so filters do the routing, and know when the requirement has outgrown the native filter and belongs on EventBridge.
Own the convention itself — a bucket-wide key schema that makes routing, access control and lifecycle all expressible as prefixes, so teams are not each inventing their own filtering workaround.
## The only filter S3 offers A notification configuration entry names a destination, a list of event types, and an optional `Filter`. That filter is narrow by design: it applies to the object **key** only, and it accepts at most one rule named `prefix` and at most one named `suffix`. ```json { "LambdaFunctionConfigurations": [ { "Id": "jpg-uploads", "LambdaFunctionArn": "arn:aws:lambda:us-east-1:111122223333:function:Thumbnailer", "Events": ["s3:ObjectCreated:*"], "Filter": { "Key": { "FilterRules": [ { "Name": "prefix", "Value": "uploads/" }, { "Name": "suffix", "Value": ".jpg" } ] } } } ] } ``` The semantics are plain string comparison against the whole key. `uploads/2026/photo.jpg` matches. `raw/uploads/photo.jpg` does not, because the prefix must start the key. `uploads/photo.JPG` does not, because the comparison is case-sensitive — if producers are inconsistent about extension case you need two configurations, or you filter inside the handler. ## What you cannot express - **No wildcards or regular expressions.** There is no `uploads/*/final/*.jpg`. A prefix anchors the start and a suffix anchors the end; the middle is unconstrained and unmatchable. - **No multiple prefixes in one rule.** One prefix per configuration. Three prefixes means three configurations (or one broad configuration and a check in the consumer). - **No non-key attributes.** Object size, storage class, object tags, user metadata and the identity of the uploader are all invisible to the filter, even though several of them appear in the delivered event. - **No content inspection.** The filter runs on the key, never on the bytes. ## The overlap rule S3 refuses to save two configurations that share an event type when their key filters overlap. Adding a second `s3:ObjectCreated:*` entry with `prefix: uploads/` to a bucket that already has one with `prefix: uploads/images/` fails with an `InvalidArgument` error saying the configurations overlap and cannot share a common event type — because a key like `uploads/images/a.jpg` would match both. The rule holds regardless of destination type: it is not "one Lambda per bucket", it is "one matching configuration per event type per key". Two lawful ways around it: - **Make the filters disjoint.** `prefix: uploads/images/` for one destination and `prefix: uploads/docs/` for another coexist happily. - **Narrow the event types.** `s3:ObjectCreated:Put` and `s3:ObjectCreated:CompleteMultipartUpload` are distinct event types, so two configurations can overlap on key filter if they name different specific types instead of the `:*` wildcard. This is a legitimate technique but a subtle one — a later `*` change silently breaks it. ## When to stop fighting the filter The moment your requirement stops being "starts with X, ends with Y", stop configuring the S3 side and turn on EventBridge notifications for the bucket. S3 then publishes every event to the default event bus, and each EventBridge rule applies its own event pattern — which can match on any field in the event, including size and the event name, with prefix, suffix, numeric and anything-but operators. It also removes the overlap restriction entirely, because many rules may match the same event independently. The cost of that move is one more hop in the path and a second service to configure and reason about. For a single consumer with a genuinely simple key convention, the native filter is simpler and stays out of the way. ## A practical design note Because the filter can only see the key, the key layout *is* the routing table. Pipelines that put type information into the prefix — `uploads/images/`, `uploads/csv/`, `staging/`, `processed/` — get cheap, reliable routing for free. Pipelines that hide the type inside the object or in a tag end up invoking one broad Lambda that immediately discards most of its invocations, which costs money and hides the real fan-out from anyone reading the configuration.
- Producers upload files with both .jpg and .JPG extensions. How do you handle that?The suffix rule is case-sensitive, so one rule cannot cover both. Either register two configurations with disjoint suffixes pointing at the same function, or drop the suffix filter, trigger on the whole prefix, and reject non-images inside the handler after lowercasing the key. Best of all, fix it at the source: normalise extensions when objects are written.
- Why does S3 reject a second ObjectCreated configuration whose prefix overlaps an existing one?Because a single object write would then match two configurations for the same event type, and S3 does not define which fires. It returns an InvalidArgument error stating the configurations overlap and cannot share a common event type. The fix is disjoint key filters, distinct specific event types, or moving fan-out to EventBridge or SNS where multiple independent matches are the intended model.
saying these in an interview costs you the question
- Writes uploads/*.jpg as a prefix value
- Assumes filters support regular expressions
- Thinks the filter can match object tags or size
- Expects .JPG to match a .jpg suffix rule
- Adds overlapping configurations and blames a bug