Passenger 5 Nginx X-Accel-Redirect - ruby-on-rails

To use Nginx's X-Accel-Redirect feature with passenger, apparently you use passenger_set_header and, if mapping to another location, passenger_set_cgi_param. For instance, here is a configuration which apparently used to work for someone else:
passenger_set_cgi_param HTTP_X_ACCEL_MAPPING "/home/user/rails_app/shared/files/=/documents/";
passenger_pass_header X-Accel-Redirect;
location ~ ^/documents/(\d\d\d)/(\d\d\d)/(\d\d\d)/(.*)$ {
alias /home/user/rails_app/shared/files/$1/$2/$3/$4;
internal;
}
But with passenger 5 they say in release notes:
[Nginx] The passenger_set_cgi_param option has been removed and
replaced by passenger_set_header and passenger_env_var.
Not much information about how to use the two together though for X-Accel-Redirect. No up-to-date tutorials or blogs seem to show how to do it either. How is this done? I can get the following nginx.conf to work for the rails development server (non-passenger) but it does not work with passenger.
upstream api_server {
server localhost:5000;
# (starting passenger with ``` RAILS_ENV=development passenger start -a 127.0.0.1 -p 5000 -d ```) not using unix:socket for a good reason
}
server {
listen 9000;
server_name $host;
return 301 https://$host:9443$request_uri;
#error_page 497 https://$host:9443$request_uri;
}
server {
charset UTF-8;
server_name localhost 0.0.0.0;
root /var/www/html/app;
listen 9443 ssl;
ssl on;
ssl_certificate /opt/nginx/conf/ssl/app.chain.pem;
ssl_certificate_key /opt/nginx/conf/ssl/app.key.pem;
error_page 497 https://$host:9443$request_uri;
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Expose-Headers' 'Content-Length, Content-Type, Keep-Alive, Date, Server, Transfer-Encoding, Cache-Control';
add_header 'Access-Control-Allow-Headers' 'Content-Length, Content-Type, Keep-Alive, Date, Server, Transfer-Encoding, Cache-Control';
passenger_env_var X-Sendfile-Type "X-Accel-Redirect";
passenger_env_var X-Accel-Mapping "/special/place/on/filesystem/=/protected_files/";
passenger_pass_header X-Accel-Redirect;
passenger_pass_header X-Sendfile-Type;
# --------- Serve static applications --------
location / {
try_files $uri $uri/ /index.html;
}
# --------- API --------
location /protected_files/{
# Used for X-Accel-Redirect
internal;
add_header Pragma "no-cache";
alias /special/place/on/filesystem/;
}
location ~ /(api|auth|raw)/ {
# Host + forwarding headers
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;
passenger_pass_header Host;
passenger_pass_header X-Real-IP;
passenger_pass_header X-Forwarded-For;
# Configuration for X-Sendfile style fast & authenticated static serving
proxy_set_header X-Sendfile-Type X-Accel-Redirect;
# proxy_set_header X-Accel-Mapping /mounts/test_data_filesystem/=/protected_files/;
proxy_set_header X-Accel-Mapping /special/place/on/filesystem/=/protected_files/;
passenger_env_var X-Sendfile-Type "X-Accel-Redirect";
passenger_env_var X-Accel-Mapping "/special/place/on/filesystem/=/protected_files/";
passenger_pass_header X-Accel-Redirect;
passenger_pass_header X-Sendfile-Type;
proxy_pass http://api_server;
}

Looks like you found an alternate solution, but posting this in case others run into the same issue and wish to upgrade from Passenger 4 syntax to Passenger 5 along with X-Accel-Redirect.
The following is the changes that worked for me:
Passenger 4 version:
passenger_set_cgi_param HTTP_X_ACCEL_MAPPING /path/to/railsapp/public/=/storage/;
passenger_pass_header X-Accel-Redirect;
location /storage {
root /var/www/shared;
internal;
}
Passenger 5 version:
passenger_set_header X-Sendfile-Type "X-Accel-Redirect";
passenger_env_var HTTP_X_ACCEL_MAPPING /path/to/railsapp/public/=/storage/;
passenger_pass_header X-Accel-Redirect;
location /storage {
root /var/www/shared;
internal;
}
Also, there's a symbolic link to the rails app in /var/www/shared, ln -s /path/to/railsapp/public /var/www/shared/storage, however this can be different based on your nginx configuration.
Hope this helps!

Related

ERR_TOO_MANY_REDIRECTS Rails + Puma + Nginx (Dockerized)

I'm running a dockerized rails app with puma and nginx, however, I'm getting ERR_TOO_MANY_REDIRECTS when trying to access the application from a browser.
I have config.force_ssl = true on my application.rb and this is my nginx conf file:
upstream kisoul {
server rails:3000;
}
server {
listen 80;
listen 443 ssl;
root /usr/share/nginx/kisoul;
try_files $uri #kisoul;
location #kisoul {
proxy_pass_request_headers on;
proxy_ignore_headers Expires Cache-Control;
proxy_set_header Host $http_host;
proxy_pass_header Set-Cookie;
proxy_set_header X-Forwarded-Proto https;
proxy_pass http://kisoul;
}
ssl_certificate /etc/nginx/fullchain.pem;
ssl_certificate_key /etc/nginx/privkey.pem;
}
I have already tried disabling force_ssl from rails and forcing the redirect through nginx, but then I get a problem with Origin header, saying that the origin header (https://localhost) didn't match request.base_url (HTTP://localhost)
I tried many different solutions already described here, but I couldn't find any solution

Rails app working with nginx locally, but redirecting 301 when deployed to EC2

I have a dockerized Rails app, and the nginx container with some custom config on runtime that I have setup. The setup works perfectly fine on my local machine.
I fire up docker-compose and the nginx container pointing to the app network, everything works like a charm. I'm able to access the app at localhost instead of localhost:8000.
However, I'm currently deploying this on AWS ECS and there's a loadbalancer with HTTPS involved here.
The app gets deployed and both containers are running fine, but urls that I hit are returning a 301 Moved Permanently.
Here's my default.conf
upstream PLACEHOLDER_BACKEND_NAME {
server PLACEHOLDER_BACKEND_NAME:PLACEHOLDER_BACKEND_PORT;
}
server {
listen 80;
server_name www.PLACEHOLDER_VHOST;
return 301 https://$host$request_uri;
}
server {
listen 80 default deferred;
server_name PLACEHOLDER_VHOST;
root /PLACEHOLDER_BACKEND_NAME/public;
# level due to Rail's asset pipeline.
location ~ ^/assets/ {
gzip_static on;
expires max;
add_header Cache-Control public;
add_header Last-Modified "";
add_header ETag "";
}
location ~ /\. {
return 404;
access_log off;
log_not_found off;
}
try_files $uri $uri/index.html $uri.html #PLACEHOLDER_BACKEND_NAME;
location = /favicon.ico {
try_files /favicon.ico = 204;
access_log off;
log_not_found off;
}
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains;";
location #PLACEHOLDER_BACKEND_NAME {
proxy_set_header X-Forwarded-Proto $scheme;
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_redirect off;
if ($http_x_forwarded_proto = "http") {
return 301 https://$host$request_uri;
}
proxy_pass http://PLACEHOLDER_BACKEND_NAME;
}
}
and here's my script that I'm running when the container starts :
#!/usr/bin/env bash
set -e
PLACEHOLDER_BACKEND_NAME="my-api"
PLACEHOLDER_BACKEND_PORT="8000"
PLACEHOLDER_VHOST="$(curl http://169.254.169.254/latest/meta-data/public-hostname)"
DEFAULT_CONFIG_PATH="/etc/nginx/conf.d/default.conf"
sed -i "s/PLACEHOLDER_VHOST/${PLACEHOLDER_VHOST}/g" "${DEFAULT_CONFIG_PATH}"
sed -i "s/PLACEHOLDER_BACKEND_NAME/${PLACEHOLDER_BACKEND_NAME}/g" "${DEFAULT_CONFIG_PATH}"
sed -i "s/PLACEHOLDER_BACKEND_PORT/${PLACEHOLDER_BACKEND_PORT}/g" "${DEFAULT_CONFIG_PATH}"
# Execute the CMD from the Dockerfile and pass in all of its arguments.
exec "$#"
I even tried setting PLACEHOLDER_VHOST in the script to my ELB public DNS but to no avail. What could be the issue here?

