---
title: Upload progress with NGINX
teaser: 'Use a Merb application to track the progress of file uploads going through
  NGINX.

  '
tags: merb,nginx,new bamboo,web
author: Damien Tanner
published_on: 2007-11-23
---

_This post was originally published on the New Bamboo blog, before [New Bamboo
joined thoughtbot in
London](https://thoughtbot.com/blog/new-bamboo-joins-thoughtbot-in-london)._

---

Last month Brice Figureau released a little NGINX module which tracks the
progress of uploads going through NGINX. Like Lighttpd's [`mod_uploadprogress`]
it's very nice to have the proxy take responsibility for reporting the progress.

![](https://images.thoughtbot.com/new-bamboo/blog/upload-progress-with-nginx/NiVdObFLSem8Jt06wuPe_nginxupload.png)

The NGINX upload progress module adds `track_uploads` and `report_uploads`
directives which do what they say on the tin. When the user submits a form to
the location supporting `track_uploads`, we also send an extra `X-Progress-ID`
parameter with a unique id for that upload. Requests made to the location
supporting `report_uploads` (typically `/uploads`) will return JSON with the
current progress for an upload. The `X-Progress-ID` header is added to the AJAX
requests to identify which upload we want to know the status of.

Having NGINX track the progress gets around an issue with using the Merb upload
progress method. NGINX (un)fortunately does not pass along the upload to its
destination until it has fully completed uploading. So when you upload a file,
your Merb application won't know about it until it has been upload to NGINX and
passed along. The only gripe with this whole process is the possibility of a
small delay as Merb handles the request once the upload is complete. If you're
using Rails however this can be an advantage, as it won't be tied up by the
upload as it happens in realtime.

To get this all going you'll first want to download the [NGINX upload progress
module] and recompile NGINX with the following configure option:
`--add-module=path/to/nginx_uploadprogress_module`.

Locate and open your NGINX configuration file and add in the `upload_progress`,
`track_uploads` and `report_uploads` directives.

If you're going to upload files of any considerable size you'll also want to the
[`max_body_side`] to something reasonable.

```nginx
http {
	upload_progress proxied 1m;

	server {
		listen 80;
		server_name _ *;
		root /usr/local/www;

		location / {
			proxy_pass http://127.0.0.1:4000;
			track_uploads proxied 30s;
			client_max_body_size 500m;
		}

		location ^~ /progress {
			report_uploads proxied;
		}
	}
}
```

NGINX is now ready to track uploads! Generate a Merb project and add a new
upload controller.

```ruby
class Uploader < Application
  def index
    render
  end

  def upload
    FileUtils.mv params[:file][:tempfile].path, _ROOT+"/files/#{params[:file][:filename]}";
  end
end
```

Then add the upload view.

```html
<div id="pandaloader">
  <div id="uploader">
    <form id="upload" enctype="multipart/form-data" action="/uploader/upload" method="post">
      <input name="file" type="file">
      <input type="submit" value="Upload">
    </form>
  </div>

  <div id="uploading">
    <div id="progress" class="bar">
      <div id="progressbar">& </div>
    </div>
  </div>
</div>
```

In the header of your application's layout you'll also need to include the
JavaScript and CSS.

```html
<%= css_include_tag 'upload' %>
<%= js_include_tag 'jquery' %>
<%= js_include_tag 'jquery.nginxUploadProgress.js' %>

<script type="text/javascript" charset="utf-8">
  $(function() {
    nginxUploadProgress();
  });
</script>
```

Aside from [jQuery](http://jquery.info), you'll notice the
[`jquery.nginxUploadProgress.js`] plugin which has been included. This is a
little jQuery library which will query NGINX for the upload's progress and
update the length of the progress bar accordingly.

Here is some styling for the progress bar.

```css
.bar {
  width: 300px;
}

#progress {
  background: #eee;
  border: 1px solid #222;
}
#progressbar {
  width: 0px;
  height: 24px;
  background: #333;
}
```

Put all these together and you'll have a great little progress bar for uploads!

[`jquery.nginxUploadProgress.js`]: https://gist.githubusercontent.com/newbamboo/312451/raw/8857343c3285d216a553c6b1c5bdec9110280b2d/jquery.nginxUploadProgress.js
[`max_body_side`]: https://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size
[`mod_uploadprogress`]: http://blog.lighttpd.net/articles/2006/08/01/mod_uploadprogress-is-back
[NGINX upload progress module]: https://www.nginx.com/resources/wiki/modules/upload_progress/
