Nginx, PHP, HTTPS global var and “single configuration” HTTP/HTTPS virtual host

I recently stumbled upon the problem of configuring a virtual host in Nginx so that it can serve HTTP and HTTPS writing just one configuration section.

I know that this is not the recommended way of configuring HTTP+HTTPS, but having two separate sections when the configuration is the same except for 3 lines (server certificates and listen 443) doesn’t make much sense.

I initially followed the official tutorial (most likely outdated) and added a part relative to PHP files

 
server {
    listen 80;
    listen 443 ssl;
    server_name domain.ext;
    ssl_certificate domain.crt;
    ssl_certificate_key domain.key;

    # ... rest of the configuration

    # PHP stuff
    location ~ \.php$ {
    include fastcgi_params;
    fastcgi_intercept_errors on;
    fastcgi_pass 127.0.0.1:9001;
    # Fixes random Bad gateway errors
    fastcgi_buffer_size 128k;
    fastcgi_buffers 4 256k;
    fastcgi_busy_buffers_size 256k;
    fastcgi_temp_file_write_size 256k;
    }
}

Everything worked smoothly on two computers running Ubuntu 10.04 + Nginx and PHP-fpm from the Nginx PPA. I then tried to do the same on Ubuntu 11.10 and PHP-fpm installed from the official repositories: all hell broke lose.

All of a sudden the $_GLOBALS["HTTPS"] variable was not defined anymore even on secure connections. Setting such variable is an Apache specific behaviour, in all truth, but like most of the rest of the PHP community I learned to rely on it for secure connection detection.

Most of the guides I found online referred to setting the HTTPS variable explicitly in the appropriate server section via the fcgi_param directive, but I didn’t want to have two completely separate configuration sections.

I then resorted to a hybrid, which makes use of dual server sections and one include file.

The configuration file looks like this:

 
# Nginx config file

# HTTP server section
server {
    listen 80;
    include domain.inc;
}

# HTTPS server section
server {
    listen 443 ssl;
    server_name domain.ext;
    ssl_certificate domain.crt;
    ssl_certificate_key domain.key;
    include domain.inc;
    fastcgi_param HTTPS on;
}

The include file looks like this:

 
# Nginx include file: domain.inc

# ... rest of the configuration

# PHP stuff
location ~ \.php$ {
    include fastcgi_params;
    fastcgi_intercept_errors on;
    fastcgi_pass 127.0.0.1:9001;
    # Fixes random Bad gateway errors
    fastcgi_buffer_size 128k;
    fastcgi_buffers 4 256k;
    fastcgi_busy_buffers_size 256k;
    fastcgi_temp_file_write_size 256k;
}

… and the magic is done!

[ALTERNATIVE]

The problem is due to the fact that the fastcgi_params file is not present. Even if it were, the $https variable is not defined anymore in recent versions of nginx.

The solution is to add the following lines to your nginx.conf file

 
# Assigns $https depending on value of  $scheme
# $scheme is either "http" (default), or "https"
map $scheme $https {
	default off;
	https	on;
}

and then create the fastcgi_params file with the following content (will set a few parameters commonly used by Apache/PHP)

 
fastcgi_param	QUERY_STRING            $query_string;
fastcgi_param	REQUEST_METHOD     $request_method;
fastcgi_param	CONTENT_TYPE           $content_type;
fastcgi_param	CONTENT_LENGTH     $content_length;

fastcgi_param	SCRIPT_FILENAME       $request_filename;
fastcgi_param	SCRIPT_NAME              $fastcgi_script_name;
fastcgi_param	REQUEST_URI               $request_uri;
fastcgi_param	DOCUMENT_URI          $document_uri;
fastcgi_param	DOCUMENT_ROOT      $document_root;
fastcgi_param	SERVER_PROTOCOL     $server_protocol;

fastcgi_param	GATEWAY_INTERFACE	CGI/1.1;
fastcgi_param	SERVER_SOFTWARE	nginx/$nginx_version;

fastcgi_param	REMOTE_ADDR             $remote_addr;
fastcgi_param	REMOTE_PORT             $remote_port;
fastcgi_param	SERVER_ADDR               $server_addr;
fastcgi_param	SERVER_PORT               $server_port;
fastcgi_param	SERVER_NAME              $server_name;

fastcgi_param	HTTPS			        $https;

# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param	REDIRECT_STATUS	200;