I have a Sinatra-based app that runs fine locally.
I moved it to a nginx-based server with Passenger and now all my links to files in my apps /public
are returning 404 errors. The primary app runs, is able to access the HAML templates in /view
, which render correctly. The files exist and permissions are correct; I can open and edit them so I know they're there.
In my HAML templates I'm referring to the files that I can't access like this:
%script{ :src => 'js/jquery.js' }
%link{ "rel" => "stylesheet", "href" => "styles/input.css" }
My config.ru
has gone through a lot mutations while I try to find the problem. Currently I have:
require 'sinatra'
require './peering_template.rb'
root_dir = File.dirname(__FILE__)
# disable :run
# set :root, root_dir
# set :views, File.join(File.dirname(__FILE__), 'views')
# set :environment, (ENV['RACK_ENV'] ? ENV['RACK_ENV'].to_sym : :development)
run Sinatra::Application
The app exists in /home/apps/peering_template
.
The web space is /home/webapps
.
There is a soft-link in /home/webapps
like this: peering_template -> /home/apps/peering_template/public/
.
/home/webapps/
`-- peering_template -> /home/apps/peering_template/public/
The pertinent part of nginx.conf for this config is:
server {
listen 3000;
server_name my_servers_name;
root /home/webapps;
passenger_enabled on;
passenger_base_uri /peering_template;
}
Obviously, my server's name is different.
The pertinent part from nginx' error.log is like this:
"/home/webapps/js/jquery.js" failed (2: No such file or directory), request: "GET /js/jquery.js HTTP/1.1"
As near as I can tell this fits the directions for an "nginx and passenger configuration using sub-URIs". What am I missing?
/home/apps/peering_template/
|-- config.ru
|-- lib
| |-- bgp-config.rb
| |-- ios-xr-config.rb
| |-- ipv4_ipv6_grammar.rb
| `-- ipv4_ipv6_grammar.treetop
|-- nginx.conf
|-- peering_template.rb
|-- public
| |-- js
| | |-- jquery-1.6.min.js
| | |-- jquery-ui-1.8.12.custom.zip
| | |-- jquery.js -> jquery-1.6.min.js
| | `-- scripts.js
| |-- peering_template_tool.htm
| `-- styles
| `-- input.css
|-- spreadsheets
| |-- Peering Template-AMS-IX.xlsx
| `-- Peering Template-IOS-XR-ASH1.xlsx
|-- tmp
| `-- always_restart.txt
`-- views
|-- index.haml
`-- output.haml
I'm not sure if it matters but this is on a CentOS release 5.3 (Final)
host, running nginx/1.0.0
and passenger (3.0.7)
.
In the original question I wrote:
I moved it to a nginx-based server with Passenger and now all my links to files in my apps /public are returning 404 errors. The primary app runs, is able to access the HAML templates in /view, which render correctly. The files exist and permissions are correct; I can open and edit them so I know they're there.
That was my clue. On my fourth pass or so through the Passenger docs I ran into a section that talked about errors with /public
assets:
The second and highly recommended way is to always use Rails helper methods to
output tags for static assets. These helper methods automatically take care of
prepending the base URI that you’ve deployed the application to. For images
there is image_tag, for JavaScript there is javascript_include_tag and for CSS
there is stylesheet_link_tag. In the above example you would simply remove the
HTML tag and replace it with inline Ruby like this:
So, that got me searching for similar helpers for Sinatra. I found in Sinatra's extensions page:
sinatra-url-for construct absolute paths and full URLs to actions in a Sinatra application
sinatra-static-assets implements image_tag, stylesheet_link_tag, javascript_script_tag and link_tag helpers. These helpers construct correct absolute paths for applications dispatched to sub URI.
And that got me searching Sinatra's docs because it sparked a memory, and I relearned Sinatra's built-in "url" method:
Generating URLs
For generating URLs you should use the url helper method, for instance, in
Haml:
%a{:href => url('/foo')} foo
It takes reverse proxies and Rack routers into account, if present.
This method is also aliased to to (see below for an example).
By using the static asset methods, or Sinatra's own url helper it fixed the problem.
The root in your nginx config should be the public (or some other) directory, not the root of the entire rails application:
root /home/webapps/public;
Now put all your static files inside that directory, and Passenger will be smart enough to resolve the config.ru
Rack up file from the parent directory automatically, but serve the files from the public directory if they exist through nginx.
For what it's worth, as well, you shouldn't need anything but the require of your application ruby file, and and the Sinatra init method in your rack file. Here's one I use in another app:
require 'application'
run Sinatra::Application
One other little note, best practice to stick a /
in front of any URLs that reference these static files to ensure they're reachable wherever the page URL ends up, eg. ...:src => '/js/jquery.js'...
Edit:
I think there's a fundamental problem with the way your app is setup on the server. In my mind, it should look something like this:
/app
whatever.rb
/public
...
The nginx config should point to app/public
as the root, and the public
directory should not be a symlink.
With all that in mind, perhaps the root should just be set directly to /home/apps/peering_template/public
?