2

On the dev version of a LAMP site (Apache 2.4 on Debian 8.5), my Apache rewrite rules implement a "whitelist then catch-all" concept. Specifically, images from images/, stylesheets from css/, javascript from /js, and a few named files are served directly as static files, and all other requests go to index.php.

The rule-set is in the <VirtualHost block in the .conf file for this site:

 RewriteEngine on
 RewriteCond "%{REQUEST_URI}" "!^(/index\.php|/robots\.txt|/favicon\.ico)$"
 RewriteCond "%{REQUEST_URI}" "!^/images/(.*)\.(jpg|png|jpeg|gif)$"
 RewriteCond "%{REQUEST_URI}" "!^/css/(.*)\.css$"
 RewriteCond "%{REQUEST_URI}" "!^/js/(.*)\.js$"
 RewriteRule ^(.*)$ /index.php/$1 [L]

And this works as intended. (It is based on this SO Q&A and adapted to work in the central config rather than in .htaccess.)

The problem is the test server (Ubuntu 16.04) has to imitate production, and production requires use of .htaccess instead of central configuration, and this rule-set does not work in .htaccess. So the question is how to get the same effect in .htaccess.

I turned on AllowOverride all in the <Directory /srv/www/site1> block in apache2.conf and put a .htaccess file in /srv/www/site1 (which is the DocumentRoot value for this site). And based on this apache doc, removed initial slashes from the paths and filenames in the conditions. Also put LogLevel info rewrite:trace8 in the VH definition to debug this problem.

Here is the current .htaccess:

 Options -Indexes +FollowSymLinks
 AddDefaultCharset UTF-8
 RewriteEngine on
 # RewriteBase "/"
 # AccepthPathInfo on
 RewriteCond "%{REQUEST_URI}" "!^(robots\.txt|favicon\.ico|css\/styles\.css)$"
 RewriteCond "%{REQUEST_URI}" "!^index\.php"
 RewriteCond "%{REQUEST_URI}" "!^css/(.*)\.css$"
 RewriteCond "%{REQUEST_URI}" "!^images/(.*)\.(jpg|png|jpeg|gif)$"
 RewriteCond "%{REQUEST_URI}" "!^js/(.*)\.js$"
 RewriteRule ^(.*)$ /index.php/$1 [L]

And getting http 500. Apache error log shows it's looping the rules until it hits a redirect limit, and creating a bad path like "index.php/index.php/index.php/..."

I've found a very large number of pages about URL rewriting but few are helpful on this.

EDIT: Solved by w3dk - thank you! Applied the solution below and corrected my misspelling of "AcceptPathInfo", then it works.

1
  • With or without initial slashes, it gets the same result as mentioned, HTTP 500 with an endless loop and accumulating path + filename. Commented Jan 9, 2017 at 18:41

1 Answer 1

1
RewriteEngine on
RewriteCond "%{REQUEST_URI}" "!^(/index\.php|/robots\.txt|/favicon\.ico)$"
RewriteCond "%{REQUEST_URI}" "!^/images/(.*)\.(jpg|png|jpeg|gif)$"
RewriteCond "%{REQUEST_URI}" "!^/css/(.*)\.css$"
RewriteCond "%{REQUEST_URI}" "!^/js/(.*)\.js$"
RewriteRule ^(.*)$ /index.php/$1 [L]

The format of your initial directives are OK for .htaccess as well. However, in .htaccess you will need an additional condition to prevent a rewrite loop after rewriting to index.php.

RewriteCond "%{REQUEST_URI}" "!^index\.php"

I can see you've tried to do this in your second code block, however, the value of REQUEST_URI is the same whether you are coding directly in the server config or in .htaccess - it still begins with a slash. So, the above negated condition would always be true (like all the other conditions in the second block) and so the RewriteRule is always processed.

However, you could just add another RewriteRule to the start of your first code block:

RewriteRule ^index\.php - [L]

So, put all together, this becomes:

RewriteEngine on

RewriteRule ^index\.php - [L]

RewriteCond "%{REQUEST_URI}" "!^(/index\.php|/robots\.txt|/favicon\.ico)$"
RewriteCond "%{REQUEST_URI}" "!^/images/(.*)\.(jpg|png|jpeg|gif)$"
RewriteCond "%{REQUEST_URI}" "!^/css/(.*)\.css$"
RewriteCond "%{REQUEST_URI}" "!^/js/(.*)\.js$"
RewriteRule ^(.*)$ /index.php/$1 [L]
# AccepthPathInfo on

And AcceptPathInfo will need to be enabled if it isn't already.

7
  • This seems very encouraging, but I'm not clear on what the .htaccess will look like, exactly. The new rewrite rule, then the conditions and the original rule? Commented Jan 9, 2017 at 19:35
  • Yes. I've updated my answer with a complete example. Note that the conditions (RewriteCond) and the final RewriteRule are really 1 statement/rule. The RewriteCond directives apply to the first RewriteRule directive that follows.
    – MrWhite
    Commented Jan 9, 2017 at 19:40
  • Just wondering... have you considered just making an exception for files that "exist", rather than listing specific file patterns?
    – MrWhite
    Commented Jan 9, 2017 at 19:42
  • It still gives the 500 error with looping. I'm starting to wonder whether something more basic is wrong, that the same rules behave differently on this server? Commented Jan 9, 2017 at 19:44
  • One purpose of the rules is to avoid serving a file merely because it's present and requested - it must also be on the whitelist. This prevents the client retrieving non-whitelisted files or finding out about the real directory structure. Otherwise something like Apache's FallbackResource would suffice. Commented Jan 9, 2017 at 19:52

Your Answer

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

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