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.
Let's look at an example problematic configuration.
# Bad configuration
location / {
proxy_pass http://127.0.0.1:9000;
}
Sinatra doesn't know it's behind a reverse proxy, so url('/robots.txt')
will generate http://127.0.0.1:9000/robots.txt
. Nginx is going to try to correct it automatically with the default proxy_redirect
substitution, resulting in http://hostname/robots.txt
.
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#L284 (def uri
)
https://github.com/rack/rack/blob/master/lib/rack/request.rb#L199 (def scheme
)
You could create binstubs. using these the init-script should be like any other. thin just needs --damonize as parameter if you do not specify it in your thin.yaml. With thin install
thin generates an init-script for you
BUNDLE INSTALL --BINSTUBS
If you use the --binstubs flag in bundle install(1), Bundler will
automatically create a directory (which defaults to app_root/bin)
containing all of the executables available from gems in the bundle.
After using --binstubs, bin/rspec spec/my_spec.rb is identical to
bundle exec rspec spec/my_spec.rb.
http://gembundler.com/man/bundle-exec.1.html
Based on these features this works for me:
bundle install --binstubs
./bin/thin install
/etc/init.d/thin start
Best Answer
How about two thin processes? One without
--ssl
, and hence bound to port 80, and the other with--ssl
and bound to port 443?To be honest, I'd probably be more tempted to use Apache as a reverse proxy between the users and thin, and then have 2 virtualhost sections in your apache configuration.