ERR_TOO_MANY_REDIRECTS Rails + Puma + Nginx (Dockerized) - ruby-on-rails

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

Related

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

Make rails url_helpers use https in production

I use Rails url_helpers to build resources urls in my app.
An example of a url in development is http://localhost:3000/resource/1, generated by the resource_url(resource) url_helper.
I use nginx in production to proxy_pass the requests to my rails app. My nginx listens on port 433 https and redirects to my rails app on port 5000 http:
server {
listen 443 ssl;
server_name api.staging.zup.me;
ssl_certificate /etc/ssl/certs/cert.crt;
ssl_certificate_key /etc/ssl/certs/cert.key;
location / {
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://localhost:5000;
}
}
Because the request that my Rails app receives from the nginx proxy is http, it is generating the urls like, http://example.com/resource/1, but only in production I want all my urls to use https, for example, https://example.com/resource/1.
What is the best way to make Rails generate urls with the https protocol only in production?
You can use redirect in nginx and never touch rails app to enable https:
server {
listen 80 default_server deferred;
server_name myserver.domain;
return 301 https://$server_name$request_uri; # enforce https
}
# HTTPS server
server {
listen 443;
server_name myserver.domain;
ssl on;
ssl_certificate /etc/nginx/ssl/myserver.domain.crt;
ssl_certificate_key /etc/nginx/ssl/myserver.domain.key;
root /srv/www/myserver;
...
}
Not sure if it's a best way, but I like it becuase you don't have to do anything with rails app, only nginx.

nginx + passenger + rails: do I need to start the rails server or just start nginx?

I'm trying to have my rails server listen on 2 different ports. One solution proposed to me was to use nginx. I installed nginx with sudo passenger-install-nginx-module and added the following to /etc/nginx/conf.d:
server {
listen 80;
listen 10000;
server_name www.myapp.com
passenger_enabled on;
root /root/myapp/public;}
When I went to www.myapp.com I got a 403 Forbidden error. I figured it was because there were no static html files in /public. I dropped a simple "hello world" html page in there and it loaded correctly. I then proceeded to start my rails app using passenger start -e production, which caused it to run in standalone phusion passenger mode on port 3000. I go to myapp.com:3000 and I get the app. However, myapp:80 and myapp:10000 still don't work. I'm confused on how to get my nginx to point to the rails server I'm running. Am I doing this completely wrong? Thanks!
Set nginx to forward to my rails server using this https://gist.github.com/jeffrafter/1229497
worker_processes 1;
error_log /usr/local/var/log/nginx.error.log;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
upstream dev {
server 127.0.0.1:3000;
}
server {
listen 80;
# You could put a server_name directive here (or multiple) if
# you have not setup wildcard DNS for *.dev domains
# See http://jessedearing.com/nodes/9-setting-up-wildcard-subdomains-on-os-x-10-6
# If we choose a root, then we can't switch things around easily
# Using /dev/null means that static assets are served through
# Rails instead, which for development is okay
root /dev/null;
index index.html index.htm;
try_files $uri/index.html $uri.html $uri #dev;
location #dev {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://dev;
}
error_page 500 502 503 504 /50x.html;
}
}

How to configure SSL termination on Nginx with a Rails application

I get my SSL request through a load balancer which is performing SSL termination and sending the decrypted traffic to my server on port 81.
I'm running Nginx and have setup a listener to the port 81 and I would like to tell my Rails app that this is a SSL request.
Here is how my Nginx config looks like:
server {
listen 81;
server_name SERVER;
root /var/www/app/current/public;
error_log /var/log/nginx/app-error.log;
access_log /var/log/nginx/app-access.log;
passenger_enabled on;
client_max_body_size 100M;
location / {
passenger_enabled on;
proxy_set_header X-FORWARDED-PROTO https;
}
}
It goes through but Ruby on Rails doesn't detect the header request.headers['X-Forwarded-Proto'], so it takes the request as non-https.
What should I add in order to get Rails thinking this is a SSL request?
So proxy_set_header is not used by passenger, you need to use passenger_set_cgi_param instead:
passenger_set_cgi_param HTTP_X_FORWARDED_PROTO https;
The nginx directive passenger_set_cgi_param has been replaced by passenger_set_env.
https://www.phusionpassenger.com/documentation/Users%20guide%20Nginx.html#PassengerEnvVar
for newer nginx the directive should be
passenger_set_cgi_param HTTP_X_FORWARDED_PROTO https;

Getting a HTTPS connection to work with a local Rails app and nginx?

I have a Ruby on Rails app that I'm running locally. I then run a nginx server locally, too. Now, the connection works fine over HTTP, but when I try to connect over HTTPS, I get the "502 Bad Gateway" error from nginx. I'm pretty sure the certs are working correctly, because when they weren't, I had a different error, but now something is wrong with my nginx config? Here's the config:
http {
access_log off;
#include mime.types;
default_type application/octet-stream;
upstream mydomain.com {
server 127.0.0.1:8080;
}
server {
listen 80;
server_name local www.local;
location / {
proxy_pass http://mydomain.com;
}
add_header Pragma "no-cache";
}
server {
listen 443;
server_name local www.local;
ssl on;
ssl_certificate /Users/daise/projects/mydomain/run/ssl/server.crt;
ssl_certificate_key /Users/daise/projects/mydomain/run/ssl/server.key;
location / {
proxy_set_header X-FORWARDED_PROTO https;
#proxy_pass https://mydomain.herokuapp.com;
proxy_pass https://mydomain.com;
}
add_header Pragma "no-cache";
}
}
I should also add that I spoof my /etc/hosts file to resolve to 127.0.0.1
Change
proxy_pass https://mydomain.com;
to
proxy_pass http://mydomain.com;
Your ssl is terminated in nginx. So there is no need to use https to connect to the upstream server.
[More]
Also notice a typo in your config file:
proxy_set_header X-FORWARDED_PROTO https
to
proxy_set_header X-FORWARDED-PROTO https;

Resources