Object Storage and Signed Download URLs

Almost every application eventually stores files: avatars, invoices, exports, attachments, videos. Putting them in the database bloats backups, and keeping them on application servers breaks the moment you run two instances or redeploy. Object storage — Amazon S3, Google Cloud Storage, Azure Blob Storage, Cloudflare R2, and S3-compatible services — is the standard answer: virtually unlimited, durable, cheap storage addressed by key over HTTP.

The design question is how users get files in and out without either making the bucket public or streaming every byte through your servers. Signed URLs solve both: your backend authorizes the request and issues a short-lived URL that lets the client upload or download one specific object directly from storage.

TL;DR

Quick Example

A backend using boto3 that issues a constrained upload policy and a short-lived download URL:

The browser uploads straight to storage using the returned policy:

Core Concepts

Objects, Keys, and Buckets

Object storage holds objects — bytes plus metadata — under keys in buckets. There are no real directories; prefixes such as tenants/42/uploads/ simply organize keys. Objects are replaced, not edited in place. Amazon S3 provides strong read-after-write consistency, and other major services offer similar guarantees for single objects.

Signed URLs

A signed URL embeds the operation, bucket, key, expiry, and a cryptographic signature made with the backend's credentials. Storage verifies the signature and expiry and allows exactly that operation. Anyone holding the URL can use it until it expires, so expiry is your main control — individual URLs cannot be revoked without rotating the signing credentials.

Upload Patterns

Browser uploads require a CORS configuration on the bucket that allows your app's origin, the method used, and the needed headers.

The Upload Lifecycle

  1. The client asks your API to start an upload; the API authorizes, validates the declared type and size, generates a key, and records a pending file.
  2. The client uploads directly to storage with the signed URL or policy.
  3. A storage event (or a client confirmation) triggers processing: verify size, detect the real type from file contents, scan for malware, generate thumbnails.
  4. The file becomes clean and visible — or rejected and deleted.
  5. Lifecycle rules remove abandoned pending uploads and incomplete multipart uploads.

See File Upload.

Serving Downloads Safely

Operations and Cost

Best Practices

Never Trust Client-Supplied Names or Types

Generate keys on the server, keep the original filename only as sanitized metadata, and determine the real type by inspecting content after upload.

Isolate Tenants

Prefix keys by tenant, check tenant ownership on every request, and consider separate buckets or keys for customers with strict isolation needs. See Multi-Tenant Application Design.

Keep Signing Credentials Narrow

Sign with a role whose permissions cover only the bucket and prefix it needs, so a leaked URL or credential cannot reach other data.

Clean Up Automatically

Lifecycle rules for pending uploads, incomplete multipart uploads, temporary exports, and old versions keep storage costs and exposure down.

Common Mistakes

Public Buckets for Convenience

Long-Lived Signed URLs

Rendering Uploaded Files Inline on the Main Domain

FAQ

What is a presigned URL?

A URL containing a signature and expiry, created with your backend's credentials, that grants temporary permission to perform one operation — such as uploading or downloading a specific object — without making the bucket public or sharing credentials.

How long should a signed URL last?

As short as practical: seconds to a few minutes for downloads of private files, and minutes for uploads. Longer expiries increase the risk if a URL is shared or leaked, because it cannot be individually revoked.

Should uploads go through my server?

Usually not. Direct uploads to object storage with signed URLs or POST policies avoid tying up servers and bandwidth. Route uploads through your server only when you must transform or inspect content during transfer.

How do I restrict upload size and type?

Use presigned POST policies (or equivalent) with content-length ranges and content-type conditions, validate declared values in your API, and verify the actual size and type after upload before making the file available.

Can signed URLs be revoked?

Not individually. They stop working when they expire or when the credentials that signed them are rotated or lose permission. Short expiries and authorization at the moment of issuing are the practical controls.

Related Topics

References