Uploads and frontend integration

# File uploads, image optimization, and your own S3 bucket

Combine resumable browser uploads, image optimization, customer-owned Amazon S3 storage, and on-demand delivery without operating separate media services.

Published August 28, 2026

## Key takeaways

* Use an Assembly to receive an upload, validate it, create fixed outputs, and export the selected originals or derivatives to S3.
* Use Smart CDN to import an existing S3 object through a saved Template, transform it on a cache miss, and cache the served result.
* Keep AWS credentials in scoped Template Credentials and generate upload or Smart CDN authorization on a trusted server.

A request for “file uploads, image optimization, and our own S3 bucket” hides two different processing moments. Some outputs must be validated, approved, and stored before publication. Other sizes or formats are cheaper to create only when a browser asks for them. Transloadit supports both paths, but they have different storage, latency, cache, and security boundaries.

## In this guide

1. [Separate upload-time work from request-time work](#file-uploads-image-optimization-s3-section-1)
2. [Receive, validate, optimize, and export the upload](#file-uploads-image-optimization-s3-section-2)
3. [Transform an S3 original on demand through Smart CDN](#file-uploads-image-optimization-s3-section-3)
4. [Decide what to store permanently versus generate on demand](#file-uploads-image-optimization-s3-section-4)
5. [Protect the application, bucket, and transformation surface](#file-uploads-image-optimization-s3-section-5)
6. [Compare this with a native AWS pipeline fairly](#file-uploads-image-optimization-s3-section-6)

## What matters most

* Treat S3 as the durable system of record, not as proof that file bytes never pass through Transloadit’s processing and cache infrastructure.
* Preprocess outputs that must exist before publication; use bounded on-demand variants when derivative demand is unpredictable.
* Store originals, fixed derivatives, or both according to the application’s recovery and migration requirements.

## Separate upload-time work from request-time work

Begin with two paths rather than one vague “optimization pipeline.” In the upload-time path, a browser sends a file through Uppy and tus to a Transloadit Assembly. The saved Template validates the observed file, creates any outputs that must exist immediately, and exports selected files to S3. The application stores the Assembly ID and durable S3 object identity with its asset record.

In the request-time path, a browser requests a Smart CDN URL. The URL identifies a saved Template and an input path in S3. On a cache miss, Transloadit imports that object, applies the Template with the permitted Assembly Variables, and serves one result through `/file/serve`. The delivery layer caches the response; a warm request can reuse the derivative without running the transformation again.

### Upload-time path

Browser → Uppy/tus → Assembly → validation and fixed transformations → customer S3.

### Request-time path

Browser → Smart CDN URL → cache miss → S3 import and transformation → edge cache → browser.

### Ownership boundary

Customer S3 holds the application’s durable objects; temporary processing files and cached derivatives have separate retention contracts.

## Receive, validate, optimize, and export the upload

Use a saved upload Template to constrain what browser code may request. `/upload/handle` receives the file, `/file/filter` can reject files whose detected properties do not satisfy policy, and `/image/resize` creates a fixed derivative. `/s3/store` then exports exactly the Steps named by its `use` value. Point it at only `optimized` to store the derivative, only `accepted_images` to store the accepted original, or both to retain both objects.

Uppy is the browser upload layer, not the authorization boundary. Let the Transloadit plugin request short-lived signed Assembly parameters from the application server. Keep `allow_steps_override` disabled when the browser must not replace the saved Steps or select another destination. The sample deliberately sets `/s3/store` to `acl: "private"` because the Robot’s default is `public-read`. Replace the example `bucket_region` with the bucket’s AWS region; setting it avoids a `GetBucketLocation` permission and the additional lookup. Treat the upload as complete for the product only after the application has reconciled the Assembly result and durable S3 keys.

Export the accepted original and a fixed WebP derivative to S3

```
{
  "allow_steps_override": false,
  "steps": {
    ":original": { "robot": "/upload/handle" },
    "accepted_images": {
      "use": ":original",
      "robot": "/file/filter",
      "accepts": [["${file.mime}", "regex", "^(image/jpeg|image/png|image/gif|image/webp|image/avif)$"]],
      "error_on_decline": true
    },
    "optimized": {
      "use": "accepted_images",
      "robot": "/image/resize",
      "resize_strategy": "fit",
      "width": 1600,
      "height": 1600,
      "format": "webp"
    },
    "stored": {
      "use": ["accepted_images", "optimized"],
      "robot": "/s3/store",
      "credentials": "my_s3_credentials",
      "bucket_region": "us-east-1",
      "acl": "private",
      "path": "images/${file.id}/${file.url_name}"
    }
  }
}
```

## Transform an S3 original on demand through Smart CDN

A Smart CDN integration still starts with a saved Template. `/s3/import` resolves the input path using scoped Template Credentials, `/image/resize` reads the URL-supplied `${fields.w}` value and the request-negotiated `${browser.wanted_image_format}` value, and `/file/serve` selects the response. Transloadit normally derives the format from the request’s `Accept` quality weights. A Smart CDN edge may instead set a trusted, pre-normalized `x-tl-image-format` header, which resolves to the same `avif`, `webp`, or `jpg` value without re-parsing `Accept`. A URL such as `https://my-workspace.tlcdn.com/responsive-image/images/a8d3eeeb67479f11f8b091b04f6181ad/canoe.jpg?w=640` supplies `images/a8d3eeeb67479f11f8b091b04f6181ad/canoe.jpg` as the implicit `${fields.input}` — the path after the Template name, with no leading slash — and `640` as `${fields.w}`. That input matches the exact key written by the upload sample, so `/s3/import` reads the stored object directly. Keep the full `images/` prefix in the URL; do not add it again in the Template. Smart CDN query values arrive as strings, so the width allowlist compares against the string literals `"320"` and `"640"`; an unmatched width falls back to 1280.

Do not interpolate an unrestricted path, width, quality, or format merely because it can arrive as a field. Restrict the S3 credential to an intended prefix, have the trusted application look up the exported key in its asset record, and sign a URL containing that exact key. Replace the example `bucket_region` with the bucket’s AWS region; setting it avoids a `GetBucketLocation` permission and the additional lookup. Validate or map transformation values in the Template. Complex Dynamic Evaluation expressions invoke `/script/run` and incur its charges. The format expression maps the `jpg` fallback to `null`, so requests without a modern-format preference keep the original format instead of flattening transparency or animation through needless re-encoding. See the `${browser.wanted_image_format}` entry in the [Assembly Variables reference](/docs/topics/assembly-variables.md). Measure the first uncached transformation separately from subsequent cached delivery.

Import an S3 original using prefix-scoped credentials and a signed, server-validated key

```
{
  "steps": {
    "imported": {
      "robot": "/s3/import",
      "credentials": "my_s3_credentials",
      "bucket_region": "us-east-1",
      "path": "${fields.input}"
    },
    "optimized": {
      "use": "imported",
      "robot": "/image/resize",
      "resize_strategy": "fit",
      "width": "${fields.w === '320' ? 320 : fields.w === '640' ? 640 : 1280}",
      "format": "${browser.wanted_image_format === 'jpg' ? null : browser.wanted_image_format}"
    },
    "served": {
      "use": "optimized",
      "robot": "/file/serve",
      "cache_duration": 604800
    }
  }
}
```

## Decide what to store permanently versus generate on demand

Keeping an original in S3 provides a stable source for reprocessing, but it does not require storing every responsive rendition. Export canonical outputs that the product needs independently of a cache: an approved master, marketplace listing image, print asset, or immutable release derivative. Let Smart CDN create bounded presentation variants whose dimensions depend on the requesting device or layout.

Conversely, do not rely only on an on-demand path when the first request cannot tolerate processing latency, an editor must approve the exact pixels, or downstream systems require a durable object before publication. In that case, create and export the derivative in the upload-time Assembly. The same application can use both approaches for different output classes without changing the durable owner of the source.

### Durable originals

Keep sources required for future transformations, recovery, audit, or migration.

### Fixed derivatives

Store outputs that must be reviewed, referenced by other systems, or available without a cold transform.

### On-demand derivatives

Cache safe presentation variants whose combinations are bounded but difficult to predict before a user requests them.

## Protect the application, bucket, and transformation surface

Create separate least-privilege Template Credentials when upload exports and Smart CDN imports need different AWS actions or prefixes. Keep raw AWS keys, the Transloadit Auth Secret, and unrestricted Instructions out of browser bundles. The application server should authorize the user, select the Template, and issue only the short-lived upload parameters or signed Smart CDN URL appropriate for that asset.

A successful upload or valid signature is not publication approval. Validate detected MIME type and size in the Assembly, associate callbacks idempotently with the expected tenant and Assembly, and expose a result only after the durable object and application record agree. For Smart CDN, design source versioning, URL expiry, cache lifetime, and deletion together so replacing an S3 key cannot leave an unintended derivative address active.

## Compare this with a native AWS pipeline fairly

A native design can upload through an S3 presigned URL, react to object-created events, process with Lambda or another compute service, store derivatives, and deliver them through a CDN. That can be a strong fit when the workload stays inside supported runtime limits and the team wants to operate authorization, retries, queues, codecs, concurrency, observability, and failure recovery itself.

Compare complete production paths rather than one successful resize. Test interrupted uploads, large sources, malformed images, orientation and color, duplicate events, partial exports, concurrency spikes, cold transforms, cache invalidation, regional latency, and deletion. Include engineering and operations time alongside upload, processing, storage, request, and egress charges. The meaningful choice is which operational responsibilities the team wants to own.

## Technical details worth knowing

* Uppy’s Transloadit plugin creates an Assembly and uploads files to its tus endpoint, while application code can request signed Assembly parameters from a trusted back end.
* An Assembly Template can connect `/upload/handle`, validation or transformation Steps, and `/s3/store`; the `use` relationships determine whether the original, derivatives, or both are exported.
* Template Credentials store AWS access separately from Assembly Instructions, and the S3 IAM policy should grant only the bucket paths and operations that each Template requires.
* A Smart CDN URL identifies a workspace, Template, input path, and optional URL fields. The input path is available to the Template as `${fields.input}`. On a cache miss, the Template runs and `/file/serve` supplies the response that the delivery layer caches.
* A Smart CDN Template can use `/s3/import` to read an object from customer-owned S3, `/image/resize` to transform it, and `/file/serve` to return the selected derivative.
* For Smart CDN requests, URL query parameters populate `${fields.*}`, while the path after the Template name becomes the implicit `${fields.input}` value. This differs from upload-time form fields and the Assembly `fields` key. The Template decides which values it reads, but each still needs validation, mapping, or authorization through a signed URL.
* Transloadit temporary result storage is not permanent application storage. Results are retained for at least 24 hours regardless of settings; current R2 storage does not support purging sooner. Production workflows should export every object that must persist.
* A cached Smart CDN derivative is separate from the durable original in S3. Source replacement, URL versioning, signature expiry, and cache lifetime must be designed together.

## A practical approach

1. 1\
   Map the upload, processing, storage, and delivery paths, including who owns every durable object and public URL.
2. 2\
   Create least-privilege Template Credentials and saved Templates for upload-time and on-demand work.
3. 3\
   Test fixed exports, cold Smart CDN misses, warm cache hits, invalid parameters, replaced sources, and unavailable origins.
4. 4\
   Record Assembly IDs and stable S3 object versions in the application, then monitor processing, export, cache, and delivery costs separately.

A four-stage media workflow

## When Transloadit is useful

Use Transloadit when one product needs Uppy and tus uploads, asynchronous image workflows, exports to its own S3 bucket, and optional URL-driven variants through Smart CDN. Use only the parts the application needs: an Assembly can preprocess and export fixed assets, while a Smart CDN Template can import an original from S3 and create a bounded derivative on demand.

## Architecture boundary

Your application owns user authorization, asset records, publication policy, and the durable copies in S3. Transloadit receives or imports files, temporarily holds data while processing, executes the saved workflow, exports selected results, and may cache Smart CDN derivatives. Customer-owned storage therefore does not mean that bytes remain exclusively inside the customer’s AWS account.

## Frequently asked questions

### Does using my own S3 bucket keep every byte inside my AWS account?

No. S3 can remain the durable system of record, but uploads, imported originals, temporary results, and Smart CDN derivatives pass through Transloadit infrastructure according to the configured workflow. Temporary results are retained for at least 24 hours regardless of settings, and current R2 storage does not support purging them sooner. Assembly Status JSON retention is configured separately, with options from No Save through 90 days; 90 days is the default. Retaining the Status JSON for 90 days does not make the temporary result files durable storage. Smart CDN caches served results separately.

### Should I store the original image, optimized derivatives, or both?

Export the original when it is needed for reprocessing, audit, or provider migration. Export fixed derivatives when they must exist before publication or be reviewed. You can export both by giving `/s3/store` both Steps as its `use` input.

### Is Smart CDN a separate processing system from Assemblies?

No. An upload-time Assembly and a Smart CDN request use the same Template-and-Robot execution model, even though each path defines its own Template. The upload path runs when bytes arrive and can export durable results. The Smart CDN path runs its Template on a cache miss and serves one selected result through `/file/serve`.

### When should I preprocess instead of transforming on demand?

Preprocess assets that need approval, deterministic availability, several durable outputs, or predictable first-view latency. Transform on demand when requested sizes are difficult to predict and a bounded set of URL variables can express the safe variants. Many applications preprocess a canonical image and create presentation sizes on demand.

### How should Transloadit receive access to a private S3 bucket?

Store the AWS credentials as least-privilege Template Credentials, reference their name from a saved Template, and prevent browsers from supplying arbitrary Assembly Instructions or storage destinations. Sign upload parameters and protected Smart CDN URLs on a trusted server.

### Do I have to use Transloadit to use Uppy?

No. Uppy is open-source upload software and can use many back ends. The maintained Transloadit plugin is the direct integration when uploads should create an Assembly, use tus transfer, and report processing progress or results.

## Build the workflow

Move from the concept to a tested Assembly with Robot documentation and working demos.

* [Explore managed file uploads](/services/handling-uploads.md)
* [Explore Smart CDN](/services/content-delivery.md)
* [Connect an Amazon S3 bucket](/docs/faq/how-to-set-up-an-amazon-s3-bucket.md)
* [Protect storage credentials](/docs/topics/template-credentials.md)
* [Validate fields with Dynamic Evaluation](/docs/topics/dynamic-evaluation.md)
* [Reference Assembly Variables](/docs/topics/assembly-variables.md)
* [Understand temporary file retention](/docs/faq/temporary-purge-sooner.md)
* [Read the API documentation](/docs.md)
* [Explore working demos](/demos.md)
* [Create a free workspace](/c/signup/)

Uploads and frontend integration

## Continue with related guides

* [File Upload API guide: architecture, security, and provider selection](/guides/file-upload-api-guide.md)\
  Choose and implement a File Upload API by comparing architecture, resumability, direct-to-cloud transfer, security, storage boundaries, and providers.
* [How to serve responsive images from one URL](/guides/serve-responsive-images-from-one-url.md)\
  Derive every image size from one canonical URL, cache the results at the edge, and keep the encoding bill flat while traffic grows.
* [Best image APIs for production workloads](/guides/best-image-apis-2026.md)\
  Compare leading image APIs in 2026 by lifecycle role: upload, workflows, URL transformation, storage, delivery, and asset management.
* [HTML video in production: 10 practical checks](/guides/html-video-production-checklist.md)\
  Ten production checks for HTML video, from source selection and captions to poster images and preprocessing.
* [Three best practices for CSS banner images](/guides/css-banner-image-best-practices.md)\
  Three durable practices for sharp, responsive CSS banner images without unreadable text or accidental crops.
* [Five reliable ways to center an image in HTML](/guides/center-images-in-html.md)\
  Five predictable ways to center images in HTML and how to choose between layout and media preprocessing.
