Chaz Meyers (cpm) wrote,
Chaz Meyers

Configuring nginx to serve files big password-protected files for Rails 1.1.6

I spent about 3 days figuring out how to do this right, so I figured I might as well save someone else the headache.

I assume that:
  • your files are at /mass-storage/protected-files/
  • you're starting off with Ezra's nginx configuration
  • you want to serve files at http://localhost/files/your/path/here via a FilesController

First, you need something like this in your routes.rb:
map.connect 'files/*path_info', :controller => 'files', :action => 'index'

This will send the extra path stuff into a path_info variable without any additional parsing.

Next, you'll want to add this to your nginx.conf after your "location /" block:

location ^~ /protected-files/ {
  root /mass-storage

This registers /protect-files urls which will serve from /mass-storage/protected-files, but can only be accessed via the magic X-Accel-Redirect header that nginx understands.

A minimal FilesController might look like this:
class FilesController < ApplicationController
  def index
    # insert any permissions check here
    path = params[:path_info]
    response.headers["Content-Type"] = "application/octet-stream"
    response.headers["Content-Disposition"] = "attachment; filename=\"#{File.basename(path)}\""
    response.headers["X-Accel-Redirect"] = "/protected-files/#{path}"
    response.headers["Content-Length"] = File.size("/mass-storage/protected-files/#{path}")
    response.headers["Status"] = "200 OK"
    response.body = ""
    @performed_render = true

I'm setting @performed_render and response.body by hand because if you render :nothing in Rails 1.1.6, it clobbers your Content-Length. Nginx does not touch any of the headers I set. Later versions of Rails fixed this, so you should render :nothing in newer versions!

You might want your file path to be /mass-storage/files and your internal url to be /files to match what people are typing into their browser, but you must resist! When I tried this, nginx seemed to get confused and would just raise a 500 because it thought it was stuck in a redirection loop.

  • My tweets

    Mon, 08:57: RT @ thomdunn: New inspiration just dropped

  • My tweets

    Fri, 23:43: RT @ TheParkerJam: Have any fellow EST @ Fusion fans been following this year? Between new time zone and the gaps between games, I…

  • My tweets

    Fri, 10:53: RT @ TheParkerJam: I can now say I'm in an Among Us YouTube video that has features Kimi, Ellum, and Chilled. 🤯(OK, it's a…

  • Post a new comment


    default userpic

    Your reply will be screened

    Your IP address will be recorded 

    When you submit the form an invisible reCAPTCHA check will be performed.
    You must follow the Privacy Policy and Google Terms of use.
  • 1 comment