1

I really need the following 3 features of my website:

  • www to non-www
  • HTTP to HTTPS
  • force trailing slash at the end of the URLs

I have a current implementation but I face a very bad bug.

When I manually enter the URL example.com/page in the browser the browser redirects to example.com/?page/. This, of course, opens my homepage and not the required /page/.

I've tried many examples and read a lot of similar questions for the last few weeks. No success.

Here is my full .htaccess file:

RewriteEngine on
RewriteBase /


# Redirects www to non-www
RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC]
RewriteRule ^(.*)$ https://%1/$1 [R=301,L]


# Force ending url traling slash
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !(.*)/$
RewriteRule ^(.*)$ /$1/ [L,R=301]



# Prevent CI index.php

RewriteCond $1 !^(index\.php|resources|robots\.txt)


RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
#RewriteRule .* index.php/$0 [PT,L] 
RewriteRule ^(.*)$ index.php?$0 [L]

# Prevent user access to the CI system folder.
RewriteCond %{REQUEST_URI} ^system.*
RewriteRule ^(.*)$ /index.php?/$1 [L] 

# Prevent user access to the CI application folder
RewriteCond %{REQUEST_URI} ^application.*
RewriteRule ^(.*)$ /index.php?/$1 [L]

# Redirects http to https protocol
RewriteCond %{HTTPS} !on
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}


# Redirect index.php to the root /
RewriteCond %{THE_REQUEST} ^.*/index\.php 
RewriteRule ^(.*)index.php$ /$1 [R=301,L]

# One month for most static assets
<filesMatch ".(css|jpg|jpeg|png|gif|js|ico)$">
Header set Cache-Control "max-age=2628000, public"
</filesMatch>

RedirectPermanent /free-project-management-certification/ https://example.com/projectmanagement/
RedirectPermanent /free-scrum-master-certification/ https://example.com/scrummaster/
RedirectPermanent /product-owner-certification/ https://example.com/productowner/
RedirectPermanent /human-resources-management-certification/ https://example.com/humanresources/
RedirectPermanent /product-management-certification/ https://example.com/productmanagement/
RedirectPermanent /project-management-certification/ https://example.com/projectmanagement/

RedirectPermanent /index.php/ https://example.com/


<IfModule mod_deflate.c>
  # Compress HTML, CSS, JavaScript, Text, XML and fonts
  AddOutputFilterByType DEFLATE application/javascript
  AddOutputFilterByType DEFLATE application/rss+xml
  AddOutputFilterByType DEFLATE application/vnd.ms-fontobject
  AddOutputFilterByType DEFLATE application/x-font
  AddOutputFilterByType DEFLATE application/x-font-opentype
  AddOutputFilterByType DEFLATE application/x-font-otf
  AddOutputFilterByType DEFLATE application/x-font-truetype
  AddOutputFilterByType DEFLATE application/x-font-ttf
  AddOutputFilterByType DEFLATE application/x-javascript
  AddOutputFilterByType DEFLATE application/xhtml+xml
  AddOutputFilterByType DEFLATE application/xml
  AddOutputFilterByType DEFLATE font/opentype
  AddOutputFilterByType DEFLATE font/otf
  AddOutputFilterByType DEFLATE font/ttf
  AddOutputFilterByType DEFLATE image/svg+xml
  AddOutputFilterByType DEFLATE image/x-icon
  AddOutputFilterByType DEFLATE text/css
  AddOutputFilterByType DEFLATE text/html
  AddOutputFilterByType DEFLATE text/javascript
  AddOutputFilterByType DEFLATE text/plain
  AddOutputFilterByType DEFLATE text/xml

  # Remove browser bugs (only needed for really old browsers)
  BrowserMatch ^Mozilla/4 gzip-only-text/html
  BrowserMatch ^Mozilla/4\.0[678] no-gzip
  BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
  Header append Vary User-Agent
</IfModule>

I changed the positions of all redirects, tried different combinations, changed characters, a lot of things. I would really appreciate some help.

0

1 Answer 1

2
# Redirects http to https protocol
RewriteCond %{HTTPS} !on
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}

Your HTTP to HTTPS redirect is in the wrong place. It needs to be at the top of your .htaccess file, immediately after the RewriteBase directive and before the www to non-www redirect. (You are also missing R and L flags to trigger a permanent redirect and prevent further processing.)

It is currently being called after the URL has been rewritten to index.php?page/ (which is after the first redirect to append the slash). So, the HTTP to HTTPS redirect ends up redirecting to https://example.com/index.php?page/ and another redirect then removes the index.php to finally end up at /?page/ - 3 redirects in all!

