module Shrine::Plugins::UploadEndpoint

  1. lib/shrine/plugins/upload_endpoint.rb

The upload_endpoint plugin provides a Rack endpoint which accepts file uploads and forwards them to specified storage. On the client side it's recommended to use Uppy for asynchronous uploads.

plugin :upload_endpoint

The plugin adds a Shrine.upload_endpoint method which, given a storage identifier, returns a Rack application that accepts multipart POST requests, and uploads received files to the specified storage. You can run this Rack application inside your app:

# (Rack)
map "/images/upload" do
  run ImageUploader.upload_endpoint(:cache)

# OR

# config/routes.rb (Rails)
Rails.application.routes.draw do
  mount ImageUploader.upload_endpoint(:cache) => "/images/upload"

Asynchronous upload is typically meant to replace the caching phase in the default synchronous workflow, so we want the uploads to go to temporary (:cache) storage.

The above will create a POST /images/upload endpoint, which uploads the file received in the file param using ImageUploader, and returns a JSON representation of the uploaded file.

# POST /images/upload
  "id": "43kewit94.jpg",
  "storage": "cache",
  "metadata": {
    "size": 384393,
    "filename": "nature.jpg",
    "mime_type": "image/jpeg"

This JSON string can now be assigned to an attachment attribute instead of a raw file. In a form it can be written to a hidden attachment field, and then it can be assigned as the attachment.

Limiting filesize

It's good practice to limit the accepted filesize of uploaded files. You can do that with the :max_size option:

plugin :upload_endpoint, max_size: 20*1024*1024 # 20 MB

If the uploaded file is larger than the specified value, a 413 Payload Too Large response will be returned.


If you want the upload endpoint to verify the integrity of the uploaded file, you can include the Content-MD5 header in the request filled with the base64-encoded MD5 hash of the file that was calculated prior to the upload, and the endpoint will automatically use it to verify the uploaded data.

If the checksums don't match, a 460 Checksum Mismatch response is returned.


The upload context will not contain :record and :name values, as the upload happens independently of a database record. The endpoint will send the following upload context:

  • :action - holds the value :upload

  • :request - holds an instance of Rack::Request

You can update the upload context via :upload_context:

plugin :upload_endpoint, upload_context: -> (request) do
  { location: "my-location" }


You can also customize the upload itself via the :upload option:

plugin :upload_endpoint, upload: -> (io, context, request) do, context)


The response returned by the endpoint can be customized via the :rack_response option:

plugin :upload_endpoint, rack_response: -> (uploaded_file, request) do
  body = { data:, url: uploaded_file.url }.to_json
  [201, { "Content-Type" => "application/json" }, [body]]

Ad-hoc options

You can override any of the options above when creating the endpoint:

Shrine.upload_endpoint(:cache, max_size: 20*1024*1024)


Public Class

  1. configure
  2. load_dependencies

Public Class methods

configure (uploader, opts = {})
[show source]
# File lib/shrine/plugins/upload_endpoint.rb, line 122
def self.configure(uploader, opts = {})
  uploader.opts[:upload_endpoint_max_size] = opts.fetch(:max_size, uploader.opts[:upload_endpoint_max_size])
  uploader.opts[:upload_endpoint_upload_context] = opts.fetch(:upload_context, uploader.opts[:upload_endpoint_upload_context])
  uploader.opts[:upload_endpoint_upload] = opts.fetch(:upload, uploader.opts[:upload_endpoint_upload])
  uploader.opts[:upload_endpoint_rack_response] = opts.fetch(:rack_response, uploader.opts[:upload_endpoint_rack_response])
load_dependencies (uploader, opts = {})
[show source]
# File lib/shrine/plugins/upload_endpoint.rb, line 118
def self.load_dependencies(uploader, opts = {})
  uploader.plugin :rack_file