How to forward all requests to www subdomain including https requests? - ruby-on-rails

I have a VPS with a Rails 4 application running on Ubuntu, NginX and Unicorn.
As I want all pages to be SSL encrypted, all requests to http:// are forwarded to https:// which is working fine.
This is an excerpt of my NginX configuration:
http {
....
server {
listen 80;
server_name example.com;
rewrite ^ https://$server_name$request_uri? permanent;
}
server {
listen 443;
server_name example.com;
root /home/rails/public;
index index.htm index.html;
ssl on;
ssl_certificate /etc/ssl/example.com.crt;
ssl_certificate_key /etc/ssl/example.com.key;
location / {
try_files $uri/index.html $uri.html $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 $host;
proxy_pass http://app_server;
}
}
}
How can I make it that all requests to http://example.com and https://example.com are forwarded to https://www.example.com?
Thanks for any help.

We use this in apache2:
<VirtualHost *80>
ServerName frontlineutilities.co.uk
ServerAlias www.frontlineutilities.co.uk
</VirtualHost>
Docs
Having researched how you'd achieve this in Nginx, I found this:
server {
listen 80;
server_name example.org www.example.org;
...
}
--
Capturing Requests
The reason I wrote this as an answer is because your choice is whether to use Middleware or the web server itself
Although I don't know the specifics, I do know that adding to the Rails middleware will eventually lead to bloat. I am a firm proponent of modular programming - and will gladly separate functionality into different parts of the stack
The problem you have is not really a rails one - it's a server issue (how to route all requests to www.). I would therefore highly recommend you focus on the server to get it sorted. As in the end, the sever is there to capture requests to your server IP & route them accordingly
I would start with the resources above & work to redirect in the server. It doesn't matter to rails whether you send a request to www. or the standard domain

If you are looking to setup which redirects http://example.com or https://example.com to http://www.example.com and https://www.example.com
The following should do the redirect
server {
listen 80;
server_name example.com;
rewrite ^/(.*) https://www.example.com$request_uri permanent;
}
server {
listen 443;
server_name example.com;
rewrite ^/(.*) https://www.example.com$request_uri permanent;
}
Also you have change your original server_name to www.example.com
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

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

Nginx reverse proxy SSL termination with docker - too many redirects

I'm a bit out of my element trying to deploy a Laravel (php) application via docker. Everything works great until I try to use SSL certs via Lets Encrypt, which triggers a redirect loop I'm unable to resolve.
upstream app {
server app1520925178:80;
}
server {
listen 80 default_server;
server_name app.example.com;
# handle future LE refreshes
location /.well-known {
root /var/www/html;
try_files $uri $uri/ =404;
}
location / {
return 301 https://$server_name$request_uri;
}
}
server {
listen 443 ssl default_server;
server_name app.example.com;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 24h;
keepalive_timeout 300s;
ssl on;
ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
charset utf-8;
location / {
#include proxy_params;
proxy_pass http://app;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
# Handle Web Socket connections
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
Any guidance is greatly appreciated.
EDIT: It "randomly" started working a few minutes after this post was created. Still not 100% sure why it would take time to "propagate", if anyone has insight into that, I'd appreciate it.
If you use Cloudflare as your DNS service (not registering domains over it, but managing your DNS records with it) and have Cloudflare's protection enabled (orange cloud symbol), this can occur.
Note the following paragraph in this support article by Cloudflare:
If the origin server happens to be configured to redirect HTTP requests to HTTPS, server responses back to Cloudflare are encrypted and since Cloudflare is expecting HTTP traffic, it keeps resending the same request, resulting in a redirect loop. This causes browsers to display "The page isn’t redirecting properly" or "ERR_TOO_MANY_REDIRECTS" errors.
So, the flexible SSL is most likely your issue. You can turn that off by going to the crypto page in your Cloudflare control panel and setting the SSL mode to "Full (strict)":
This resolved my issues on an Apache system, but I'm very confident it's the same source of problems with nginx.
Changing the crypto setting to "Full (strict)" may help:

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.

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;

Resources