rails fallback to assets pipeline - ruby-on-rails

I'm trying to deploy a rails4 (ruby-2.0.0) app to my server. Almost all of my assets are precompiled, and served by nginx.
One js.erb, generates a dynamic html-list, by getting models from my database. This asset can't be precompiled, because it must remain dynamic.
I'm excluding this asset from asset.precompile, and turned on
config.assets.compile = true
to fall back to the asset pipeline, for this one asset.
In my local production env, everthing is working, but on my server (nginx, unicorn) the asset pipeline fall back won't work. I get a simple 404 Error
nginx error log:
2013/09/13 08:54:54 [error] 27442#0: *58 open() "/XXX/current/public/assets/rails_admin/rails_admin_switchable-051203ae1d7aca2c08092e5c92bcdf15.js" failed (2: No such file or directory), client: XXX, server: , request: "GET /assets/rails_admin/rails_admin_switchable-051203ae1d7aca2c08092e5c92bcdf15.js HTTP/1.1", host: "XXX", referrer: "http://XXX/admin"
unicorn and rails don't show any errors.
Any ideas, how I can solve this?
best,
Franz

It looks like your nginx server definition isn't properly integrated with your app server. It should be configured to pass a request that doesn't match a physical file on to the app server.
Here is a standard configuration for a rails app living in /app with nginx via a unicorn/UNIX-socket integration:
upstream app_server {
server unix:/tmp/nginx.socket fail_timeout=0;
}
server {
listen <%= ENV["PORT"] %>;
server_name _;
keepalive_timeout 5;
# path for static files
root /app/public;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://app_server;
}
# Rails error pages
error_page 500 502 503 504 /500.html;
location = /500.html {
root /app/public;
}
}
If your asset pipeline compiles to /app/public/assets you should be good to go.

Related

Nginx + Rails + sendfile : file not found

Been having a problem setting up sendfile for nginx+rails lately. We have one kind of file download that's already handled by nginx, and we wanted to add a second rule to deal with another kind of files at another location, but we have no success so far.
Ruby controller :
def download_file
send_file("/srv/www/myapp/shared/tmp/directory/file.zip")
end
Environment configuration file:
Rails.application.configure do
# ..
config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx
# ..
end
Nginx configuration :
upstream myapp {
server 127.0.0.1:9292;
}
server {
listen 80;
server_name myapp.tld;
client_max_body_size 10M;
root /srv/www/myapp/current/public;
# This first block works perfectly
location /__working_file {
internal;
alias /var/lib/myapp;
}
# This second block does not work at all
location /__new_files {
internal;
alias /srv/www/myapp/shared/tmp/directory;
}
location / {
root /srv/www/myapp/current/public;
try_files $uri #app;
}
location #app {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-Sendfile-Type X-Accel-Redirect;
# This first rule works perfectly
proxy_set_header X-Accel-Mapping /var/lib/myapp/=/__working_file/;
# This second rule doesn't
proxy_set_header X-Accel-Mapping /srv/www/myapp/shared/tmp/directory/=/__new_files/;
proxy_pass_header Server;
proxy_read_timeout 300;
proxy_pass http://myapp;
}
}
Result
When accessing the controller action, the send_file command is triggered, then we get a "File not found" in the browser, nothing gets downloaded, and the rails log shows this:
Sent file /srv/www/myapp/shared/tmp/directory/file.zip (0.4ms)
Completed 200 OK in 169ms (ActiveRecord: 37.5ms)
Started GET "/srv/www/myapp/shared/tmp/directory/file.zip" for 109.190.197.126 at 2018-11-20 11:34:44 +0100
ActionController::RoutingError (No route matches [GET] "/srv/www/myapp/shared/tmp/directory/file.zip"):
The file does exist and is readable but nginx can't seem to access it. Any idea?
The problem comes from the way the two X-Accel-Mapping are set. Rack is indeed able to deal with several mappings since the merge of this PR #1187.
However, as of today, this PR has been merged on master but not released yet (2.0.6 is currently the latest release).
The only thing is, the correct way of setting several mappings is by using one single proxy_set_header rule, and separate each mapping with a comma, like this:
proxy_set_header X-Accel-Mapping, /var/lib/myapp/=/__working_file/,/srv/www/myapp/shared/tmp/directory/=/__new_files/
while routing it is not getting . you have to provide proper route to work.

Nginx 404 (Not Found) only for rails images on AWS

