Enabling SSL on NGINX reverse proxy towards non-SSL apache

NGINX and APACHE Prestashop Docker containers

Last modified: 17 January 2019

My Setup is as follow:

  • NGINX web facing proxy (docker container) that accepts connections on port 80/443.
  • APACHE internal web server (docker container) hosting the Prestashop website

Prestashop 1.7.3 is installed in an internal server with APACHE.

NGINX is the main web proxy which forwards encrypted requests to internal UNENCRYPTED server running APACHE (also docker containers).

So the setup is:

INTERNET –(SSL)–> NGINX —(NON-SSL)—>INTERNAL SERVER (apache)

It is useless to set up TLS also on the apache web server, it would be redundant and would add more load on the server.

Problem

The problem is that I cannot enable SSL on Prestashop directly on the internal Apache web server which hosts the code. Apache uses port 80 and would get redirection loops towards the proxy server.

Without enabling SSL on prestashop, it doesnt rewrite URLs to the correct https scheme.

My actual _nginx.conf:

server {
        listen      80 reuseport;
        server_name  localhost <domain>;
        
        
        modsecurity off;
        modsecurity_rules_file /etc/nginx/modsecurity.d/include.conf;
        server_tokens off;

        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

        rewrite ^ https://<domain>$request_uri? permanent;

    }

    server {
        listen       443 ssl default_server http2;
        server_name  <domain>;

        ssl_certificate      certs/cert.pem;
        ssl_certificate_key  certs/privkey.pem;

        ssl_session_cache    shared:SSL:10m;
        ssl_session_timeout  15m;

        ssl_ciphers  HIGH:!aNULL:!MD5;
        ssl_prefer_server_ciphers  on;

        ssl_protocols TLSv1.2;
        ssl_ecdh_curve secp384r1;

        ssl_stapling on;
        ssl_stapling_verify on;

#       add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
        add_header X-Frame-Options SAMEORIGIN;
        add_header X-Content-Type-Options nosniff;
        add_header Referrer-Policy "strict-origin" always;
        add_header X-XSS-Protection "1; mode=block";
        add_header Content-Security-Policy "upgrade-insecure-requests;  ... ;


        location / {
            resolver 127.0.0.11 valid=30s;

            proxy_pass   http://APACHE_PRIVATE_IP;
            proxy_http_version 1.1;           

            #proxy_set_header Host APACHE_PRIVATE_IP;
            proxy_set_header Host  <domain>;
            index index.html index.php;

            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Real-IP $remote_addr;            
            
            root APACHE_ROOT_DIR/htdocs/DOMAIN;
            proxy_bind  MODSEC_INT_IP;
            proxy_pass_request_headers      on;
        }
    }


}

Note:

If you also need ModSecurity Web Application Firewall, you just need to install first the rules (OWASP core ruleset) and then use these two lines:

modsecurity on;
modsecurity_rules_file /etc/nginx/modsecurity.d/include.conf;

Solution

On NGINX using

rewrite ^ https://<DOMAIN>$request_uri? permanent;

First: I need prestashop to create https links instead of http link though the function BaseLink so i changed “http” to “https” in the last row:

File: classes/Link.php

public function getBaseLink($idShop = null, $ssl = null, $relativeProtocol = false)
    {
        static $force_ssl = true;  /*CHANGED null to true */

        if ($ssl === null) {
            if ($force_ssl === null) {
                $force_ssl = (Configuration::get('PS_SSL_ENABLED') && Configuration::get('PS_SSL_ENABLED_EVERYWHERE'));
            }
            $ssl = $force_ssl;
        }

        if (Configuration::get('PS_MULTISHOP_FEATURE_ACTIVE') && $idShop !== null) {
            $shop = new Shop($idShop);
        } else {
            $shop = Context::getContext()->shop;
        }

        if ($relativeProtocol) {
            $base = '//'.($ssl && $this->ssl_enable ? $shop->domain_ssl : $shop->domain);
        } else {
            $base = (($ssl && $this->ssl_enable) ? 'https://'.$shop->domain_ssl : 'https://'.$shop->domain); /*CHANGED second http to https */
        }

        return $base.$shop->getBaseURI();
    }

I just forced PS to use https whether it is activated or not.

Now the back office works flawlessly but the front end get loop redirection because apache sees HTTP requests instead of HTTPS ones and so send a Location header to the HTTPS site.

Now you just need to make apache believe those are HTTPS requests even if those are actually HTTP so add these lines in NGINX reverse proxy nginx.conf:

proxy_set_header X-Scheme https;
proxy_set_header X-Forwarded-Proto https;

and everything works!

Prestashop Forum

My question with related answer is also on the official Prestashop forum here.