Server Fault is a question and answer site for system and network administrators. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I have a Sinatra app running in nginx (using thin as a back-proxy) and I'm using redirect '/<path>' statements in Sinatra. However, when I access the site under https, those redirects send me to http://localhost/<path> rather than to https://localhost/<path> as they should.

Currently, nginx passes control to thin with this command proxy_pass http://thin_cluster, where thin_cluster is

upstream thin_cluster { server unix:/tmp/thin.cct.0.sock; }

How can I fix this?

share|improve this question

Try this:

redirect "https://#{request.host}/<path>"

Sinatra is lightweight, it does not have any detection for this by default i guess.

share|improve this answer

You should be able to achieve this with the proxy_redirect directive. If proxy_redirect default doesn't work (it should be the default setting), try something like this:

proxy_redirect http://localhost/ https://localhost/;
share|improve this answer

url() determines whether to use HTTP or HTTPS based on information from the Sinatra::Request class, which is derived from the Rack::Request class. When put behind a reverse proxy like Nginx, the reverse proxy has to inject some headers telling Rack (and thus Sinatra) about how the world sees it. Sadly, Nginx doesn't set these headers by default, so we'll have to do so manually.

Rack determines whether or not it is HTTPS based on headers sent from Nginx. Sadly, Nginx doesn't set these headers by default, so you'll have to specify them manually.

Let's look at an example, problematic configuration.

# Bad configuration
location / {
    proxy_pass http://127.0.0.1:9000;
}

Sinatra doesn't even know it's behind a reverse proxy, so url('/robots.txt') will generate http://127.0.0.1:9000/robots.txt. Nginx is actually going to try to correct it automatically with the proxy_redirect substitution, resulting in http://hostname/robots.txt. I would guess that the reason it doesn't fix the scheme is because some people like to mix'n'match HTTP and HTTPS requests, but I'm not sure.

Here's an example proxy clause from my own Nginx configuration.

location / {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-SSL on;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    proxy_pass http://127.0.0.1:9000;
}

X-Forwarded-SSL: on tells Sinatra that it's sitting behind HTTPS and not hTTP, and setting Host: $http_host tells Sinatra the host name of the real server. Now, url('/robots.txt') will output https://hostname/robots.txt. Since we don't need Nginx to run substitutions on our redirects, I turned it off explicitly with proxy_redirect off;. (The X-Forwarded-For is just so Sinatra can figure out the user's IP address, too.)

An additional benefit to relying on Sinatra to do everything correctly instead of Nginx's proxy_redirect clause is that you can use the url() function in within views and the like as well.

X-Forwarded-SSL is only one of a few headers you can set. HTTPS: on, X-Forwarded-Scheme: https, and X-Forwarded-Proto: https should work just fine, too. Note that setting the scheme to something like otherproto will not work with Sinatra since it checks if it's HTTPS or not and generating the URL based on that boolean instead of just copying the scheme.

If you're interested in looking through the code yourself, I recommend these two references as a starting point:

https://github.com/sinatra/sinatra/blob/master/lib/sinatra/base.rb#L263 https://github.com/rack/rack/blob/master/lib/rack/request.rb#L184

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.