AWS S3
pre-signed URLs
file upload
cloud storage
multiple files

Pre-signed url for multiple files?

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

An S3 pre-signed URL is generated for one request against one object key. That means there is no native "single pre-signed URL for multiple files" feature for a batch of unrelated S3 objects; the usual pattern is to generate one signed URL per file or change the workflow so clients fetch a manifest or archive instead.

What a Pre-Signed URL Actually Signs

A pre-signed URL is a signed version of a specific S3 request. The signature covers things such as:

  • bucket
  • object key
  • HTTP method
  • expiration
  • optional headers or query parameters

Because the request is object-specific, a single URL does not magically grant bulk access to many different keys.

That is why the answer to the question is usually:

  • one object download means one signed URL
  • multiple object downloads mean multiple signed URLs

Generating One URL Per File

The most common server-side solution is to accept a list of object keys and return a list of signed URLs.

python
1import boto3
2
3s3 = boto3.client("s3")
4
5
6def presign_many(bucket, keys, expires_in=900):
7    results = []
8    for key in keys:
9        url = s3.generate_presigned_url(
10            "get_object",
11            Params={"Bucket": bucket, "Key": key},
12            ExpiresIn=expires_in,
13        )
14        results.append({"key": key, "url": url})
15    return results
16
17
18files = ["reports/jan.csv", "reports/feb.csv"]
19print(presign_many("my-bucket", files))

This is usually the simplest and most correct answer for a frontend that needs to fetch several files.

Uploads Work the Same Way

For uploads, you also usually generate one signed request per object key.

python
1import boto3
2
3s3 = boto3.client("s3")
4
5
6def presign_upload(bucket, key, expires_in=900):
7    return s3.generate_presigned_url(
8        "put_object",
9        Params={"Bucket": bucket, "Key": key},
10        ExpiresIn=expires_in,
11    )
12
13
14print(presign_upload("my-bucket", "incoming/photo1.jpg"))

If the client needs to upload ten files, the usual pattern is still ten pre-signed requests.

When a Single URL-Like Workflow Is Desired

Sometimes the real requirement is not "multiple signed URLs are impossible" but "the client wants one thing to click." In that case, you usually change the delivery model instead of searching for a bulk-signing feature.

Common alternatives are:

  • create a zip file and sign one URL for the archive
  • return a signed manifest JSON that contains many per-file URLs
  • proxy the download through your own backend

For example, if a user wants to download twenty invoice PDFs at once, it may be better to build a zip file server-side and generate one signed URL to that archive than to make the client juggle twenty independent downloads.

Multipart Upload Is a Different Problem

Do not confuse "multiple files" with multipart upload. Multipart upload is about splitting one large object into many parts. It still represents one final S3 object, not many independent files.

That workflow involves several signed requests too, but they are all part of the same object upload session.

So if someone asks for a pre-signed URL for multiple files, the answer is usually not multipart upload.

Security and Expiration Strategy

When generating many URLs at once, think about:

  • short expiration windows
  • limiting keys to exactly what the caller should access
  • logging or auditing the server endpoint that generated the URLs
  • avoiding excessively broad object naming patterns on the client side

A batch of pre-signed URLs is still a batch of delegated access tokens. Treat them as sensitive.

A Useful API Shape

A practical backend API often looks like this:

  • client sends requested file IDs
  • backend maps IDs to allowed S3 object keys
  • backend returns one signed URL per approved object

That keeps authorization on the server and leaves the client with a simple list to use.

Common Pitfalls

A common mistake is looking for one S3 pre-signed URL that covers an arbitrary set of object keys. S3 pre-signing does not work that way.

Another issue is generating URLs directly from client-supplied bucket and key values without validating authorization first.

Developers also sometimes use long expiration periods for convenience. That weakens the whole point of temporary delegated access.

Finally, do not reach for multipart upload when the real problem is many separate files. Those are different workflows.

Summary

  • A pre-signed S3 URL signs one request for one object key.
  • For multiple files, the normal solution is one signed URL per file.
  • If the client wants one download action, consider a zip or signed manifest instead.
  • Multipart upload is for one large object, not many independent files.
  • Keep URL generation server-side so authorization remains under your control.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.