Rails 3 running in subdirectory, assets not found - ruby-on-rails

I'm serving two Rails sites using nginx. Rails1 does not use the asset pipeline but Rails2 does. Rails2 also uses a prefix to differentiate it from Rails1. For example:
http://myhost -> Rails1
http://myhost/abc -> Rails2
Both sites are running, however any reference to assets on the Rails2 site are not found.
Here's what my pseudo nginx.conf looks like:
http {
upstream rails1 {
server 127.0.0.1:3000;
}
upstream rails2 {
server 127.0.0.1:3030;
}
server {
location ~ ^/assets/ {
expires max;
add_header Cache-Control public;
access_log off;
}
location /abc {
proxy_pass http://rails2;
}
location / {
proxy_pass http://rails1;
}
}
}
Also, the routes.rb in my Rails 2 app:
Rails2App::Application.routes.draw do
scope '/abc' do
resources :projects
root :to => 'home#index'
end
end
Browsing to http://myhost/abc/ for the Rails2 app, brings up the page with no css, and the following error:
GET http://myhost/assets/application-asdasd.css 404 (Not Found)
I've tried using config.assets.prefix = '/abc' in the production.rb file but it didn't work. I've also tried different variations in the ngnix.conf file to no avail either.
Anyone know what I'm doing wrong, or missing?
UPDATE
I'm not quite sure why, but I was able to get this to (incorrectly) work using an #location instead of an upstream. But I had to move the assets folder from the Rails2 app to the Rails1 app. Not exactly ideal.
Changes to the server section:
location ~ ^/(assets)/ {
expires max;
add_header Cache-Control public;
access_log off;
}
location ~ ^/(abc)/ {
root /rails2/public;
try_files $uri/index.html $uri.html $uri #rails2;
error_page 404 /404.html;
error_page 422 /422.html;
error_page 500 502 503 504 /500.html;
error_page 403 /403.html;
}
location / {
root /rails1/public;
try_files $uri/index.html $uri.html $uri #rails1;
error_page 404 /404.html;
error_page 422 /422.html;
error_page 500 502 503 504 /500.html;
error_page 403 /403.html;
}
location #rails1 {
proxy_pass http://127.0.0.1:3000;
}
location #rails2 {
proxy_pass http://127.0.0.1:3030;
}

From a similar question: https://stackoverflow.com/a/3355589/417872
Have you tried this?
config.action_controller.relative_url_root = '/rails_app'
I would recommend googleing for how to correctly serve a rails app from a subdirectory. That's the real issue you're dealing with, and a quick search returned several pages of useful looking links.

I had a similar problem running Rails 4 on Nginx in production environment. The solution I found was to specify the root path for the asset location in nginx.conf:
location ^~ /assets/ {
root /home/rails/myapp/public;
gzip_static on;
expires max;
add_header Cache-Control public;
}
Hope this helps

Related

Nginx + Puma + Sidekiq web interface not showing css styles

