AWS S3 & CloudFront File Upload Limits Summary | Multipart & Presigned URLs
When building a file upload feature using AWS, S3, CloudFront, API Gateway, and Lambda each have different size limits. Understanding which layer becomes a bottleneck is extremely important. This article explains the limit values for each service, how to use multipart upload and presigned URLs for handling large files, and provides implementation examples in PHP.
Amazon S3 Upload Limits
Object upload size limits to S3 vary depending on the method used.
| Upload method | Maximum size | Remarks |
|---|---|---|
| PUT Object (single) | 5 GB | Upload limit per request |
| Multipart Upload | 5 TB | Maximum size per object |
| Per part | Minimum 5 MB to maximum 5 GB | Only the final part may be less than 5 MB |
| Number of Parts | Maximum 10,000 parts | Multipart Upload Limit |
AWS recommends multipart upload for files larger than 100 MB. From the perspective of efficient retransmission during network failures and improved throughput through parallel transfers, it should be actively used for large files.
Cases Requiring Multipart Upload
Multipart upload is a method where a single object is divided into multiple parts, each sent as an independent request, and then combined at the end. It is effective in the following scenarios.
- <strong>100 MB or larger files</strong>: This is the multipart upload threshold recommended by AWS.
- <strong>Unstable network environments</strong>: Even if an error occurs mid-transfer, only the failed parts need to be retransmitted.
- <strong>Maximize throughput</strong>: Upload multiple parts in parallel to improve transfer speed.
- <strong>Pause and resume uploads</strong>: Save the Upload ID to resume across sessions.
The multipart upload process consists of three major steps.
- <strong>Initiate</strong> (CreateMultipartUpload): Obtain the Upload ID
- <strong>Send parts</strong> (UploadPart): Upload each part and record the ETag
- <strong>Complete</strong> (CompleteMultipartUpload): Send a list of part numbers and ETags to finalize the object
If you cancel midway without calling <code>AbortMultipartUpload</code>, incomplete parts will continue to be charged as storage fees. It is strongly recommended to set up a lifecycle policy on your S3 bucket to "delete incomplete multipart uploads after N days."
Presigned URL expiration and size
Using a signed URL (Presigned URL) allows clients without S3 access permissions (such as browsers or mobile apps) to upload files directly to S3. Since the server is not involved as an intermediary, large files can be handled efficiently.
| Item | Restrictions & Specifications |
|---|---|
| Maximum expiration (issued by IAM user) | 7 days (604,800 seconds) |
| Maximum expiration (issued by IAM role) | Within the role's session time (maximum 12 hours) |
| Maximum uploadable file size | Maximum 5 GB for single PUT |
| Support for multipart | Issue individual Presigned URLs for each part |
A Presigned URL is generated on the backend and returned to the client, which then sends a PUT request directly to that URL. Since the URL contains a signature, it succeeds only when uploading the correct file within the expiration period.
CloudFront body size limit
When forwarding requests through CloudFront to an origin (such as S3 or EC2), there are size limits on the request body.
| Settings | Default Value | Maximum value |
|---|---|---|
| Request Body Size (Default) | 1 MB | Can be changed in distribution settings |
| When Using Lambda@Edge | 1 MB | 1 MB (unchangeable) |
| In the case of CloudFront Functions | — | Request body is not accessible |
In CloudFront distribution settings, you can enable "Allow requests with body" and raise the limit, but note that Lambda@Edge has a non-changeable 1 MB limit. For uploading large files, it is recommended to adopt an architecture that bypasses CloudFront and uploads directly from the client to S3 using Presigned URLs.
API Gateway Payload Limit
API Gateway (both REST API and HTTP API) has a payload size limit.
| API Type | Maximum payload size | Remarks |
|---|---|---|
| REST API | 10 MB | The same applies when binary support is enabled |
| HTTP API | 10 MB | — |
| WebSocket API | 128 KB (message) / 32 KB (frame) | — |
API Gateway's 10 MB limit is classified as an AWS service quota and cannot be raised. Uploading files exceeding 10 MB via API Gateway is considered a design flaw; consider migrating to the Presigned URL pattern.
File Limits via Lambda
When receiving files in a Lambda function, there is a size limit on the event payload passed from API Gateway. Additionally, Lambda itself has the following constraints.
| Restriction Items | Value |
|---|---|
| Synchronous call (Request) payload | 6 MB |
| Synchronous call (Response) payload | 6 MB |
| Asynchronous Call Payload | 256 KB |
| Storage capacity of <code>/tmp</code> directory | Default 512 MB (maximum 10 GB) |
| Maximum execution time | 15 minutes |
When processing files in Lambda, temporary files are written to <code>/tmp</code>. The default is 512 MB, but ephemeral storage can be extended up to 10 GB through Lambda configuration (additional charges apply).
Code example for S3 multipart upload in PHP
This is an implementation example of multipart upload using AWS SDK for PHP (v3). Using the SDK's <code>MultipartUploader</code> class automatically handles chunk splitting, retries, and completion processing.
use Aws\S3\S3Client;
use Aws\S3\MultipartUploader;
use Aws\Exception\MultipartUploadException;
$s3 = new S3Client([
'region' => 'ap-northeast-1',
'version' => 'latest',
]);
$uploader = new MultipartUploader($s3, '/path/to/large-file.mp4', [
'bucket' => 'your-bucket-name',
'key' => 'uploads/' . basename('/path/to/large-file.mp4'),
'before_initiate' => function (\Aws\Command $command) {
// アップロード開始前のフック
},
'before_upload' => function (\Aws\Command $command) {
// 各パートアップロード前のフック(プログレス表示など)
},
'concurrency' => 5, // 並列アップロード数
'part_size' => 10 * 1024 * 1024, // 1パート = 10 MiB
]);
try {
$result = $uploader->upload();
echo 'アップロード完了: ' . $result['ObjectURL'];
} catch (MultipartUploadException $e) {
// 失敗したパートから再開する例
$uploader = new MultipartUploader($s3, '/path/to/large-file.mp4', [
'state' => $e->getState(),
]);
$result = $uploader->upload();
}
Here is how to issue a Presigned URL and allow clients to upload directly to S3.
use Aws\S3\S3Client;
$s3 = new S3Client([
'region' => 'ap-northeast-1',
'version' => 'latest',
]);
// 署名付きURLを生成(有効期限: 15分)
$cmd = $s3->getCommand('PutObject', [
'Bucket' => 'your-bucket-name',
'Key' => 'uploads/' . uniqid('', true) . '.jpg',
'ContentType' => 'image/jpeg',
]);
$request = $s3->createPresignedRequest($cmd, '+15 minutes');
$presignedUrl = (string) $request->getUri();
// このURLをクライアントに返す
// クライアントはこのURLに PUT リクエストでファイルを直接送信する
echo json_encode(['upload_url' => $presignedUrl]);
Architecture selection guidelines
- <strong>Up to 10 MB</strong>: Can be implemented simply with API Gateway → Lambda → S3
- <strong>10 MB–100 MB</strong>: Direct upload from client to S3 using Presigned URLs is recommended
- <strong>100 MB–5 GB</strong>: Achieve stable transfers with Presigned URL + multipart upload
- <strong>Over 5 GB</strong>: Multipart upload is mandatory (PUT alone is not sufficient)
Test Procedure Using DevLab Threshold Files
To verify <code>S3</code> upload settings in practice, you need test files of precise sizes. Using DevLab's boundary value test files allows you to efficiently verify behavior around specific sizes.
- Download a file of the desired size from the <a href="/ja/files/threshold/">boundary value test files list</a> on DevLab.
- When validating the multipart switching threshold, you can also use the large-size version of <a href="/ja/files/images/png/">PNG test images</a>.
- Upload the downloaded file using the <code>aws s3 cp</code> command or the SDK's <code>PutObject</code> / <code>MultipartUploader</code>, and verify success or failure with files around the size limit.
Test files for this article
- <a href="/ja/files/threshold/" class="text-primary-600 dark:text-primary-400 hover:underline">Boundary Value Test File List</a> — For validating S3, API Gateway, and Lambda limits
- <a href="/ja/files/threshold/10mb/" class="text-primary-600 dark:text-primary-400 hover:underline">10MB Boundary Value Test Set</a> — Check just before and after the 10 MB API Gateway limit
- <a href="/ja/files/threshold/25mb/" class="text-primary-600 dark:text-primary-400 hover:underline">25MB Threshold Test Set</a> — For verifying behavior around the multipart recommended line
- <a href="/ja/files/images/png/" class="text-primary-600 dark:text-primary-400 hover:underline">PNG Test Images</a> — Verify image upload pipeline functionality
Related articles
- <a href="/ja/blog/how-to-test-upload-limit/" class="text-primary-600 dark:text-primary-400 hover:underline">How to Properly Test File Upload Size Limits</a>
- <a href="/ja/blog/php-file-upload/" class="text-primary-600 dark:text-primary-400 hover:underline">How to implement file upload in PHP | Complete guide to validation, storage, and security</a>
- <a href="/ja/blog/multipart-form-data-overhead/" class="text-primary-600 dark:text-primary-400 hover:underline">Calculate multipart/form-data overhead accurately</a>
- <a href="/ja/blog/mb-vs-mib-file-size/" class="text-primary-600 dark:text-primary-400 hover:underline">MB and MiB Are Different! Pitfalls of File Size Units</a>