In other words:

  • Request http://example.com/page (HTTP)
  • 301 redirect to http://example.com/page/ (append trailing slash)
  • Internal rewrite to http://example.com/index.php?page/
  • 302 redirect to http://example.com/index.php?page/ (since missing R=301 flag on directive)
  • 301 redirect to http://example.com/?page/ (removal of index.php).

As mentioned, you need to move your HTTP to HTTPS redirect to the start of the file (this is the correct order if you intend to implement HSTS in the future):

RewriteEngine on
RewriteBase /

# Redirects http to https protocol
RewriteCond %{HTTPS} !on
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

# Redirects www to non-www
RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC]
RewriteRule ^(.*)$ https://%1/$1 [R=301,L]

You could reverse these two rules to prevent a potential double redirect when requesting http://www.example.com/ (ie. HTTP + www), however, this won't be HSTS compliant (which states that you must redirect to HTTPS on the same host).

However, you still have further conflicts between your mod_rewrite (RewriteRule, RewriteCond) rewrites and mod_alias (RedirectPermanent) redirects. You should avoid mixing redirects from both modules.

RedirectPermanent /index.php/ https://example.com/

This RedirectPermanent directive is not required. You are already performing the necessary redirect (to remove index.php from the visible URL) with mod_rewrite earlier in the code. Although the corresponding mod_rewrite directive would not catch a URL that contained trailing path-info:

# Redirect index.php to the root /
RewriteCond %{THE_REQUEST} ^.*/index\.php 
RewriteRule ^(.*)index.php$ /$1 [R=301,L]

Omit the trailing $ on the RewriteRule pattern to allow for optional trailing slashes (ie. path-info) to also be caught. ie. RewriteRule ^(.*)index\.php /$1 [R=301,L].

RedirectPermanent /free-project-management-certification/ https://example.com/projectmanagement/
RedirectPermanent /free-scrum-master-certification/ https://example.com/scrummaster/
RedirectPermanent /product-owner-certification/ https://example.com/productowner/
RedirectPermanent /human-resources-management-certification/ https://example.com/humanresources/
RedirectPermanent /product-management-certification/ https://example.com/productmanagement/
RedirectPermanent /project-management-certification/ https://example.com/projectmanagement/

You should also convert the remaining mod_alias RedirectPermanent directives to corresponding mod_rewrite RewriteRule redirects and place them near the start of your file (either at the very top, immediately after the RewriteBase directive or after the canonical www to non-www / HTTP to HTTPS redirects) in order to avoid further conflicts.

Currently, with using mod_alias RedirectPermanent, requesting any one of these "redirected" URLs will also exhibit similar behaviour. eg. Request /free-project-management-certification/ and you will be externally redirected to /projectmanagement/?free-project-management-certification/ due to earlier rewrites (regardless of whether you request HTTP or HTTPS initially).

I assume these are intended to be 1 to 1 redirects. (Although the RedirectPermanent directive is strictly a many to many redirect due to its prefix-matching nature.)

For example:

RewriteRule ^free-project-management-certification/$ /projectmanagement/ [R=301,L]
RewriteRule ^free-scrum-master-certification/$ /scrummaster/ [R=301,L]
RewriteRule ^product-owner-certification/$ /productowner/ [R=301,L]
RewriteRule ^human-resources-management-certification/$ /humanresources/ [R=301,L]
RewriteRule ^product-management-certification/$ /productmanagement/ [R=301,L]
RewriteRule ^project-management-certification/$ /projectmanagement/ [R=301,L]

UPDATE: If you include these redirects at the very top of the file then you could include a full absolute URL (as you did initially) for the target URL, to avoid multiple redirects when requesting the non-canonical HTTP or www subdomain. eg. https://example.com/projectmanagement/.

You will need to clear your browser cache before testing. Test with 302 (temporary) redirects and/or with browser caching disabled to avoid potential caching issues.

2
  • Thank you very much! It's working! Just these RewriteRule ^free-project-management-certification/$ /projectmanagement/ [R=301,L] redirect to the index page but they are not that important. Jun 29, 2020 at 15:36
  • Have you moved those specific redirects for old pages to the start of the file? (Actually, it would perhaps be preferable to include an absolute URL in those redirects - to avoid multiple redirects when requesting non-canonical HTTP and/or www. eg. RewriteRule ^free-project-management-certification/$ https://example.com/projectmanagement/ [R=301,L]
    – MrWhite
    Jun 29, 2020 at 15:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.