Has anyone had any luck configuring Passenger and nginx with SSL? I've spent hours trying to get this configuration working as I'd like, using what few resources there are out there on the net, and I can't get any of the supposedly forwarded headers to show up in the Rails controller.

For example, with a conf file of the following (and multiple variations thereof):

server {
    listen 3000;
    server_name .example.com;
    root /Users/website/public;
    passenger_enabled on;
    rails_env development;

server {
    listen 3443;
    root /Users/website/public;
    rails_env development;
    passenger_enabled on;

    ssl                  on;
    #ssl_verify_client    on;                                               
    ssl_certificate      /Users/website/ssl/server.crt;
    ssl_certificate_key  /Users/website/ssl/server.key;
    #ssl_client_certificate /Users/website/ssl/CA.crt;                                                                              
    ssl_session_timeout  5m;

    ssl_protocols  SSLv3 TLSv1;
    ssl_ciphers  ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:-LOW:-SSLv2:-EXP;

    proxy_set_header         Host             $http_host;
    proxy_set_header         X-Real-IP        $remote_addr;
    proxy_set_header         X_FORWARDED_PROTO https;
    proxy_set_header         X-Forwarded-For  $proxy_add_x_forwarded_for;
    #proxy_set_header        X-SSL-Subject    $ssl_client_s_dn;             
    #proxy_set_header        X-SSL-Issuer     $ssl_client_i_dn;             
    proxy_redirect           off;
    proxy_max_temp_file_size 0;

and Rails code in the controller like this:

request.headers.each { |k, v|
  RAILS_DEFAULT_LOGGER.error "Header #{k} Val #{v}"

other headers appear, but not those set in nginx, e.g.:

Header rack.multithread Val false
Header REQUEST_URI Val /login/new
Header REMOTE_PORT Val 64021
Header rack.multiprocess Val true
Header SCGI Val 1
Header SERVER_PORT Val 3443
Header HTTP_ACCEPT_CHARSET Val ISO-8859-1,utf-8;q=0.7,*;q=0.7
Header rack.request.query_hash Val 
Header DOCUMENT_ROOT Val /Users/website/public

I've even gone so far as to modify Passenger's abstract_request_handler's main_loop method, i.e.,

headers, input = parse_request(client)
if headers
  if headers[REQUEST_METHOD] == PING
    process_ping(headers, input, client)
    headers.each { |h,v|
      log.unknown "abstract_request_handler: #{h} = #{v}"
    process_request(headers, input, client)

only to find that the supposedly added headers do not exist there either:

abstract_request_handler: HTTP_KEEP_ALIVE = 300
abstract_request_handler: HTTP_USER_AGENT = Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1) Gecko/20090624 Firefox/3.5
abstract_request_handler: PASSENGER_SPAWN_METHOD = smart-lv2
abstract_request_handler: CONTENT_LENGTH = 0
abstract_request_handler: HTTP_IF_NONE_MATCH = "b6e8b9afbc1110ee3bf0c87e119252ad"
abstract_request_handler: HTTP_ACCEPT_LANGUAGE = en-us,en;q=0.5
abstract_request_handler: SERVER_PROTOCOL = HTTP/1.1
abstract_request_handler: HTTPS = on
abstract_request_handler: REMOTE_ADDR =
abstract_request_handler: SERVER_SOFTWARE = nginx/0.7.61
abstract_request_handler: SERVER_ADDR =
abstract_request_handler: SCRIPT_NAME = 
abstract_request_handler: PASSENGER_ENVIRONMENT = development
abstract_request_handler: REMOTE_PORT = 64021
abstract_request_handler: REQUEST_URI = /login/new
abstract_request_handler: HTTP_ACCEPT_CHARSET = ISO-8859-1,utf-8;q=0.7,*;q=0.7
abstract_request_handler: SERVER_PORT = 3443
abstract_request_handler: SCGI = 1 
abstract_request_handler: PASSENGER_APP_TYPE = rails
abstract_request_handler: PASSENGER_USE_GLOBAL_QUEUE = false

I'm tired of banging my head against the wall, so I'd truly appreciate any help I can get!


Passenger 3 implements this feature with a new directive passenger_set_cgi_param which behaves like proxy_set_header.

For example, to pass SSL variables to Rack, you could do this:

server {
  listen 443 default ssl;

  # other SSL stuff goes here
  # other passenger stuff here

  passenger_set_cgi_param  X_FORWARDED_PROTO       https;
  passenger_set_cgi_param  X-SSL-Raw-Cert          $ssl_client_raw_cert;
  passenger_set_cgi_param  X-SSL-Cert              $ssl_client_cert;
  passenger_set_cgi_param  X-SSL-Client-S-DN       $ssl_client_s_dn;
  passenger_set_cgi_param  X-SSL-Client-I-DN       $ssl_client_i_dn;
  passenger_set_cgi_param  X-SSL-Client-Verify     $ssl_client_verify;

You would then have access to X-SSL-Raw-Cert and the others headers in the Rack::Request instance (accessible from #request in your controller).

It's as yet undocumented but here is some more info:


Passenger is not the same as proxying, it uses SCGI in some fashion. The only work around I've come up with is to proxy back through nginx on a different (protected) port. It far from ideal but it does work.


Hummm... Do you happen to have a location directive where proxy_set_header is used once again ?

I'm asking because array directive like fastcgi or proxy_set_header are not inherited from a higher level when they are modified at a lower level. Since you declare your proxy_set_header at server level, any other proxy_set_header directive at a lower level like "location" for instance will clean-up everything declared so far for this level except your lastest directive.


I recommend adding your "star" to passenger issue 371. http://code.google.com/p/phusion-passenger/issues/detail?id=371


The valid options for nginx/1.8.1 Phusion_Passenger/5.0.25

passenger_enabled on; passenger_set_header X-SSL-Subject $ssl_client_s_dn; passenger_set_header X-Client-DN $ssl_client_s_dn; passenger_set_header X-Client-Verify $ssl_client_verify; passenger_set_header X-Forwarded-For "";

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