Skip to content
Simple demo of client-side uploads to S3
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit 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.

You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.