Simple demo of client-side uploads to S3
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.

S3 Photo Upload Demo

This is a simple demonstration of client-side image uploads to Amazon S3. It simulates the scenario where you have an S3 bucket to which you want your users to be able to upload photos directly, with a reasonable user experience beyond what's used to be possible with a simple form POST and redirect.

Disclaimer I don't claim this is pretty code - it's just a prototype/demo of a few things that are now possible!

The AWS credentials used to upload to S3 are never exposed to the client-side, but the image data never has to be sent to the server. The client-side code makes a request to /sign which takes the requested username and filename, and returns a set of form fields, including a base64-encoded policy and a signature. These values are then used on the client-side when uploading the file.

Live Demo

A demo is, at the time of this writing, available at


The server side is very straightforward (all in app.rb). /sign takes a username/filename combination (username is hardcoded to 'demo' atm) and returns some JSON:

  "url": "",
  "fields": {
    "AWSAccessKeyId": "AKIAI6C4JGWHT2C5PGLQ",
    "key": "demo/oversize.jpg",

These values tell the client-side where to post to, and what multipart-encoded fields to include in order to make a successful request to S3. I've used the official AWS gem to generate these values for simplicity, but it's not a hard problem to generate the policy and signature on your own if you'd like. The policy looks like gibberish but is just base64-encoded json, so I recommend having a closer look at it to fully understand what's being generated.


All of the client-side code is in (which generates public/s3-upload.js). It's written to be very procedural, keeping the interesting bits together. Unless you're writing something trivial, I wouldn't use this code as-is in a real application: it's just a demonstration.

S3 CORS Configuration

This requires CORS to be configured on your S3 bucket. For the demo, I used the following, including an entry that allows requests from both my local dev environment and the demo app server:

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="">


The easiest way to get this running locally:

cp dot-env.example .env
vim .env
# Start the server
foreman start -p 3000

Now navigate to http://localhost:3000/ and you should be set to play.

If you're changing the coffeescript, make sure you run rake assets:compile afterwards to generate the javascript. This will become annoying if you're making serious changes, in which case I'd recommend setting up Guard or using the official coffeescript compiler's -w flag to continuously recompile it.