0

I have been scratching my head off as all of the forums are saying that following simple code should work for redirecting all traffic from HTTPS to HTTP except one location (Nginx: force SSL on one path, non-SSL on others) but in my case it is not working as expected

It is Magento site having following nginx configuration

upstream examplecombackend {
    server unix:/var/run/php-fcgi-examplecom.sock;
}

server {
    listen 111.11.111.111:80;

    server_name example.com *.example.com;

    access_log /home/www/vhosts/example.com/logs/access.log;
    error_log /home/www/vhosts/example.com/logs/error.log;
    root /home/www/vhosts/example.com/httpdocs;

    location / {
        index index.html index.php;
        try_files $uri $uri/ @handler;
        expires 30d;
    }

    location /checkout/ {
        rewrite ^ https://$host$request_uri? permanent;
    }

    location @handler {
        rewrite / /index.php;
    }

    location ~ .php/ {
        rewrite ^(.*.php)/ $1 last;
    }

    location ~ .php$ {
        if (!-e $request_filename) {
            rewrite / /index.php last;
        }
        expires        off;
        fastcgi_pass   examplecombackend;
        fastcgi_param  HTTPS $fastcgi_https;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }
}

server {
    listen 111.11.111.111:443 ssl;
    ssl_certificate      /etc/nginx/ssl/example.com.crt;
    ssl_certificate_key  /etc/nginx/ssl/example.com.key;
    ssl_protocols        TLSv1 TLSv1.1 TLSv1.2;

    server_name example.com *.example.com;

    root /home/www/vhosts/example.com/httpdocs;

    location / {
        rewrite ^ http://$host$request_uri? permanent;
    }

    location /checkout/ {
    }
}

Now, after doing this when I go to SSL https://example.com/ it correctly forwards to Non- SSL http://example.com/ but if I go to https://example.com/checkout it says

404 Not Found
nginx/1.8.1

Not sure what I am missing here....

4
  • Not being familiar with magento: shouldn't you enable php on your https virtualhost?
    – SYN
    Nov 12, 2016 at 20:26
  • /checkout/ is a single fixed URL and doesn't include urls such as /checkout/session=abc?def . Are you sure that is the only URL used? If not then you'll need to use a regular expression match.
    – Tim
    Nov 12, 2016 at 20:27
  • @SYN yes you are correct I should need to add PHP handlers in 443 section too.
    – Farmi
    Nov 12, 2016 at 21:17
  • @Tim yes, it makes sense let me try that too...
    – Farmi
    Nov 12, 2016 at 21:19

3 Answers 3

2

First of all, I wouldn't make such a configuration myself. Having mixed http and https on the same site makes https security weaker and exposes your site to man-in-the-middle attacks.

However, here should be a working configuration for you if you really want to set up thing like this.

The main issue in your configuration is the missing definitions for PHP for your /checkout URI.

I would make a configuration like this. This also contains optimizations for your configuration:

upstream examplecombackend {
    server unix:/var/run/php-fcgi-examplecom.sock;
}

server {
    listen 111.11.111.111:80;

    server_name example.com *.example.com;

    access_log /home/www/vhosts/example.com/logs/access.log;
    error_log /home/www/vhosts/example.com/logs/error.log;
    root /home/www/vhosts/example.com/httpdocs;

    location / {
        index index.html index.php;
        try_files $uri $uri/ /index.php;
        expires 30d;
    }

    location /checkout {
        rewrite ^ https://$host$request_uri? permanent;
    }

    location ~ (.+\.php)/ {
        rewrite ^ $1 last;
    }

    location ~ \.php$ {
        expires        off;
        fastcgi_pass   examplecombackend;
        fastcgi_param  HTTPS $fastcgi_https;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }
}

server {
    listen 111.11.111.111:443 ssl;
    ssl_certificate      /etc/nginx/ssl/example.com.crt;
    ssl_certificate_key  /etc/nginx/ssl/example.com.key;
    ssl_protocols        TLSv1 TLSv1.1 TLSv1.2;

    server_name example.com *.example.com;

    root /home/www/vhosts/example.com/httpdocs;

    location /checkout {
        rewrite ^ /index.php;
    }

    location ~ \.php$ {
        expires        off;
        fastcgi_pass   examplecombackend;
        fastcgi_param  HTTPS $fastcgi_https;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }

    location / {
        rewrite ^ http://$host$request_uri? permanent;
    }
}

