Search code examples
apache.htaccesshttp-redirectmod-rewritehttps

http to https redirection through htaccess: incorrect redirection error


I need all HTTP requests to redirect to HTTPS:

RewriteEngine On
RewriteBase /

RewriteCond %{HTTP_HOST} !^some-domain\.com$ [NC]
RewriteRule ^(.*)$ https://some-domain.com/$1 [R=301,L]

RewriteCond %{THE_REQUEST} /(\.+) [OR]
RewriteCond %{THE_REQUEST} /(\?+) [OR]
RewriteCond %{THE_REQUEST} /(/+)
RewriteRule ^(.*)$ 404.html [L]
RewriteRule ^core/(install|temp|smarty|modules|languages|includes|functions|fonts|files|config|classes|cache|backup|tpl)/(.*) - [F]
RewriteRule ^data/(.+)\.(tpl\.html|php|php3|php4|php5|phtml|pl|cgi) - [F]
RewriteRule ^install_check\.html$ install.php?check=yes [L]
RewriteRule ^index\.html$ index.php [L]
RewriteRule ^news\.html$ index.php?news=yes [L]
RewriteRule ^price\.html$ index.php?show_price=yes [L]
RewriteRule ^cart\.html$ index.php?shopping_cart=yes [L]
RewriteRule ^wide_search\.html$ index.php?search_with_change_category_ability=yes [L]
RewriteRule ^feedback\.html$ index.php?feedback=yes [L]
RewriteRule ^compare\.html$ index.php?comparison_products=yes [L]
RewriteRule ^page_([0-9]+)\.html$ index.php?show_aux_page=$1 [L]
RewriteRule ^product_([0-9]+)\.html$ index.php?productID=$1 [L]
RewriteRule ^category_([0-9]+)\.html$ index.php?categoryID=$1 [L]
RewriteRule ^category_([0-9]+)_offset_([0-9]+)\.html$ index.php?categoryID=$1&offset=$2 [L]
RewriteRule ^category_([0-9]+)_show_all\.html$ index.php?categoryID=$1&show_all=yes [L]
RewriteRule ^show_news_([0-9]+)\.html$ index.php?fullnews=$1 [L]

# BEGIN Articles
RewriteRule ^poleznoe/([^/]+)\.html$ index.php?fullarticles=$1 [L]
RewriteRule ^poleznoe/([0-9]+)/$ index.php?articles=yes&offset=$1 [L]
RewriteRule ^poleznoe/$ index.php?articles=yes [L]
# END Articles

RewriteRule ^google([a-z0-9_-]+).html$ google$1.html [L]
RewriteRule ^yandex([a-z0-9_-]+).html$ yandex$1.html [L]

# BEGIN Human friendly URL's
RewriteRule ^news/([^/]*).html$ index.php?uri=$1&uriFor=news [L]
RewriteRule ^([^/]*).html$ index.php?uri=$1&uriFor=pages [L]
RewriteRule ^([^/]*)/$ index.php?uri=$1&uriFor=category [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !([^/]*)/$
RewriteRule ^([^/]*)$  $1/ [L,R=301]
RewriteRule ^(([^/]*)_offset_([0-9]+))/$ index.php?uri=$1&uriFor=category&offset=$2 [L]
RewriteRule ^([^/]*)_show_all/$ index.php?uri=$1&uriFor=category&show_all=yes [L]
#RewriteRule ^([^/]*)/([^/]*)/([^/]*)\.html$ index.php?uri=$3&uriFor=product [L]
RewriteRule ^([^/]*)/([^/]*)\.html$ index.php?uri=$2&uriFor=product [L]
# END Human friendly URL's

I've Googled and came across this solution several times:

RewriteCond %{HTTPS} off
RewriteRule ^.*$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

but if I place it after RewriteEngine On or after RewriteBase / or anywhere near or between

RewriteCond %{HTTP_HOST} !^some-domain\.com$ [NC]
RewriteRule ^(.*)$ https://some-domain.com/$1 [R=301,L]

It gives me an incorrect redirection error when I load the page. Please, tell me what I am doing wrong.


Solution

  • $_SERVER['HTTPS'] is always empty, but $_SERVER['HTTP_X_FORWARDED_PROTO'] returns https when I am on https page

    This tells you that your SSL is managed by a front-end proxy server, not your application server. Your application server does not appear to have an SSL cert installed and is most probably serving a plain HTTP request over port 80 to the proxy server. The proxy server then serves a secure HTTPS response back to the client, so the clients request is secure.

    What this means is that if your application server attempts to redirect HTTP to HTTPS by simply checking the standard server variables (ie. HTTPS or SERVER_PORT) then this is likely to fail with a redirect loop since HTTPS is always off and SERVER_PORT is always 80 - even after the redirect. (Note that the HTTPS Apache variable is likely to be "off", but this often translates to a PHP superglobal $_SERVER['HTTPS'] that is empty - that's just how PHP handles it.)

    Note that the HTTP to HTTPS redirect could be performed in the front-end proxy instead, before it even gets to the application server. (Cloudflare does this with its page rules.)

    Alternatively, you can examine the additional HTTP request headers that are set by the proxy server as the request is forwarded from the proxy to your application server. The X-Forwarded-Proto header (which is stored by PHP as $_SERVER['HTTP_X_FORWARDED_PROTO']) is set by the proxy server and tells you what protocol the client used to connect to your proxy.

    So, bringing the canonical redirects together:

    # www to non-www redirect
    RewriteCond %{HTTP_HOST} !^example\.com
    RewriteRule (.*) https://example.com/$1 [R=301,L]
    
    # http to https redirect
    RewriteCond %{HTTP:X-Forwarded-Proto} !https
    RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
    

    With the format of the directives you posted, the www to non-www redirect should come first in order to avoid a double redirect when requesting http://www.example.com (ie. HTTP and www).

    Note that you should only use this type of HTTP to HTTPS redirect when you are behind a proxy server that is handling the SSL. Otherwise, the X-Forwarded-Proto HTTP request header could be faked by a malicious client request (this, however, should be handled by your proxy server).


    These two rules can be combined into 1 rule if you wish:

    # Canonical redirect (no-www and HTTPS)
    RewriteCond %{HTTP_HOST} !^example\.com [OR]
    RewriteCond %{HTTP:X-Forwarded-Proto} !https
    RewriteRule (.*) https://example.com/$1 [R=301,L]