4

I'm trying to change the DocumentRoot to subfolder through .htaccess.

This works well...

RewriteCond %{REQUEST_URI} !subfolder/
RewriteRule ^(.*)$ /subfolder/$1

...however, I only want to do this when a specific query parameter (var=test) is set, so I have this:

RewriteCond %{QUERY_STRING} ^var=test$
RewriteCond %{REQUEST_URI} !subfolder/
RewriteRule ^(.*)$ /subfolder/$1

...but for some reason, this doesn't work.

Any suggestions as to what the problem could be?


UPDATE: Here is a full (simplified) example:

.htaccess

RewriteEngine on
RewriteCond %{QUERY_STRING} (.*(?:^|&))var=test((?:&|$).*)
RewriteCond %{REQUEST_URI} !sites/test/
RewriteRule ^(.*)$ /sites/test/$1

sites/test/index.php

<!DOCTYPE html>
<html>
<head>
    <title>Test</title>
    <link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
    <p>Test</p>
</body>
</html>

sites/test/styles.css

* {
    color: red;
}

Now try loading the page with http://example.com/?var=test. The Test text should be red, but it's not.

Remove the QUERY_STRING RewriteCond line, and it works.

But I need to be able to check for the query string somehow..

Apache Virtual Host (in case it's needed)

<VirtualHost *:80>

      ServerName www.example.com
      ServerAdmin admin@example.com

      DocumentRoot "/var/www/mainsite"
      <Directory />
              Options FollowSymLinks
              AllowOverride None
      </Directory>
      <Directory "/var/www/mainsite">
              Options Indexes FollowSymLinks MultiViews
              AllowOverride All
              Order allow,deny
              allow from all
      </Directory>

</VirtualHost>
  • 2
    Is var=test the only parameter? A better rule would be RewriteCond %{QUERY_STRING} (.*(?:^|&))var=test((?:&|$).*) if it isn't. See: wiki.apache.org/httpd/RewriteQueryString – Stephen Ostermiller Jan 10 '18 at 21:22
  • Yes that's right, var=test is the only parameter. Many thanks for the better RewriteCond. For some reason, the second code block above breaks CSS file paths, and I can't figure out why... – camursm Jan 10 '18 at 22:26
  • 1
    Please include an example of the complete URL you are requesting. Bear in mind also, that your condition checks for "subfolder/" anywhere in the request URL, you should be more specific. If you are going for the "can appear anywhere in the query string" approach, that @Stephen suggests, then that can be simplified to RewriteCond %{QUERY_STRING} (?:^|&)var=test(?:&|$) – MrWhite Jan 10 '18 at 22:26
  • Many thanks. I've updated my question above with a simplified example. – camursm Jan 10 '18 at 22:45
5

For some reason, the second code block above breaks CSS file paths, and I can't figure out why...

The only difference with the second code block is that the request for your CSS file(s) won't be rewritten. Whereas it will be for the first code block. Your CSS URL does not contain the var=test URL parameter. Only the main page URL is being rewritten, that contains the URL parameter.

So, given a request for http://example.com/?var=test then /styles.css will be requested (and not rewritten), instead of /sites/test/styles.css, which would seem to be the intention.


UPDATE#1: As mentioned in comments, it probably wouldn't be practical to try and resolve this with .htaccess. The request for the CSS file is an entirely separate request. There is no concept of a "session" here (or rather "page request"). You would need to resort to cookies in order to maintain state - which adds complexity (would need to be unset for other requests) and may not be reliable. If you wanted to stay with the query string trigger then you could solve this in PHP by checking for the var=test query string and manually appending this to the CSS URL in your HTML. (Or even, appending the correct (full) URL-path to the CSS URL in your HTML - although maybe you're trying to hide the real URL path from your users, so that may not be desirable.)

For example:

<?php
$resourceQueryString = '';
if (isset($_GET['var']) && ($_GET['var'] == 'test')) {
    $resourceQueryString = '?var=test';
}
?>
<!DOCTYPE html>
<html>
<head>
    <title>Test</title>
    <link rel="stylesheet" type="text/css" href="styles.css<?=$resourceQueryString?>">
</head>
<body>
    <p>Test</p>
</body>
</html>

This naturally checks for var=test anywhere in the query string, not literally just ?var=test.

However, if you have 100s of static pages then that may not be practical either.


UPDATE#2: If you did want to try the "cookie" approach in .htaccess, then you could perhaps do something like the following (untested):

RewriteEngine on

# If query string passed then set a (session) cookie...
RewriteCond %{QUERY_STRING} (?:^|&)var=test(?:&|$)
RewriteRule ^ - [CO=rewrite_trigger:1:.example.com]

# If no query string and a ".php" request then unset cookie
RewriteCond %{QUERY_STRING} !var=test
RewriteRule \.php$ - [CO=rewrite_trigger:0:.example.com:-1]

# Rewrite request if query string OR cookie is set
RewriteCond %{QUERY_STRING} (?:^|&)var=test(?:&|$) [OR]
RewriteCond %{HTTP_COOKIE} rewrite_trigger=1
RewriteCond %{REQUEST_URI} !^/sites/test/
RewriteRule (.*) /sites/test/$1 [L]

Change .example.com to match your domain.

Bear in mind that the cookie is not readable on the request the cookie is actually set. The cookie can only be read on subsequent requests, hence the need to check for either the query string OR the cookie.

Note that this is currently untested (may test this later if I have time). In fact, thinking about it, there may be a problem with the first .php request without the query string after a .php request with the query string (as the cookie will still be set). May need an environment variable to resolve this? Or maybe check for (query string AND .php) OR (cookie AND no .php) in order to rewrite?

  • Aa, that is right - many thanks for it explaining it so well. – camursm Jan 11 '18 at 7:06
  • Is there any way to tell htaccess "if 'main request' contains 'var=test' -and- this is a request for a CSS file, then rewrite the URL"? – camursm Jan 11 '18 at 7:08
  • Or rather, "if initial request contains "var=test", then all subsequent requests in this session should be rewritten"? – camursm Jan 11 '18 at 7:41
  • 1
    It probably wouldn't be practical to try and do this with .htaccess. The request for the CSS file is an entirely separate request. There is no concept of a "session" here (or rather "page request"). You would need to resort to cookies in order to maintain state - which adds complexity and may not be reliable. If you wanted to stay with the query string trigger then you could solve this in PHP by checking for the var=test query string and manually appending this to the CSS URL in your HTML. I'll update my answer. – MrWhite Jan 11 '18 at 10:13
  • 1
    You're welcome. I've added some example code for "the cookie approach" - although this is currently untested and may need some work to debug (as mentioned in my answer)? I would add, however, that using cookies for this does "feel" wrong. It would only working in a development/testing environment (which I assume is what you are doing) - since bots generally do not set cookies so would be unable to spider/render your site correctly. – MrWhite Jan 11 '18 at 11:26

Your Answer

By clicking "Post Your Answer", you agree to our terms of service, privacy policy and cookie policy

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