Changes I did:

location / {
    index index.html index.php;
    try_files $uri $uri/ /index.php;
    expires 30d;
}

I removed the @handler location, because it is the same as if index.php was the last element of try_files directive.

location ~ .php/ {
    rewrite ^(.*.php)/ $1 last;
}

I changed the regex capture to happen on the location level, therefore there is one regex match less happening on every request matching this location, since we can use simple ^ as the match condition in the rewrite directive.

    location ~ \.php$ {
        if (!-e $request_filename) {
            rewrite / /index.php last;
        }

I added backslash escape to the location regex condition, because without backslash it would match for example /path/to/aphp, since . by itself matches any character.

I also removed the if test shown above from the block, because the same test happens already in the try_files directive earlier.

And finally, the fix to your actual issue:

In your server block for https stuff, I changed location blocks like this:

    location /checkout {
        rewrite ^ /index.php;
    }

    location ~ \.php$ {
        expires        off;
        fastcgi_pass   examplecombackend;
        fastcgi_param  HTTPS $fastcgi_https;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }

So, the location /checkout block tells nginx to rewrite all requests to /index.php, just like in http server block. Also the PHP processing location is the same as in http block.

However, there still might be issues with the links Magento creates, and the way requests get redirected between http and https. Cookie security settings is one thing that can cause problems.

0

Try this small change. The ~* should tell Nginx to case insensitive match any URL with /checkout/ in it, rather than just the precise URL /checkout/.

I'm a little rusty and I can't remember the precise location match order and syntax, but it's worth trying.

location ~* /checkout/ {
2
  • unfotunately that too is throwing 404 Not Found nginx/1.8.1 means location directive has always been correct it's something other which may be directly related to Magento.
    – Farmi
    Nov 13, 2016 at 18:53
  • @Tim, nginx matches locations with simple prefixes just fine, you don't need to enable regex matching here. Nov 13, 2016 at 21:37
-1

Conditional rewriting might help in your case:

server {
    listen 111.11.111.111:80;
    listen 111.11.111.111:443 ssl;   

    ssl_certificate /etc/nginx/ssl/example.com.crt;
    ssl_certificate_key  /etc/nginx/ssl/example.com.key;
    ssl_protocols   TLSv1 TLSv1.1 TLSv1.2;

    server_name example.com *.example.com;

    access_log /home/www/vhosts/example.com/logs/access.log;
    error_log /home/www/vhosts/example.com/logs/error.log;

    root /home/www/vhosts/example.com/httpdocs;

    location / {
        if ($scheme = "https") {
            return 301 http://$server_name$request_uri;
            break;
        }
        index index.html index.php;
        try_files $uri $uri/ @handler;
        expires 30d;
    } 

    location /checkout/ {
        if ($scheme = "http") {
            return 301 https://$server_name$request_uri;
            break;
        }
    }

    location @handler {
        rewrite / /index.php;
    }

    location ~ .php/ {
        rewrite ^(.*.php)/ $1 last;
    }

    location ~ .php$ {
        if (!-e $request_filename) { rewrite / /index.php last; }
        expires        off;
        fastcgi_pass   examplecombackend;
        fastcgi_param  HTTPS $fastcgi_https;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }
}
6
  • the issue is that conditional forwarding makes much load on server and not recommended by nginx, do you see any other alternate than using if
    – Farmi
    Nov 12, 2016 at 21:21
  • The original approach is better, not using if. Follow up the reply there rather than using this IMHO.
    – Tim
    Nov 12, 2016 at 21:23
  • @Tim Please read about if more throughly. It clearly says that this is exactly the case it was designed for. There is no performance penalty if (only) return or rewrite are done inside of an if block.
    – Anubioz
    Nov 12, 2016 at 21:36
  • @Anubioz what is the difference if I use redirect instead of return under if statement?
    – Farmi
    Nov 12, 2016 at 21:45
  • @Tim What I see is that page which is secure has this URL example.com/index.php/checkout/cart/sessionID.... so what should be the regular expression for location directive?
    – Farmi
    Nov 12, 2016 at 21:47

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .