rack/rack — pull request #417 by thomasklemm (2012-08-17 12:14:30+00:00)
Title: Allow Rack::File and Rack::Static to serve custom HTTP Headers

Hey everyone,

this PR makes Rack::File and Rack::Static able to serve custom HTTP Headers.

```ruby
New:
# Rack::Static
use Rack::Static, :root => 'public', :headers => {'Cache-Control' => 'public, max-age=31536000', 'Access-Control-Allow-Origin' => '*'}

# Plain Rack::File
@file_server = Rack::File.new(root, headers)

Old:
use Rack::Static, :root => 'public', :cache_control => 'public, max-age=31536000'
@file_server = Rack::File.new(root, cache_control)
```

The API changes all respect backward compatibility, all existing and new tests pass. It's implemented in a way that allows the all legacy implementations to continue but give priority to the settings provided in `:headers => {}`.

What's the reason behind this?
When deploying a Rails app the to date usual way to handle the serving of static files is by having a nginx instance or similar running in front of your Rails app. This nginx instance will set custom HTTP Headers for files it serves based on rules you define. The served files including the custom HTTP Headers can be cached by a CDN like Amazon's Cloudfront and then served to the website visitor.

When deploying to Heroku's Cedar stack there is no nginx etc. instance in front that could help with setting custom HTTP headers for files. Heroku currently enables Rails to serve static files through the ActionDispatch::Static middleware, which is enables by a plugin injection of theirs. ActionDispatch::Static relies on Rack::File for serving the precompiled assets. The served files can once again be cached by Amazon's Cloudfront, they need to carry all the headers you want them to carry however when sending them from your app, and I think there is no way to add custom headers via Cloudfront (even if there were, this wouldn't be the place to set them, would it?).

To have a file served by any higher level static files webserver with custom headers the basic Rack::File implementation should be able to add custom HTTP headers wider than 'Cache-Control' a developer wants the served files to carry. Rack::Static as well as ActionDispatch::Static both rely on this implementation.

Is it nescessary to serve files with custom headers at all?
Yes. There's an issue with web fonts / icon fonts and firefox that require icon fonts to carry a header of `'Access-Control-Allow-Origin' = '*'` or set to your domain name. Currently this is a huge pain in the ass especially for Rails developers deploying their app to Heroku and shipping their assets via a CDN.

Unless a functionality such as the one proposed in this PR is being implemented, Rack::File will not be able to serve web fonts that can be delivered by a CDN in a way that Firefox will render them. The required HTTP headers are simply missing.

Thanks for reading this rather long explanation. If you'd like me to point you to sources for anything stated above please comment, I'll try to dig out the relevant stuff for you from the many small clues you get when researching this topic.

The longer term goal here might be to have a great ruby static files server implementation that can be used to serve static files in production. It would be great to see this extended in the future to allow developers to add headers to files in a certain folder or ones that match certain Regexp expressions.

What do you think? Which way, at which place, would you set custom headers?

Thanks
Thomas