Rails redirects to wrong host (NginX HTTP_X_FORWARDED_HOST)
ArticleThe other day I noticed a problem on our beta server
(let’s call it qa.ourapplication.it
): Some controllers were redirecting
to production (let’s call it www.ourapplication.com
).
This happened due to fact that entire Rails application was hardcoding
*_url
(like root_url
, welcome_url
, …) and I’ve replace them to
be *_path
(like root_path
, welcome_path
,…).
This should not be an issue for Rails application as long as
default_url_options
host
is set to correct location. So let’s have a
look:
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
# ...
def default_url_options
case Rails.env
when 'production'
{ host: 'www.ourapplication.com' }
when 'qa'
{ host: 'qa.ourapplication.it' }
# ...
else
{ host: 'localhost:3000' }
end
end
# ...
end
http://apidock.com/rails/ActionController/Base/default_url_options
Note: Configuring this in a controller is ok but better practice to configure it in
config/enviroments/qa.rb
likeconfig.action_controller.default_url_options = { host: 'qa.ourapplication.it' }
. Or if you really want to have it in a controller, configureconfig.x.app_host =
‘qa.ourapplication.it’ and inApplicationController
let methoddefault_url_options
to return hash like{ host: Rails.application.config.x.app_host }
Looks ok to me.
So lets have a look into NginX config:
upstream ourapplication {
server unix:///shared/sockets/ourapplication.sock;
}
server {
listen 80;
server_name www.ourapplication.com qa.ourapplication.it;
root /app/public;
# ...
location / {
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;
proxy_set_header Client-IP $remote_addr;
proxy_pass http://ourapplication;
}
# ...
So all should be good ! What is happening here ?
Lets do some debugging and print out the entire request.inspect
to the Rails logs to see
what’s happening
class WelcomeController < ApplicationController
# ...
def index
Rails.logger.info request.inspect
# ...
end
# ...
end
tail -f log/qa.log
Note: If you use Docker + Nginx + Puma and if you need to debug running docker container, you don’t have to deploy new image or commit running container with a change in a file. All you have to do is get inside running docker container , (
sudo docker ps; sudo docker exec -it 1234MyContainerId bash
) and once inside do a file change and then runpumactl restart
that will reload Puma workers with file change without killing Docker container.
…and this is what popped out (inside qa.ourapplication.it):
# ...
"HTTP_HOST"=>"qa.ourapplication.it", "HTTP_X_REAL_IP"=>"81.11.111.148",
"HTTP_X_FORWARDED_FOR"=>"81.11.111.148",
"HTTP_X_FORWARDED_HOST"=>"www.ourapplication.com",
"HTTP_CLIENT_IP"=>"81.91.247.148", "HTTP_CONNECTION"=>"close", "HTTP_USER_A
# ...
@delete_cookies={},
@host="www.ourapplication.com",
@secure=false,
# ...
Look at the HTTP_X_FORWARDED_HOST
This make sense, X-Forwarded-Host
is being passed for the first
server of our NginX configuration www.ourapplication.com
and Rails is using this header to
change the default host.
http://calvincorreli.com/2005/12/05/what-s-with-http_x_forwarded_host/comment-page-1/
Solution
So either remove proxy_set_header X-Forwarded-Host $server_name;
if you don’t necessarily need header HTTP_X_FORWARDED_HOST
,
or extract servers to individual blocks and include commonalities like this:
upstream ourapplication {
server unix:///shared/sockets/ourapplication.sock;
}
server {
listen 80;
server_name www.ourapplication.com:
include /etc/nginx/includes/ourapp-redirects;
include /etc/nginx/includes/ourapp-rootlocation;
}
server {
listen 80;
server_name qa.ourapplication.it:
include /etc/nginx/includes/ourapp-redirects;
include /etc/nginx/includes/ourapp-rootlocation;
}
This way you will ensure proper server_name
block gets triggered and
therefore proper host passed as HTTP_X_FORWARDED_HOST
Entire blog website and all the articles can be forked from this Github Repo