On 2/17/08, Adam D. [email protected] wrote:
I am using nginx as the front end to a rails cluster. When rails
generates a page I write the page to disk, where nginx can look for it
later. I want to use something like this:
We’re doing the same thing.
if (-f $document_root/$uri)
This works:
if (-f $request_filename) {
break;
}
But I anticipate a few problems:
- the uri might include “…” or similar hackery
It’s up to you to ensure that the saved file corresponds to the file
name Nginx generates in $request_filename. Nginx will URL-decode the
path, which means that
http://example.com/buttons/a%2F..%2Fbutton.png
will resolve to
$document_root/buttons/a/…/button.png
which is expanded to
$document_root/buttons/button.png
…which is the file name that Rails’ cache_page method will use.
In other words it’s possible to generate URLs which may end up outside
your designated document root. The risk is not Nginx that could try to
serve stuff beyond the document root, but that the Rails app might
write its cache file in unexpected places.
I don’t know if cache_page validates the file name to ensure it’s
within $RAILS_ENV/public. It certainly ought to. There’s also the risk
that cache_page could overwrite other files within the public
directory, of course, such as stuff in public/images.
- the uri might include query parameters
If so you will need to create an Nginx variable to appends the query
string to the end of the $request_filename. But a better option is to
rely on Rails routes. Here’s a typical route we use for rendering
buttons:
map.connect ‘cache/button/:id’, :controller => “theme”, :action =>
‘button’,
:requirements => {:id => /.*/}
This will map a URL such as this:
http://example.com/cache/button/style=green;text=Click+me.png
to a controller action as well as a nicely readable file name within
our cache directory. In the controller we parse the file name and do
the rendering:
def button
options = Button.parse_options(params[:id])
button = Button.new(options)
…
data = button.render.to_blob
send_data(data, :type => button.content_type, :disposition =>
“inline”)
cache_page(data)
end
Alexander.