Rails showing images even though nginx misconfigured?

I have a Rails 5 app. I'm using the Carrierwave gem to allow image uploads to public/system/....
In reviewing production app for performance tweaks, I realized that I misconfigured nginx, and that it's only serving static files from /assets instead of /assets and /system.
What I have:
location ~ ^/assets/ {
gzip_static on;
expires max;
add_header Cache-Control public;
}
What I (think I) should have:
location ~ ^/(assets|system)/ {
gzip_static on;
expires max;
add_header Cache-Control public;
}
However, config.public_file_server.enabled = false is set in production.rb.
So now i'm confused-- how is Rails serving these images? I'm assuming I have a (grossly) incomplete understanding of how the asset pipeline actually works?
Update: nginx config
upstream puma {
server unix:///home/deploy/apps/myapp/shared/sockets/mydomain.sock;
}
server {
listen 80 default;
server_name mydomain.com;
root /home/deploy/apps/myapp/current/public;
access_log /home/deploy/apps/myapp/shared/log/nginx.access.log;
error_log /home/deploy/apps/myapp/shared/log/nginx.error.log info;
location ~ ^/(assets|system)/ {
gzip_static on;
expires max;
add_header Cache-Control public;
}
location ~ ^/(robots.txt|sitemap.xml.gz)/ {
root /home/deploy/apps/myapp/current/public;
}
try_files $uri/index.html $uri #puma;
location #puma {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://puma;
}
location /cable {
proxy_pass http://puma;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
error_page 500 502 503 504 /500.html;
client_max_body_size 50M;
keepalive_timeout 10;
listen 443 ssl; # managed by Certbot
# ssl certificate info...
}
It would help posting the whole nginx configuration for this application. Rails will respect public_file_server when served by passenger, puma etc. However, it can be easily overriden with nginx.
The common nginx config line
root /home/rails/testapp/public;
basically tells nginx to serve /public "as it is" and makes public_file_server irrelevant.
(perhaps).

