skip to content

In Livewire, what changes when temporary uploads go directly to S3, and what must you configure or watch for in production?

level: seniorimportance: should knowfreq 22%

answer

  1. LIVEWIRE_TEMPORARY_FILE_UPLOAD_DISK=s3
  2. presigned PUT from the browser
  3. global rules and throttle skipped
  4. no multiple uploads on S3
  5. livewire:configure-s3-upload-cleanup

basics

~20 s

Setting the temporary disk to an S3 disk makes the browser upload straight to the bucket with a presigned URL, bypassing the app server; you then need a bucket lifecycle rule for cleanup, component validation for every file, and single-file inputs.

solid answer

~40 s

With `LIVEWIRE_TEMPORARY_FILE_UPLOAD_DISK=s3` (any disk using the `s3` driver), the component answers the upload start with a **presigned PUT URL**, valid for `max_upload_time` minutes, and the browser sends the file straight to `livewire-tmp/` in the bucket, so large receipts do not tie up PHP workers. Consequences: Livewire's upload endpoint never runs, so the global `rules` and its throttle middleware do not apply, and component validation is the only check; a `multiple` input throws `S3DoesntSupportMultipleFileUploads`; Livewire's cleanup scan is skipped, so you run `php artisan livewire:configure-s3-upload-cleanup` to add a bucket lifecycle rule expiring the prefix after one day; and `temporaryUrl()` returns an S3 presigned URL. Note that `store()` still reads the temporary object and writes the permanent copy through PHP, so the bytes cross the app server once at save time.

go deeper

for a junior

Recall that an env variable can send Livewire's temporary uploads straight to S3.

for a middle

Explain the presigned PUT flow and that the S3 path needs its own cleanup rule.

for a senior

Close the gaps it opens: skipped global rules and throttling, no multiple inputs, CORS, and the save-time copy through PHP.

for a principal

Decide when direct-to-cloud uploads justify their extra configuration surface for the product's file sizes and traffic.

## The default path and its cost By default Livewire's temporary uploads go through your application: the browser posts the file to Livewire's upload endpoint, PHP validates it and writes it to `livewire-tmp` on the default disk. For an expense app where people photograph receipts on phones that is fine, but for large scans it means every upload occupies a PHP worker for the whole transfer and passes through the app server's network and disk. ## Switching to direct S3 uploads Set the temporary disk to a disk that uses the `s3` driver: ```ini LIVEWIRE_TEMPORARY_FILE_UPLOAD_DISK=s3 ``` The flow then changes: 1. The browser tells the component it wants to upload, with the file's name, size and type. 2. The component asks the S3 client for a **presigned `putObject` request** under `livewire-tmp/`, valid for `max_upload_time` minutes (5 by default), and sends the URL and headers back. 3. The browser uploads directly to S3. 4. The browser tells the component the upload finished; the property becomes a `TemporaryUploadedFile` pointing at the S3 object. ## What stops happening | Behaviour | Default disk | S3 temporary disk | |---|---|---| | Global `temporary_file_upload.rules` | run at the endpoint | never run | | Upload route middleware (throttle) | applies | the file never hits the route | | Livewire's 24-hour cleanup scan | on each finished upload | skipped | | `multiple` file inputs | supported | throws `S3DoesntSupportMultipleFileUploads` | | `temporaryUrl()` | signed app route | S3 presigned URL | Each row is a production decision: - **Validation.** Because the endpoint is bypassed, the component's validation is the only size and type check. Validate every S3-backed upload in the component, including size, since the presigned request does not enforce the size the browser declared. - **Cleanup.** Run `php artisan livewire:configure-s3-upload-cleanup` in the environment that owns the bucket. It adds a lifecycle rule that expires objects under the temporary prefix after **one day**. Without it, the prefix grows forever. - **Multiple receipts.** An expense form that lets users attach several receipts at once needs either one input per receipt or the default disk; a single `multiple` input fails on S3. - **CORS.** The browser now talks to the bucket directly, so the bucket's CORS policy must allow `PUT` from the app's origin. ## What still passes through PHP Direct upload removes the **upload** hop, not every hop. When the save action calls `$this->receipt->store('receipts', 's3')`, Livewire's `storeAs()` opens a read stream on the temporary object and writes it to the destination disk, so the bytes travel bucket to PHP to bucket once. For very large files that save request can be slow; keep the action lean or move the copy into a queued job that receives the temporary path. ## Checklist 1. Set the temporary disk to an S3 disk and confirm its credentials can presign `putObject`. 2. Configure bucket CORS for browser `PUT` requests. 3. Run the cleanup command once per bucket. 4. Validate every upload in the component. 5. Avoid `multiple` inputs on S3-backed fields. 6. Remember that previews come from presigned bucket URLs. ## When it is worth it Direct S3 uploads pay off when files are large (scanned multi-page receipts, video), when upload traffic is heavy enough that PHP workers are a bottleneck, or when the application servers have little local disk. For small phone photos on a modest app, the default path is simpler and keeps the global rules and throttling in force. Interviewers look for this judgement: the feature is a trade of simplicity and built-in safeguards for throughput, and the safeguards have to be rebuilt in the component and the bucket.

  • Why does raising temporary_file_upload.rules have no effect once uploads go directly to S3?
    Those rules are enforced by Livewire's upload controller, and with an S3 temporary disk the browser uploads straight to the bucket with a presigned URL. The controller never sees the file, so only the component's own validation applies.
  • What does livewire:configure-s3-upload-cleanup actually do?
    It adds a lifecycle rule to the bucket that expires objects under Livewire's temporary prefix after one day. Livewire's own cleanup scan only runs for non-S3 temporary disks, so without this rule temporary uploads accumulate indefinitely.

saying these in an interview costs you the question

  • Believes the global upload rules still run with an S3 temporary disk.
  • Expects Livewire to clean up the S3 temporary prefix on its own.
  • Uses a multiple file input with an S3 temporary disk.
  • Thinks store() copies S3 to S3 without PHP reading the file.
  • Forgets bucket CORS for direct browser uploads.