Htaccess replaces full URL instead of REQUEST_URI

.htaccessmod-rewriterewrite

I'm working on something that should be really simple but hitting head against the wall second day. The URLs redirect requirements are:

  • non-www => www
  • non-https => https
  • /file.html => /utilities/template_handler.php?filename=file.html

The problem: when I request https://example.com/file.html I get r=301 to

https://example.com/utilities/template_handler.php?filename=https://www.example.com/file.html

My .htaccess:

RewriteEngine On
RewriteBase /

RewriteCond %{HTTPS} off
RewriteRule .* https://%{HTTP_HOST}%{REQUEST_URI} [R=301]
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule .* https://www.%{HTTP_HOST}%{REQUEST_URI} [R=301]

RewriteRule ^(.*)\.html$ /utilities/template_handler.php?filename=$1.html [NC]

I'm using litespeed webserver but for troubleshooting purpose set up Apache too, the same result. After debug is turned on I see:

strip base: '/' from URI: '/file.html'
Rule: Match 'file.html' with pattern '.*', result: 1
Cond: Match 'on' with pattern 'off', result: -1
Rule: Match 'file.html' with pattern '.*', result: 1
Cond: Match 'domain.com' with pattern '^www\.', result: -1
Source URI: 'file.html' => Result URI: 'https://www.example.com/file.html'
Rule: Match 'https://www.example.com/file.html' with pattern '^(.*)\.html$', result: 2
Source URI: 'https://www.example.com/file.html' => Result URI: '/utilities/template_handler.php?filename=https://www.example.com/file.html'
replace current query string with 'filename=https://www.example.com/file.html'

If I comment out the last rule I have first two requirements processed correctly.

The same error if the request sent to non-www or to www but non-ssl.

Best Answer

RewriteCond %{HTTPS} off
RewriteRule .* https://%{HTTP_HOST}%{REQUEST_URI} [R=301]
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule .* https://www.%{HTTP_HOST}%{REQUEST_URI} [R=301]

You need to include the L (last) flag on both of these RewriteRule directives, otherwise, processing will continue through the file and the URL will get further rewritten by the RewriteRule that follows (using the output from the previous directive). And since an external redirect has already been triggered by the preceding directives, you then get externally redirected to /utilities/template_handler.php?filename=...., rather than internally rewritten.

These directives should also be reversed to avoid an unnecessary double redirect when requesting a URL of the form http://example.com/.... (Particularly with the addition of the L flag.)

For example:

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

Note also, I've removed the NC flag from the negated !^www\. condition. Since this is a negated regex, you want it to redirect when it does not start www - all lowercase. You still want "bad" requests of the form WwW to be redirected - but if you include the NC flag here they will not.

You can include the L flag on the very last RewriteRule if you wish - although it would be good practise to do so. It is effectively implied, since it is the last rule anyway, but if you added more directives then you might need to remember to add it.

Related Topic