https://github.com/dhassanali/laravel-s3-browser-based-uploads
Upload files to AWS S3 Directly from Browser
https://github.com/dhassanali/laravel-s3-browser-based-uploads
aws-s3 directly laravel laravel-storage uploads
Last synced: 4 months ago
JSON representation
Upload files to AWS S3 Directly from Browser
- Host: GitHub
- URL: https://github.com/dhassanali/laravel-s3-browser-based-uploads
- Owner: dhassanali
- License: mit
- Created: 2019-07-24T17:52:43.000Z (almost 7 years ago)
- Default Branch: master
- Last Pushed: 2025-11-20T14:02:48.000Z (7 months ago)
- Last Synced: 2025-11-20T16:07:28.189Z (7 months ago)
- Topics: aws-s3, directly, laravel, laravel-storage, uploads
- Language: PHP
- Homepage:
- Size: 34.2 KB
- Stars: 4
- Watchers: 0
- Forks: 2
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
README
# Laravel S3 Browser Based Uploads
[](https://packagist.org/packages/hassan/laravel-s3-browser-based-uploads)
[](https://travis-ci.org/dhassanali/laravel-s3-browser-based-uploads)
[](https://packagist.org/packages/hassan/laravel-s3-browser-based-uploads)
[](https://packagist.org/packages/hassan/laravel-s3-browser-based-uploads)
Upload files to AWS S3 directly from the browser using presigned POST requests, reducing server load and bandwidth usage.
## Requirements
- PHP 8.1 or higher
- Laravel 9.x, 10.x, or 11.x
- AWS S3 bucket with appropriate permissions
## Installation
### 1. Install the package via composer
```bash
composer require hassan/laravel-s3-browser-based-uploads
```
For Laravel 9+, you may need to install Flysystem dependencies:
```bash
composer require league/flysystem-aws-s3-v3 "^3.0" --with-all-dependencies
```
### 2. Publish the config file
```bash
php artisan vendor:publish --provider="Hassan\S3BrowserBasedUploads\ServiceProvider" --tag=config
```
### 3. Configure your AWS credentials
Add your AWS settings to `.env`:
```bash
AWS_ACCESS_KEY_ID=your-access-key-id
AWS_SECRET_ACCESS_KEY=your-secret-access-key
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=your-bucket-name
```
### 4. Configure S3 CORS (Required!)
For browser uploads to work, you **must** configure CORS on your S3 bucket. Add this CORS configuration in your AWS S3 Console:
```json
[
{
"AllowedHeaders": ["*"],
"AllowedMethods": ["POST"],
"AllowedOrigins": ["https://yourdomain.com"],
"ExposeHeaders": ["ETag"],
"MaxAgeSeconds": 3000
}
]
```
**Important**: Replace `https://yourdomain.com` with your actual domain(s). For local development, you may add `http://localhost:8000` or use `["*"]` (not recommended for production).
## Usage
### Basic Usage
```php
use Hassan\S3BrowserBasedUploads\Facades\S3BrowserBasedUploads;
// Get the S3 endpoint URL
$endpointUrl = S3BrowserBasedUploads::getEndpointUrl();
// Get the presigned POST fields
$fields = S3BrowserBasedUploads::getFields();
// Use a different connection
$fields = S3BrowserBasedUploads::connection('secure_images')->getFields();
```
## Example
``` javascript
const formData = new FormData();
@foreach(S3BrowserBasedUploads::getFields() as $key => $value)
formData.append('{{ $key }}', '{{ $value }}');
@endforeach
formData.append('Content-Type', file.type);
formData.append('file', file, file.name);
const request = new XMLHttpRequest();
request.open('POST', "{{ S3BrowserBasedUploads::getEndpointUrl() }}");
request.send(formData);
```
Check out [the demo with Filepond](demo.blade.php)
### Using Credentials Routes
You can optionally register a route that returns the credentials as JSON:
```php
// In your RouteServiceProvider or routes/web.php
use Hassan\S3BrowserBasedUploads\S3BrowserBasedUploads;
public function boot()
{
// Registers GET route: /s3_browser_based_uploads/credentials
S3BrowserBasedUploads::routes();
// With custom options (e.g., authentication middleware)
S3BrowserBasedUploads::routes([
'middleware' => ['auth', 'throttle:60,1'],
'prefix' => 'api/uploads',
]);
}
```
This creates an endpoint that returns:
```json
{
"url": "https://your-bucket.s3.amazonaws.com",
"fields": {
"key": "tmp/images/${filename}",
"policy": "eyJ...",
"x-amz-algorithm": "AWS4-HMAC-SHA256",
"x-amz-credential": "...",
"x-amz-date": "...",
"x-amz-signature": "..."
}
}
```
## Security Considerations
### ⚠️ Important Security Warnings
1. **Filename Sanitization**: Using `${filename}` in your config can expose you to path traversal attacks. Consider:
```php
// In your backend before generating credentials
'key' => 'uploads/' . Str::uuid() . '.' . $extension
```
2. **File Size Limits**: Always set `content-length-range` in your config to prevent abuse:
```php
['content-length-range', 1, 10485760] // 1 byte to 10MB
```
3. **Content-Type Validation**: Restrict file types using conditions:
```php
['starts-with', '$Content-Type', 'image/'] // Images only
['eq', '$Content-Type', 'application/pdf'] // PDFs only
```
4. **Short Expiration Times**: Use short-lived URLs (1-15 minutes recommended):
```php
'expiration_time' => '+5 minutes'
```
5. **Rate Limiting**: The credentials endpoint includes default rate limiting (60 requests/minute). Adjust as needed.
6. **HTTPS Only**: Always use HTTPS in production to prevent credential interception.
7. **Bucket Permissions**: Set appropriate S3 bucket policies and ACLs. Avoid public write access.
### AWS IAM Permissions
Your AWS IAM user needs these S3 permissions:
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:PutObjectAcl"
],
"Resource": "arn:aws:s3:::your-bucket-name/*"
}
]
}
```
### Known Limitations
- Does not work with AWS IAM Identity Center credentials (use standard IAM credentials)
- Maximum expiration time is capped at 12 hours for security
- Requires CORS configuration on S3 bucket
### Security Disclosure
If you discover any security related issues, please email hello@hassan-ali.me instead of using the issue tracker.