3 Additional notes about the Opencart .htaccess file
source | link

Aside: Just a few additional comments about the following .htaccess code...

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !.*\.(ico|gif|jpg|jpeg|png|js|css)
RewriteRule ^([^?]*) index.php?_route_=$1 [L,QSA]

I've seen this code repeated a lot with regards to OpenCart (in fact it does seem to be part of the official distribution), however, it strikes me as a bit odd/puzzling:

  1. The presence of the 3rd RewriteCond directive that checks against REQUEST_URI prevents any request that contains .ico or .gif etc. (ie. usually "static resources"), being routed through Opencart. However, since this check appears after the first two conditions that already check for existing files/directories, then it is a check for requests to "static resources" that don't actually map to real files?! Is this a security feature?

    A check of this nature is usually implemented to improve efficiency since filesystem checks are "expensive". But to do that it should be implemented as a separate rule before the front-controller. This would have the same "security" (?) benefit as above, but with improved efficiency.

    In other words, remove this RewriteCond directive and use something like the following before the front-controller instead (although the regex is still begging for an end-of-string anchor ie. $ at the end of the regex, otherwise it is matching .css (for example) anywhere in the URL - maybe that is the intention, but it's also less efficient):

    RewriteRule \.(ico|gif|jpg|jpeg|png|js|css) - [L]
    
  2. The RewriteRule pattern ^([^?]*) looks like a newbie mistake. On first glance it might look like it is trying to capture everything in the URL up to, but not including, the query string (that starts with a literal ? in the URL). However, it doesn't, the query string is not part of the URL-path that is matched by the RewriteRule pattern. Instead, this matches everything up to the first URL-encoded ? in the URL-path (if any). This is strange, unless it is another "security" feature? But if it is a "security" feature then it should instead be implemented as a blocking directive earlier in the .htaccess file?

Unfortunately, I've not been able to find any further explanation as to why the directives might have been written this way.


Aside: Just a few additional comments about the following .htaccess code...

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !.*\.(ico|gif|jpg|jpeg|png|js|css)
RewriteRule ^([^?]*) index.php?_route_=$1 [L,QSA]

I've seen this code repeated a lot with regards to OpenCart (in fact it does seem to be part of the official distribution), however, it strikes me as a bit odd/puzzling:

  1. The presence of the 3rd RewriteCond directive that checks against REQUEST_URI prevents any request that contains .ico or .gif etc. (ie. usually "static resources"), being routed through Opencart. However, since this check appears after the first two conditions that already check for existing files/directories, then it is a check for requests to "static resources" that don't actually map to real files?! Is this a security feature?

    A check of this nature is usually implemented to improve efficiency since filesystem checks are "expensive". But to do that it should be implemented as a separate rule before the front-controller. This would have the same "security" (?) benefit as above, but with improved efficiency.

    In other words, remove this RewriteCond directive and use something like the following before the front-controller instead (although the regex is still begging for an end-of-string anchor ie. $ at the end of the regex, otherwise it is matching .css (for example) anywhere in the URL - maybe that is the intention, but it's also less efficient):

    RewriteRule \.(ico|gif|jpg|jpeg|png|js|css) - [L]
    
  2. The RewriteRule pattern ^([^?]*) looks like a newbie mistake. On first glance it might look like it is trying to capture everything in the URL up to, but not including, the query string (that starts with a literal ? in the URL). However, it doesn't, the query string is not part of the URL-path that is matched by the RewriteRule pattern. Instead, this matches everything up to the first URL-encoded ? in the URL-path (if any). This is strange, unless it is another "security" feature? But if it is a "security" feature then it should instead be implemented as a blocking directive earlier in the .htaccess file?

Unfortunately, I've not been able to find any further explanation as to why the directives might have been written this way.

2 added 17 characters in body
source | link

If I add that after the existing redirect, it appears to not have any effect.

Yes, that is correct. mod_rewrite directives execute top-down (in any given context). The existing directives implement a front-controller type pattern and consequently rewrite most requests to index.php. If the request is rewritten then any directives that follow (ie. your redirect) are simply never processed. (However, change your request to something like /foo/bar.css - regardless of whether this file exists or not - and the redirect should still be triggered.)

If I add it just after the RewriteBase / Firefox gives me the message:

Yes, the HTTP to HTTPS redirect should be placed at the top of your file. After the RewriteBase directive is a logical place to put it.

The page isn't redirecting properly

In other words, you are seeing a redirect-loop. With the directive you've given, that uses the HTTPS server variable, this error implies your host has implemented SSL with some kind of front-end proxy and traffic from the front-end proxy to your site is probably plain-old HTTP (ie. unencrypted).

You need to clarify with your webhost that this is the case and they should be able to tell you exactly what directive(s) you should be using. This might be specific to your server config.

Some hosts set an HTTPS environment variable (yes, same name, but different variable) when the request comes in over HTTPS. This is accessed like %{ENV:HTTPS} (as opposed to %{HTTPS}). This is reasonably common, but is server specific.

A standard proxy server will set the X-Forwarded-Proto HTTP request header in the forwarded request to your application server. So, you can perform the required HTTP to HTTPS redirect with something like the following instead:

RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule (.*) https://www.example.com/$1 [R=301,L,NE]

However, you must clarify with your webhost that this is indeed how the SSL is implemented before using something like this in production. Otherwise, if your site is not behind a (SSL) proxy server then it's possible for someone to construct a malicious request and prevent the site from redirecting to HTTPS.

Since you are messing with 301 (permanent) redirects, you must clear your browser cache before testing.

If I add that after the existing redirect, it appears to not have any effect.

Yes, that is correct. mod_rewrite directives execute top-down (in any given context). The existing directives implement a front-controller type pattern and consequently rewrite most requests to index.php. If the request is rewritten then any directives that follow (ie. your redirect) are simply never processed. (However, change your request to something like /foo/bar.css - regardless of whether this file exists or not - and the redirect should still be triggered.)

If I add it just after the RewriteBase / Firefox gives me the message:

Yes, the HTTP to HTTPS redirect should be placed at the top of your file. After the RewriteBase directive is a logical place to put it.

The page isn't redirecting properly

In other words, you are seeing a redirect-loop. With the directive you've given, that uses the HTTPS server variable, this error implies your host has implemented SSL with some kind of front-end proxy and traffic from the front-end proxy to your site is plain-old HTTP (ie. unencrypted).

You need to clarify with your webhost that this is the case and they should be able to tell you exactly what directive(s) you should be using. This might be specific to your server config.

Some hosts set an HTTPS environment variable (yes, same name, but different variable) when the request comes in over HTTPS. This is accessed like %{ENV:HTTPS} (as opposed to %{HTTPS}). This is reasonably common, but is server specific.

A standard proxy server will set the X-Forwarded-Proto HTTP request header in the forwarded request to your application server. So, you can perform the required HTTP to HTTPS redirect with something like the following:

RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule (.*) https://www.example.com/$1 [R=301,L,NE]

However, you must clarify with your webhost that this is indeed how the SSL is implemented before using something like this in production. Otherwise, if your site is not behind a (SSL) proxy server then it's possible for someone to construct a malicious request and prevent the site from redirecting to HTTPS.

Since you are messing with 301 (permanent) redirects, you must clear your browser cache before testing.

If I add that after the existing redirect, it appears to not have any effect.

Yes, that is correct. mod_rewrite directives execute top-down (in any given context). The existing directives implement a front-controller type pattern and consequently rewrite most requests to index.php. If the request is rewritten then any directives that follow (ie. your redirect) are simply never processed. (However, change your request to something like /foo/bar.css - regardless of whether this file exists or not - and the redirect should still be triggered.)

If I add it just after the RewriteBase / Firefox gives me the message:

Yes, the HTTP to HTTPS redirect should be placed at the top of your file. After the RewriteBase directive is a logical place to put it.

The page isn't redirecting properly

In other words, you are seeing a redirect-loop. With the directive you've given, that uses the HTTPS server variable, this error implies your host has implemented SSL with some kind of front-end proxy and traffic from the front-end proxy to your site is probably plain-old HTTP (ie. unencrypted).

You need to clarify with your webhost that this is the case and they should be able to tell you exactly what directive(s) you should be using. This might be specific to your server config.

Some hosts set an HTTPS environment variable (yes, same name, but different variable) when the request comes in over HTTPS. This is accessed like %{ENV:HTTPS} (as opposed to %{HTTPS}). This is reasonably common, but is server specific.

A standard proxy server will set the X-Forwarded-Proto HTTP request header in the forwarded request to your application server. So, you can perform the required HTTP to HTTPS redirect with something like the following instead:

RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule (.*) https://www.example.com/$1 [R=301,L,NE]

However, you must clarify with your webhost that this is indeed how the SSL is implemented before using something like this in production. Otherwise, if your site is not behind a (SSL) proxy server then it's possible for someone to construct a malicious request and prevent the site from redirecting to HTTPS.

Since you are messing with 301 (permanent) redirects, you must clear your browser cache before testing.

1
source | link

If I add that after the existing redirect, it appears to not have any effect.

Yes, that is correct. mod_rewrite directives execute top-down (in any given context). The existing directives implement a front-controller type pattern and consequently rewrite most requests to index.php. If the request is rewritten then any directives that follow (ie. your redirect) are simply never processed. (However, change your request to something like /foo/bar.css - regardless of whether this file exists or not - and the redirect should still be triggered.)

If I add it just after the RewriteBase / Firefox gives me the message:

Yes, the HTTP to HTTPS redirect should be placed at the top of your file. After the RewriteBase directive is a logical place to put it.

The page isn't redirecting properly

In other words, you are seeing a redirect-loop. With the directive you've given, that uses the HTTPS server variable, this error implies your host has implemented SSL with some kind of front-end proxy and traffic from the front-end proxy to your site is plain-old HTTP (ie. unencrypted).

You need to clarify with your webhost that this is the case and they should be able to tell you exactly what directive(s) you should be using. This might be specific to your server config.

Some hosts set an HTTPS environment variable (yes, same name, but different variable) when the request comes in over HTTPS. This is accessed like %{ENV:HTTPS} (as opposed to %{HTTPS}). This is reasonably common, but is server specific.

A standard proxy server will set the X-Forwarded-Proto HTTP request header in the forwarded request to your application server. So, you can perform the required HTTP to HTTPS redirect with something like the following:

RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule (.*) https://www.example.com/$1 [R=301,L,NE]

However, you must clarify with your webhost that this is indeed how the SSL is implemented before using something like this in production. Otherwise, if your site is not behind a (SSL) proxy server then it's possible for someone to construct a malicious request and prevent the site from redirecting to HTTPS.

Since you are messing with 301 (permanent) redirects, you must clear your browser cache before testing.