I have an angularJS web app running in a Nginx server that sends request to a Rails API running in a Puma server. I have integrated Sidekiq 5.2.8 and everything works great but the Sidekiq web interface.
In my Nginx config file, I have a rule to pass request to the API. Please find the whole nginx.conf document:
events {
worker_connections 1024;
}
http {
upstream api.development {
# Path to Puma SOCK file, as defined previously
server unix:/tmp/puma.sock fail_timeout=0;
}
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
# set client body size to 10M #
client_max_body_size 10M;
gzip on;
server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
#charset koi8-r;
#access_log logs/host.access.log main;
root /Users/Rober/Projects/domain/dev/domain/app;
index index.html index.htm;
try_files $uri $uri/ /index.html =404;
# Proxy requests to the backoffice Rails API
location /api {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
#proxy_set_header X-Forwarded-Proto https;
proxy_redirect off;
rewrite ^/api(.*) /$1 break;
proxy_pass http://api.development;
}
# Rule to proxy the sidekiq web UI
location /sidekiq {
proxy_pass http://api.development;
}
# Expire rules for static content
# RCM: WPO
# Images
location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
root /Users/Rober/Projects/domain/dev/domain/app;
expires 1w;
add_header Cache-Control "public";
}
# This rule is the root cause of the problems with the sidekiq css
# I have commented it for testing purposes
# CSS and Javascript
#location ~* \.(?:css|js)$ {
# root /Users/Rober/Projects/domain/dev/domain/app;
# expires 1w;
# add_header Cache-Control "public";
#}
# I have replaced the previous location above for this as suggested by #Beena Shetty.
location ~* \.(?:css|js)$ {
add_header X-debug-message "Into the location css" always;
if ($uri !~* "^/sidekiq/\w*(.*)+$") {
add_header X-debug-message "Into the location css if" always;
root /Users/Rober/Projects/domain/dev/domain/app;
expires 1w;
add_header Cache-Control "public";
}
}
# cache.appcache, your document html and data
location ~* \.(?:manifest|appcache|html?|xml|json)$ {
root /Users/Rober/Projects/domain/dev/domain/app;
expires -1;
}
}
include servers/*;
}
In Rails:
routes:
require 'sidekiq/web'
mount Sidekiq::Web => '/sidekiq'
I have included next rule in Nginx config file and now when I request http://localhost/sidekiq I can see the web interface and navigate, but still cannot see the styles.
location /sidekiq {
proxy_pass http://api.development;
}
See screenshot.
The dev tools shows that when I load sideqik is trying to get bootstrap.css and some other css and javascript in http://localhost/sidekiq/stylesheets/bootstrap.css
What am I missing?
UPDATE:
I have found out the root cause of the problem in my nginx.conf. I have next rule that set a cache expiration time for performance purposes. If I comment this code, everything works. But how can I have both things living together?
CSS and Javascript
location ~* \.(?:css|js)$ {
root /Users/Rober/Projects/domain/dev/domain/app;
expires 1w;
add_header Cache-Control "public";
}
UPDATE 2: Just in case the problem comes from another point, I have included my whole nginx conf.
Now, with the provided config, the expiration rules in my web app are still working, but the css in the sidekiq webapp do not.
I have included two headers as debug. One when the server is accessing the location rule and the second when the server is accessing inside the if condition. When I request my home page with localhost and I check the request for my own css, such as app.css, I can see the header X-debug-message: Into the location css if, which is right.
If I request sidekiq with localhost/sidekiq I still get 404 error for css, let´s say http://localhost/sidekiq/stylesheets/bootstrap.css and I can see the header X-debug-message: Into the location css.
Current conclusions:
As soon as I include the location ~* .(?:css|js)$ rule, sidekiq css stops working. Even if the rule is empty, like:
location ~* \.(?:css|js)$ {
}
As soon as I delete or comment the whole rule, the sidekiq css works perfectly, but unfortunately this is not compatible with the expires rules that we need to include for performance purposes.
Try this:
location ~* \.(?:css|js)$ {
if ($uri !~* "^/sidekiq/\w*(.*)+$"){
root /Users/Rober/Projects/domain/dev/yanpy/app;
expires 1w;
add_header Cache-Control "public";
}
}
I wasn't able to find a fix for this, so I hacked it with the following method: I copied sidekiq's assets to the public folder and it started working ( since they're referenced by the UI ).
|- images
|-- favicon.io
|-- logo.png
|-- status.png
|- javascripts
|-- application.js
|-- dashboard.js
|- stylesheets
|-- application.css
|-- application-rtl.css
|-- application-dark.css
|-- application-rtl.min.css
|-- bootstrap.css
Mainly, these files: https://github.com/mperham/sidekiq/tree/master/web/assets

Assets not loading in newly deployed Rails app

Just deployed my rails app for the first time and none of my assets are loading. I don;t use the Asset pipeline but have left it switched on for any gems that may use it. I have a Gulp workflow configured which outputs all assets to the public/assets directory.
My my layout I call:
<link rel="stylesheet" href="<%= gulp_asset_path('stylesheets/application.css') %>">
which gives me:
<link rel="stylesheet" href="/assets/stylesheets/application.css">
Any ideas?
Edit:
My conf file:
server {
listen 80 default_server;
server_name dev.myapp.co.uk;
passenger_enabled on;
rails_env production;
root /home/deploy/myapp/current/public;
location /assets {
gzip_static on;
expires max;
add_header Cache-Control public;
}
# redirect server error pages to the static page /50x.html
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}

Content-Length Header missing from Nginx-backed Rails app

I've a rails app that serves large static files to registered users. I was able to implement it by following the excellent guide here: Protected downloads with nginx, Rails 3.0, and #send_file. The downloads and everything else is working great, but there is just this problem - The Content-Length header isn't being sent.
It's okay for small files, but it gets really frustrating when large files are downloaded, since download managers and browsers don't show any progress. How can I fix this? Do I have to add something to my nginx configuration or do I have to pass along some other option to the send_file method in my rails controller? I have been searching online for quite some time but have been unsuccessful. Please Help! Thanks!
Here's my nginx.conf:
upstream unicorn {
server unix:/tmp/unicorn.awesomeapp.sock fail_timeout=0;
}
server {
listen 80 default_server deferred;
# server_name example.com;
root /home/deploy/apps/awesomeapp/current/public;
location ~ /downloads/(.*) {
internal;
alias /home/deploy/uploads/$1;
}
location ^~ /assets/ {
gzip_static on;
expires max;
add_header Cache-Control public;
}
try_files $uri/index.html $uri #unicorn;
location #unicorn {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_set_header X-Sendfile-Type X-Accel-Redirect;
proxy_set_header X-Accel-Mapping /downloads/=/home/deploy/uploads/;
proxy_pass http://unicorn;
}
error_page 500 502 503 504 /500.html;
client_max_body_size 20M;
keepalive_timeout 10;
}
Okay, here's something. I don't know if it's the right way or not but I was able to fix the issue by manually sending the Content-Length Header from my Rails Controller. Here's what I'm doing:
def download
#file = Attachment.find(params[:id])
response.headers['Content-Length'] = #file.size.to_s
send_file(#file.path, x_sendfile: true)
end
nginx should be automatically able to set the header. There must be something that I'm missing; but until I find a 'proper' solution, I guess this will have to do.
P.S: The Header needs to be a string to work properly with some webservers, hence the .to_s

rails app on nginx+passenger not showing custom error pages

I have a Rails app running on nginx 1.2.0 and passenger 3.0.7. I would like to have the custom error pages in the rails app (e.g. /rail_app/public/500.html) be displayed when the appropriate http error occurs within the app.
Here is my current nginx config file:
http {
passenger_root /usr/lib/ruby/gems/1.8/gems/passenger-3.0.7;
passenger_ruby /usr/bin/ruby;
include mime.types;
default_type application/octet-stream;
#access_log /opt/nginx/logs/access.log main;
sendfile on;
#tcp_nopush on;
server {
listen 80;
server_name localhost;
root /var/www/dashboard/current/public;
passenger_enabled on;
passenger_min_instances 1;
# listen 443;
# ssl on;
# ssl_certificate /opt/nginx/conf/server.crt;
# ssl_certificate_key /opt/nginx/conf/server.key;
error_page 500 502 503 504 /500.html;
location = /500.html {
root /var/www/dashboard/current/public/;
}
}
}
This configuration does not show the rails app customer error page rather just sends the http error status code to the client.
Anyone know what it takes to have nginx/passenger send the rails app custom error page to the client with the http error status code?
Please try the following:
# We use the x just because it is for all 5xx errors.
error_page 500 502 503 504 /5xx.html;
location = /5xx.html {
alias /var/www/dashboard/current/public/;
}
Reconfiguring the root directive makes no sense, as it is already set to the path you specified before. The alias ensures that the specific location is internally matched to a different location on the file system. All incoming request parameters should be passed along and if your Rails app is taking care of things at this point it should answer. Just make sure that your Rails app isn't answering with a 500 status again (I don’t know what would happen then).
Related Links
alias
You're probably missing passenger_intercept_errors on; in your nginx config
see the passenger docs for this directive for more info
The config I use:
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}

Should I have these directives in my Nginx config?

I'm using just Rails straight with Nginx (no apache), and wondering if I even need the following in my virtual hosts:
# serve static content directly
location ~* \.(ico|jpg|gif|png|css|js|swf|html)$ {
if (-f $request_filename) {
expires max;
break;
}
}
Is it even relevant if I'm not using Apache?
Thanks in advance.
Since you are using Rails, probably the only config you need is just:
server {
listen 80;
server_name example.com;
location / {
root path/to/static/files;
try_files $uri #rails;
expires max;
}
location #rails {
# proxy_pass to gunicorn or whatever...
}
}
But it is always better to keep all media files in a separate directory:
server {
listen 80;
server_name example.org;
location / {
# proxy_pass to gunicorn or whatever...
}
location /media/ {
root path/to/static/files;
try_files $uri #rails;
expires max;
}
}
Leave ugly and complicated rules like:
location ~* \.(ico|jpg|gif|png|css|js|swf|html)$`
to PHP-folks. They usually have a mess of code and media in their projects and like programming in web-server configs instead of doing all the application logic in application.
This if is meaningless and just wasting of your cpu.
From documentation:
Enables or disables adding or modifying the “Expires” and “Cache-Control” response header fields provided that the response code equals 200, 201, 204, 206, 301, 302, 303, 304, or 307.
# http://nginx.org/r/expires
Do you see 404 here? I do not.
Stops processing the current set of ngx_http_rewrite_module directives.
# http://nginx.org/r/break
And I see no other rewrite directives in your location.
This directive tells nginx to set the expires header to largest possible value and to stop processing further directives when requested file ends with one of those extensions and exists. Those files will be served by nginx. This is valid directive whether you use apache or not. You don't have to use it, but you better do because nginx is very good at serving static files.
Update
As other people pointed out, you should avoid using if. You can do this by:
location ~* \.(ico|jpg|gif|png|css|js|swf|html)$ {
try_files $uri =404;
expires max;
}
if you want to do special processing, use named locations:
location ~* \.(ico|jpg|gif|png|css|js|swf|html)$ {
try_files $uri #process_404;
expires max;
}
location #process_404{
# do your stuff
}

Resources