Goal: Have some static content, served by Nginx, but it requires an authenticated user. User auth is through Rails.
Project structure: The actual static files are in /home/vagrant/docs/ The browser will use the URL /help/* to access this content
The Ruby bits:
RAILS_ENV=production rails server
is used to start a WEBrick on port 3000.
config/environments/production.rb
config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect'
config.middleware.insert(0, Rack::Sendfile, config.action_dispatch.x_sendfile_header)
config/routes.rb
get 'help/*file(.:extension)' => 'docs#show'
controllers/docs_controller.rb
class DocsController < ActionController::Base
before_filter :validate_user
def validate_user
true
end
def show
file_path = "/home/vagrant/docs/" + params[:file].to_s
file_path += "." + params[:extension].to_s if params[:extension]
send_file(file_path, disposition: 'inline', status: 200, x_sendfile: true)
end
end
Nginx config:
server {
listen 8888 default_server;
listen [::]:8888 default_server ipv6only=on;
root /home/vagrant/Projects/seabass/management/public;
index index.html index.htm;
# Make site accessible from http://localhost/
server_name localhost;
location ~ /docs/(.*) {
alias /home/vagrant/docs/$1;
internal;
}
location /help {
proxy_redirect off;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Sendfile-Type X-Accel-Redirect;
proxy_set_header X-Accel-Mapping /docs/=/home/vagrant/docs/;
proxy_pass http://rails;
}
Result from all this:
- Making a browser request to
http://server:8888/docs/index.html
- The browser receives an Nginx 404
This is as expected because the /docs/
location is marked internal
, so we are successfully preventing the users from directly accessing the content.
- Making a browser request to
http://server:8888/help/index.html
- succesfully hits Rails on port 3000 with the X-Accel headers.
- Rails
send_file()
is called with the full file system path. - I THINK this redirects back to Nginx and Nginx serves the file from
/home/vagrant/docs/index.html
. - The browser gets the contents of the file that is physically at
/home/vagrant/docs/index.html
.
That is working as expected. (yay!) However:
- Making a browser request to
http://server:8888/help/not_exist.html
- succesfully hits Rails on port 3000 with the X-Accel headers.
- Rails
send_file()
is called with the full file system path. - The browser gets HTTP 500 with the Rails default 500 error HTML page.
- Rails logs from logs/production.log:
I, [2016-03-02T16:18:07.923144 #6439] INFO -- : Started GET "/help/not_exist.html" for 127.0.0.1 at 2016-03-02 16:18:07 +0000
I, [2016-03-02T16:18:07.924338 #6439] INFO -- : Processing by DocsController#show as HTML
I, [2016-03-02T16:18:07.924542 #6439] INFO -- : Parameters: {"file"=>"not_exist", "extension"=>"html"}
I, [2016-03-02T16:18:07.926543 #6439] INFO -- : Sent file /home/vagrant/docs/not_exist.html (1.5ms)
I, [2016-03-02T16:18:07.926829 #6439] INFO -- : Completed 500 Internal Server Error in 2ms (ActiveRecord: 0.0ms)
F, [2016-03-02T16:18:07.927604 #6439] FATAL -- :
ActionController::MissingFile (Cannot read file /home/vagrant/docs/not_exist.html):
app/controllers/docs_controller.rb:15:in `show'
Maybe this is all OK and working as expected, but it is unclear from any docs I can find how send_file()
gets back in contact with Nginx to serve the file.
What I thought would happen is that send_file()
would just tell Nginx to serve the static content file, and Nginx would then find the file missing and give the standard 404 response.
However maybe something internal to send_file()
checks the file existance prior to doing that? Maybe Rails is actually piping the file back to the browser and not Nginx? I don't know how to tell...
I'd like to get a 404 not a 500, but adding a file system check to the rails controller seems like a lot of overhead.