{"id":46109787,"url":"https://github.com/dhassanali/laravel-s3-browser-based-uploads","last_synced_at":"2026-03-01T22:04:56.340Z","repository":{"id":62514976,"uuid":"198684508","full_name":"dhassanali/laravel-s3-browser-based-uploads","owner":"dhassanali","description":"Upload files to AWS S3 Directly from Browser","archived":false,"fork":false,"pushed_at":"2025-11-20T14:02:48.000Z","size":35,"stargazers_count":4,"open_issues_count":0,"forks_count":2,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-11-20T16:07:28.189Z","etag":null,"topics":["aws-s3","directly","laravel","laravel-storage","uploads"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dhassanali.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-07-24T17:52:43.000Z","updated_at":"2025-11-20T14:02:52.000Z","dependencies_parsed_at":"2022-11-02T13:03:29.783Z","dependency_job_id":null,"html_url":"https://github.com/dhassanali/laravel-s3-browser-based-uploads","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/dhassanali/laravel-s3-browser-based-uploads","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dhassanali%2Flaravel-s3-browser-based-uploads","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dhassanali%2Flaravel-s3-browser-based-uploads/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dhassanali%2Flaravel-s3-browser-based-uploads/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dhassanali%2Flaravel-s3-browser-based-uploads/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dhassanali","download_url":"https://codeload.github.com/dhassanali/laravel-s3-browser-based-uploads/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dhassanali%2Flaravel-s3-browser-based-uploads/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29986242,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-01T21:06:37.093Z","status":"ssl_error","status_checked_at":"2026-03-01T21:05:45.052Z","response_time":124,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aws-s3","directly","laravel","laravel-storage","uploads"],"created_at":"2026-03-01T22:04:54.403Z","updated_at":"2026-03-01T22:04:56.329Z","avatar_url":"https://github.com/dhassanali.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Laravel S3 Browser Based Uploads\n\n[![Latest Version on Packagist](https://img.shields.io/packagist/v/hassan/laravel-s3-browser-based-uploads.svg?style=flat-square)](https://packagist.org/packages/hassan/laravel-s3-browser-based-uploads)\n[![Build Status](https://badgen.net/travis/dhassanali/laravel-s3-browser-based-uploads/master)](https://travis-ci.org/dhassanali/laravel-s3-browser-based-uploads)\n[![Total Downloads](https://poser.pugx.org/hassan/laravel-s3-browser-based-uploads/d/total.svg)](https://packagist.org/packages/hassan/laravel-s3-browser-based-uploads)\n[![License](https://badgen.net/packagist/license/hassan/laravel-s3-browser-based-uploads)](https://packagist.org/packages/hassan/laravel-s3-browser-based-uploads)\n\nUpload files to AWS S3 directly from the browser using presigned POST requests, reducing server load and bandwidth usage.\n\n## Requirements\n\n- PHP 8.1 or higher\n- Laravel 9.x, 10.x, or 11.x\n- AWS S3 bucket with appropriate permissions\n\n## Installation\n\n### 1. Install the package via composer\n\n```bash\ncomposer require hassan/laravel-s3-browser-based-uploads\n```\n\nFor Laravel 9+, you may need to install Flysystem dependencies:\n\n```bash\ncomposer require league/flysystem-aws-s3-v3 \"^3.0\" --with-all-dependencies\n```\n\n### 2. Publish the config file\n\n```bash\nphp artisan vendor:publish --provider=\"Hassan\\S3BrowserBasedUploads\\ServiceProvider\" --tag=config\n```\n\n### 3. Configure your AWS credentials\n\nAdd your AWS settings to `.env`:\n\n```bash\nAWS_ACCESS_KEY_ID=your-access-key-id\nAWS_SECRET_ACCESS_KEY=your-secret-access-key\nAWS_DEFAULT_REGION=us-east-1\nAWS_BUCKET=your-bucket-name\n```\n\n### 4. Configure S3 CORS (Required!)\n\nFor browser uploads to work, you **must** configure CORS on your S3 bucket. Add this CORS configuration in your AWS S3 Console:\n\n```json\n[\n    {\n        \"AllowedHeaders\": [\"*\"],\n        \"AllowedMethods\": [\"POST\"],\n        \"AllowedOrigins\": [\"https://yourdomain.com\"],\n        \"ExposeHeaders\": [\"ETag\"],\n        \"MaxAgeSeconds\": 3000\n    }\n]\n```\n\n**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).\n\n## Usage\n\n### Basic Usage\n\n```php\nuse Hassan\\S3BrowserBasedUploads\\Facades\\S3BrowserBasedUploads;\n\n// Get the S3 endpoint URL\n$endpointUrl = S3BrowserBasedUploads::getEndpointUrl();\n\n// Get the presigned POST fields\n$fields = S3BrowserBasedUploads::getFields();\n\n// Use a different connection\n$fields = S3BrowserBasedUploads::connection('secure_images')-\u003egetFields();\n```\n\n## Example\n\n``` javascript\nconst formData = new FormData();\n\n@foreach(S3BrowserBasedUploads::getFields() as $key =\u003e $value)\n    formData.append('{{ $key }}', '{{ $value }}');\n@endforeach\n\nformData.append('Content-Type', file.type);\nformData.append('file', file, file.name);\n\nconst request = new XMLHttpRequest();\nrequest.open('POST', \"{{ S3BrowserBasedUploads::getEndpointUrl() }}\");\nrequest.send(formData);\n```\nCheck out [the demo with Filepond](demo.blade.php)\n\n\n### Using Credentials Routes\n\nYou can optionally register a route that returns the credentials as JSON:\n\n```php\n// In your RouteServiceProvider or routes/web.php\nuse Hassan\\S3BrowserBasedUploads\\S3BrowserBasedUploads;\n\npublic function boot()\n{\n    // Registers GET route: /s3_browser_based_uploads/credentials\n    S3BrowserBasedUploads::routes();\n\n    // With custom options (e.g., authentication middleware)\n    S3BrowserBasedUploads::routes([\n        'middleware' =\u003e ['auth', 'throttle:60,1'],\n        'prefix' =\u003e 'api/uploads',\n    ]);\n}\n```\n\nThis creates an endpoint that returns:\n```json\n{\n    \"url\": \"https://your-bucket.s3.amazonaws.com\",\n    \"fields\": {\n        \"key\": \"tmp/images/${filename}\",\n        \"policy\": \"eyJ...\",\n        \"x-amz-algorithm\": \"AWS4-HMAC-SHA256\",\n        \"x-amz-credential\": \"...\",\n        \"x-amz-date\": \"...\",\n        \"x-amz-signature\": \"...\"\n    }\n}\n```\n\n## Security Considerations\n\n### ⚠️ Important Security Warnings\n\n1. **Filename Sanitization**: Using `${filename}` in your config can expose you to path traversal attacks. Consider:\n   ```php\n   // In your backend before generating credentials\n   'key' =\u003e 'uploads/' . Str::uuid() . '.' . $extension\n   ```\n\n2. **File Size Limits**: Always set `content-length-range` in your config to prevent abuse:\n   ```php\n   ['content-length-range', 1, 10485760] // 1 byte to 10MB\n   ```\n\n3. **Content-Type Validation**: Restrict file types using conditions:\n   ```php\n   ['starts-with', '$Content-Type', 'image/'] // Images only\n   ['eq', '$Content-Type', 'application/pdf'] // PDFs only\n   ```\n\n4. **Short Expiration Times**: Use short-lived URLs (1-15 minutes recommended):\n   ```php\n   'expiration_time' =\u003e '+5 minutes'\n   ```\n\n5. **Rate Limiting**: The credentials endpoint includes default rate limiting (60 requests/minute). Adjust as needed.\n\n6. **HTTPS Only**: Always use HTTPS in production to prevent credential interception.\n\n7. **Bucket Permissions**: Set appropriate S3 bucket policies and ACLs. Avoid public write access.\n\n### AWS IAM Permissions\n\nYour AWS IAM user needs these S3 permissions:\n\n```json\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Effect\": \"Allow\",\n            \"Action\": [\n                \"s3:PutObject\",\n                \"s3:PutObjectAcl\"\n            ],\n            \"Resource\": \"arn:aws:s3:::your-bucket-name/*\"\n        }\n    ]\n}\n```\n\n### Known Limitations\n\n- Does not work with AWS IAM Identity Center credentials (use standard IAM credentials)\n- Maximum expiration time is capped at 12 hours for security\n- Requires CORS configuration on S3 bucket\n\n### Security Disclosure\n\nIf you discover any security related issues, please email hello@hassan-ali.me instead of using the issue tracker.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdhassanali%2Flaravel-s3-browser-based-uploads","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdhassanali%2Flaravel-s3-browser-based-uploads","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdhassanali%2Flaravel-s3-browser-based-uploads/lists"}