Nginx – how to enable ssl for PHP when behind a reverse proxy

nginxreverse-proxyssl

I have a setup that roughly looks like this (I have tried to simplify this as much as possible):

Port 443 – nginx listens and forwards to port 80, config looks something like this

server {
  listen 443 ssl;
  location / {
  proxy_pass http://127.0.0.1:80;
  proxy_set_header X-Real-IP  $remote_addr;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header X-Forwarded-Proto https;
  proxy_set_header X-Forwarded-Port 443;
  proxy_set_header Host $host;
}

Port 80 – Varnish (reverse proxy) which forwards traffic to port 8080 unless there is a cache hit.

port 8080 nginx setup for PHP with fastcgi. I have some common config:

location ~ \.php$ {
  ...
  include fastcgi_params;
}

and fastcgi_params file which looks like this

...
fastcgi_param   HTTPS           $https if_not_empty;

What I've been trying to do is to create a setup, where nginx when listening on port 8080 is able to detect if it's serving content to http or https. It needs to tell this to PHP so it can create URLs with the correct scheme.

I was thinking that somewhere I should listen for the X-Forwarded-Proto and then tell nginx that https is turned on. I don't know how/where to make some kind of if statement or if something like this is even possible or how to tell nginx that https should be on.

Best Answer

You may solve this in two ways... at the application level or at the Nginx level.

At the application level

Since, it is a PHP application, you may use the following at the top of the code / application...

if ($_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') $_SERVER['HTTPS']='on';

At the Nginx level

Step 1: Map X-Forwarded-Proto to a variable whose value depends on X-Forwarded-Proto.

map $http_x_forwarded_proto $fastcgi_param_https_variable {
    default '';
    https 'on';
}

Step 2: Replace the existing line fastcgi_param HTTPS $https $if_not_empty with the following line that sets fastcgi_param HTTPS based on the above variable

fastcgi_param HTTPS $fastcgi_param_https_variable;

Personally, I prefer the first method since it requires just a single line to set things correctly. The second method should work too, but is not tested.