How to deploy React front end + Rails api backend with nginx, capistrano and passenger

I am deploying an app with a React front end created using create-react-app and a Rails API as the backend. I do not know much about configuring web servers and proxies so I am not sure exactly how to get it to work in production. I am deploying the app to Ubuntu on an Amazon EC2 instance. Nginx is the web server. I need to configure it so that Nginx serves the static files from the client/build directory and then any requests made to /api go to the Rails app running on port 3001. Right now Nginx is serving the index.html and the Javascript is running properly but requests to /api are not going to the right place. Any thoughts on how to configure Nginx to do this? Here is my /etc/nginx/sites-enabled/default file:
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name mydomain.com;
passenger_enabled on;
rails_env staging;
root /home/ubuntu/app-name/current/client/build;
index index.html;
location /api {
proxy_pass http://127.0.0.1:3001;
}
}
What am I missing? How do I get the Rails app to run on port 3001 and have all requests to /api go there? Do I need a separate server block in this config?
I don't know if you already solved your issue and if this solution will apply to you but I think it might be useful for other people searching on this issue.
I am using Puma as the application server to run my rails 5 api.
This is the configuration file for my dev environment:
upstream app {
# Path to Puma SOCK file, here is where you make the connection to your application server
server unix:/path/to/your/puma.sock fail_timeout=0;
}
server {
listen 80;
server_name mydomain.com;
# this is where my react-app is located
root /var/www/development/ram/public/;
index index.html index.htm;
# Serve the static content (React app)
location / {
try_files $uri /index.html =404;
}
location /api {
# Insert your public app path
root /your/rails-app/path/public;
proxy_pass http://app;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
}
error_page 500 502 503 504 /500.html;
client_max_body_size 4G;
keepalive_timeout 10;
}
So comparing, I think your problem might be solved by adding a root directive pointing to your rails api public directory
I hope this can give you some hints on how to configure yours
Here is my working nginx config
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80 default_server;
root /home/deploy/www/sublime/current/public;
index index.html;
server_name domain.com;
access_log /home/deploy/www/sublime/logs/access.log;
error_log /home/deploy/www/sublime/logs/errors.log;
server_name localhost;
passenger_enabled on;
passenger_app_env production;
location ~* ^.+\.(jpeg|gif|png|jpg) {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
}
location /api {
# Insert your public app path
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_buffering off;
}
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri /index.html;
}
}
You can use this repos to see rails configuration
https://github.com/Eth3rnit3/rails-react

serve ssl webpage via nginx, ssl_error_rx_record_too_long

Please help me: there is something wrong in this virtual.conf nginx conf file that means that when viewing the site, I get the error ssl_error_rx_record_too_long rather than being able to view my site. I am using aws, nginx to serve some rails apps - this was working yesterday, but I crashed my entire server and am frantically trying to fix this at 2am, ready for production at 9:30am.
ssl_certificate /etc/ssl/star_my_site.pem;
ssl_certificate_key /etc/ssl/star_my_site.key;
# ------------------
# rails app one
# ------------------
upstream my_app {
server unix:///var/run/puma/my_app.sock;
}
server {
listen 80;
# server_name rails_app_one.my_site.com.au www.rails_app_one.my_site.com.au;
server_name _ localhost;
return 301 https://rails_app_one.my_site.com.au;
}
server {
listen 443;
server_name _ localhost;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://my_app;
}
location ~ "^/assets/" {
root /var/app/current/public;
gzip_static on;
expires max;
add_header Cache-Control public;
}
}
# ------------------
# rails app two
# ------------------
upstream rails_app_two_app {
server unix:///var/run/puma/rails_app_two_app.sock;
}
server {
listen 80;
server_name rails_app_two.my_site.com.au www.rails_app_two.my_site.com.au;
return 301 https://rails_app_two.my_site.com.au;
}
server {
listen 443;
server_name rails_app_two.my_site.com.au;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://rails_app_two_app;
}
location ~ "^/assets/" {
root /var/app/rails_app_two.my_site.com.au/current/public;
gzip_static on;
expires max;
add_header Cache-Control public;
}
}
ssl_error_rx_record_too_long usually means the server is returning plain HTTP content when the browser is expecting HTTPS content. (You can probably verify this by going to http://your.site:443 and seeing your site.)
You need to enable SSL in nginx - simply declaring ssl_certificate isn't enough.
Change listen 443; to listen 443 ssl;
(Also, I'd suggest putting ssl_certificate in the server block, so you can use different ssl certs with other domains.)
Edit: https://serverfault.com/questions/497430/error-code-ssl-error-rx-record-too-long has almost the exact same issue, hopefully you found it before your 9am production release!

Resources