Im testing my app in production mode and running into a weird problem. The image assets aren't loading on the webpage. All the css and javascript are loading fine but any image gets a 404 (Not Found) from nginx. Im pushing my app with docker to AWS services. When I precompile the assets the images are included in the output so they should be there:
I, [2017-08-30T12:10:50.092655 #7] INFO -- : Writing /livingrecipe/public/assets/default-user-d39e051d757a0f45602b6065147cec0a307c6a2a070834f6fe6fc2b0fc914207.jpg
I, [2017-08-30T12:10:50.104846 #7] INFO -- : Writing /livingrecipe/public/assets/default_recipe-be8d509a8e34dfec514c097b119753d4aaadbfd1dd6747e3c868ed454b8a36b3.jpg
I, [2017-08-30T12:10:50.108486 #7] INFO -- : Writing /livingrecipe/public/assets/edit-4e381318428d71d55ed0cbdc32b4ce99db6622140c6e7c751ae0f1bfb40d3963.png
I, [2017-08-30T12:10:50.111606 #7] INFO -- : Writing /livingrecipe/public/assets/favorite-91432751e505915d80cf4e24d7a8d0abd3493d05d2d447c1cebb055d99757ac8.png
I, [2017-08-30T12:10:50.115720 #7] INFO -- : Writing /livingrecipe/public/assets/icon_email-203dcd7bdcf7559d742f76a9d8c21e98694d45b9556b29ab9ce286a7dd37af11.png
I, [2017-08-30T12:10:50.118457 #7] INFO -- : Writing /livingrecipe/public/assets/icon_facebook-0140437ec6cb29bc6ac6f8505f05f11603a430298e48c1ea483f7de390ba34a9.png
I, [2017-08-30T12:10:50.121393 #7] INFO -- : Writing /livingrecipe/public/assets/icon_google-2a98026430dddc53539d283c04a2ad4b50536c93d0becadb03f3f61443e52c9c.png
I, [2017-08-30T12:10:50.125228 #7] INFO -- : Writing /livingrecipe/public/assets/star-half-db15fb9b3561d5c741d8aea9ef4f0957bd9bc51aa1caa6d7a5c316e083c1abd5.png
I, [2017-08-30T12:10:50.127714 #7] INFO -- : Writing /livingrecipe/public/assets/star-off-6aaeebdaab93d594c005d366ce0d94fba02e7a07fd03557dbee8482f04a91c22.png
I, [2017-08-30T12:10:50.130083 #7] INFO -- : Writing /livingrecipe/public/assets/star-on-fd26bf0ea0990cfd808f7540f958eed324b86fc609bf56ec2b3a5612cdfde5f5.png
I, [2017-08-30T12:10:50.132442 #7] INFO -- : Writing /livingrecipe/public/assets/unfavorite-7000a53c81f55cf94c88031a8529f847e5a6abd7460e0e54ae0b581b0cde85a3.png
And here is my default.conf file for the nginx:
upstream PLACEHOLDER_BACKEND_NAME {
# The web app.
server PLACEHOLDER_BACKEND_NAME:PLACEHOLDER_BACKEND_PORT;
}
# Redirect 'www' addresses to the non-www version, and also take care of
# redirects to HTTPS at the same time.
server {
listen 80;
server_name www.PLACEHOLDER_VHOST;
return 301 https://$host$request_uri;
}
server {
# "deferred" reduces the number of formalities between the server and client.
listen 80 default deferred;
server_name PLACEHOLDER_VHOST;
# Static asset path, which is read from the PLACEHOLDER_BACKEND_NAME
# container's VOLUME.
root /PLACEHOLDER_BACKEND_NAME/public;
# Serve static assets.
#
# gzip_static is enabled because the assets are already gzipped to a maximum
# level due to Rail's asset pipeline.
#
# Add headers to set the maximum amount of cache time.
#
# We can do this because the Ruby on Rails asset pipeline md5 hashes all of
# the file names for us. When a file changes, its md5 will change, and the
# cache will be automatically busted. Other frameworks can do this as well.
location ~ ^/assets/ {
gzip_static on;
# Set a maximum cache time period and null out a few headers to address
# certain browsers from occasionally requesting cached content.
expires max;
add_header Cache-Control public;
add_header Last-Modified "";
add_header ETag "";
}
# Ensure timeouts are equal across browsers.
keepalive_timeout 60;
# Disallow access to hidden files and directories.
location ~ /\. {
return 404;
access_log off;
log_not_found off;
}
# Allow optionally writing an index.html file to take precedence over the upstream.
try_files $uri $uri/index.html $uri.html #PLACEHOLDER_BACKEND_NAME;
# Attempt to load the favicon or fall back to status code 204.
location = /favicon.ico {
try_files /favicon.ico = 204;
access_log off;
log_not_found off;
}
# Force SSL connections on agents (browsers) who support this header.
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains;";
# Load the web app back end with proper headers.
location #PLACEHOLDER_BACKEND_NAME {
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Ssl on;
proxy_redirect off;
if ($http_x_forwarded_proto = "http") {
return 301 https://$host$request_uri;
}
proxy_pass http://PLACEHOLDER_BACKEND_NAME;
}
}
I am going to answer from experience, although I had the same problem with Nginx + Django. What saved my situation is giving a user that runs the server (might be www-data, in my case was me) privilege to access images. Yes, it sounds stupid since it has permissions on other files, but that helped. Try
sudo chmod -R 750 /root/of/website
This will recursively give permission 750 to all files in that directory. Hope it helps!
Note: It might not be the problem so please do not downvote if it doesn't help.
So it was a simple mistake of wrong way to reference the assets. In rails I was using this:
<%= asset_path('/assets/image.png') %>
which in development mode still showed the assets but in production it couldnt find it cause it already adds the assets folder so was looking in assets/assets/image. Changed everything to:
<%= asset_path('image.png') %>
Like it should be and it worked just fine.

Gem asset files when using Nginx

The asset files for a couple of gems I'm using aren't loading after using Nginx in production mode. I'm pretty certain it has to do with the location blocks in my Nginx config, but I'm not sure what to add so that Nginx will point to where the files are located.
The gems in question are sidekiq and rack-mini-profiler
upstream cable {
server unix:///tmp/cable.sock;
}
server {
listen 80;
server_name 66.207.0.133;
root /home/john/rails/cable/public/assets;
location / {
proxy_pass http://cable;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location ~* \.(css|js|otf|woff|ttf|svg|eot)$ {
root /home/john/rails/cable/public/;
}
}
The error in the Nginx log is:
2016/11/07 21:04:36 [error] 22745#22745: *51175 open() "/home/john/rails/cable/public/sidekiq/javascripts/dashboard.js" failed (2: No such file or directory), client: 69.49.80.136, server: 66.207.0.133, request: "GET /sidekiq/javascripts/dashboard.js HTTP/1.1", host: "66.207.0.133", referrer: "http://66.207.0.133/sidekiq"
Obviously the second location block is redirecting all requests for the needed .js and .css files to the wrong location, but how and to where can I redirect requests to /sidekiq/*.js to the correct files?
You first need to find where the correct files are in your filesystem.
find / -path "*/javascripts/dashboard.js"
A web search reveals that it might be /home/site/homepage_production/shared/bundle/ruby/1.9.1/gems/sidekiq-2.16.1/web/assets/javascripts/dashboard.js.
So, if you gotta serve that from /sidekiq/javascripts/dashboard.js on the web, and provided that all requests within /sidekiq/ are for static assets, then the following should be used:
location ^~ /sidekiq/ {
alias /home/site/homepage_production/shared/bundle/ruby/1.9.1/gems/sidekiq-2.16.1/web/assets/;
}
For more details, see:
http://nginx.org/r/location
http://nginx.org/r/alias

Carrierwave + NGINX rails production images not displaying

I've deployed a rails app that allows a user to upload a photo and have it display on another page, fairly simple. I tested it in development, the image uploads to the public folder and displays properly. In production and deployed the image uploads to the server but isn't being rendered on the page.
404 errors for only the images I've uploaded, path looks like this:
http://IP-OF-APP/uploads/blog/name-of-image.jpg
I read in another S.O. article mentioning setting a production config serve_static_files as true. This didn't fix the problem.
I thought it might be a server configuration with NGINX not picking up the uploads path here is my /sites-default/nginx.conf file.
upstream app {
server unix: /home/deploy/MYAPPNAME/shared/tmp/sockets/puma.sock fail_timeout=0;
}
server {
listen 80;
server_name IP_ADDRESS_OF_SERVER;
root /home/deploy/MYAPPNAME/public;
location #app {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_pass http://app;
proxy_redirect off;
}
location ~ ^/(assets)/ {
root /home/deploy/MYAPPNAME/shared/public
gzip_static on;
expires max;
add_header Cache-Control public;
}
location ~ ^/uploads/ {
root /home/deploy/MYAPPNAME/shared/public;
expires 24h;
add_header Cache-Control public;
break;
}
location ~ ^/(fonts|system)/favicon.ico/robots.txt {
gzip_static on;
expires max;
add_header Cache-Control public;
}
error_page 500 502 503 504 /500.html;
client_max_body_size 4G;
keepalive_timeout 10;
}
I also thought it might be that the image isn't uploading to the server correctly so I SSH'd into the server and found the image uploaded living in:
home/deploy/MYAPPNAME/shared/public/uploads/blog/name-of-image.jpg
Also found what I assume to be the same image in:
home/deploy/MYAPPNAME/current/public/uploads/blog/name-of-image.jpg
My uploader looks like this:
class BlogUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
storage :file
def store_dir
'uploads/blog'
end
def extension_whitelist
%w(jpg jpeg gif png)
end
process :resize_to_fit => [825, 825]
#titles are validated to be unique
def filename
"#{model.title}"+".#{file.extension}" if original_filename.present?
end
end
using
Rails 4.2.6
Ruby 2.3.0
Carrierwave 0.11.0
EDIT
All of the other static images, CSS and JS are displaying and rendering properly.
The NGINX error when rending the upload image produces this:
2016/04/29 17:32:34 [error] 4993#0: *23 open() "/home/deploy/MYAPPNAME/shared/public/assets/uploads/blog/name-of-image.jpg" fails (2: no such file or directory), client: *******, server: SERVER_IP, request: "GET /assets/uploads/blog/name-of-image.jpg HTTP/1.1", host: "SERVER_IP"
From your log:
User is requesting:
/assets/uploads/blog/name-of-image.jpg
Nginx is looking for the image in:
/home/deploy/MYAPPNAME/shared/public/assets/uploads/blog/name-of-image.jpg
You confirmed that the image is in:
home/deploy/MYAPPNAME/shared/public/uploads/blog/name-of-image.jpg
Nginx is looking in public/assets/uploads, your file is in public/uploads.

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;
}

Resources