2

Would there be any way to continuously repeat execution of a single RewriteRule in .htaccess?

For example:

RewriteEngine On

# ...

RewriteCond %{QUERY_STRING} (.*?)(?:^|&)q=[^&]*(.*)
RewriteRule (.*) $1?%1%2

# ...

This rule will only be executed once before moving on to other rules. However, it could certainly match more than once:

Input:        /?a=a&q=q&b=b&q=q&c=c
Expected: /?a=a&b=b&c=c
Actual:      /?a=a&b=b&q=q&c=c

The closest way to do this seems to be the [N] flag, but this only works if the rule is at the very top of the file. Unfortunately, this is not feasible if the rule relies on other rules being executed beforehand.

An answer to a similar question suggests that it is not possible, but it never addresses the question directly.


To add more context, the end goal is to essentially encode a query string:

RewriteEngine On

# ...

RewriteCond %{REQUEST_URI} ^/folder/(.+)
RewriteRule .? - [E=QUERY:%1,S=1]
RewriteRule .? - [S=2]
# Repeat the next rule as many times as possible
RewriteCond %{ENV:QUERY} (.*?)&(.*)
RewriteRule .? - [E=QUERY:%1\%26%2]
RewriteRule .? /folder?query=%{ENV:QUERY}

# ...

This would result in the following:

Input:        /folder/a&b&c
Expected: /folder?query=a%26b%26c
Actual:      /folder?query=a%26b&c

Anonymous
  • 11,748
  • 6
  • 35
  • 57

2 Answers2

1

However, I'm still curious if there would be a general way to loop on one rule continuously in .htaccess.

First you should create an index.php in folder/ with this content to display the various PHP variables:

<?php
   phpinfo();
?>

Here is a generic way to loop the rules and replace each & by - using N flag.

RewriteEngine On

#   
# your other rules here
#

# recursively replace & by - and build query string
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{QUERY_STRING} ^(?:query=-?(.+))?$
RewriteRule ^(folder)/([^&]+)(?:&(.+))?$ $1/$3?query=%1-$2 [N,NC]

# **will never execute** due to previous rule looping
RewriteRule ^(folder)/(.+)$ $1/?query=$2 [NC,L,R]

The [N] flag causes the ruleset to start over again from the top, using the result of the ruleset so far as a starting point. Use with extreme caution, as it may result in loop.

anubhava
  • 761,203
  • 64
  • 569
  • 643
  • I appreciate the follow up, but this will still make .htaccess restart from the top of the file and test all prior conditions between each replacement of `&` (assuming that this is not at the top of the file). This seems extremely unnecessary and inefficient, so I was wondering if there was a way to continuously repeat a single rule without beginning from the top of the file after each individual replacement. If there were 500 or so `&` symbols, it would be much better to not try matching the previous rules that many times, instead just focusing on the one rule. It appears not to be possible. – Anonymous Jan 04 '16 at 20:51
  • By removing `L` flag you can stop all the rules to be evaluated again. See updated answer. – anubhava Jan 04 '16 at 21:12
  • But, wouldn't that mean that the entire file is reevaluated every time, so the same problem exists? – Anonymous Jan 04 '16 at 21:59
  • I tested with your snippet, and it appears to be executed multiple times. The first runthrough skips the second rule because of the `[^&]`. It continues repeating the whole file but only matching the first rule until the second rule matches as well. – Anonymous Jan 04 '16 at 22:47
  • If it really wasn't executing the second rule before the first rule completely finishes, the second rule could be reduced to `^(folder)/(.+)/?$ ...`. – Anonymous Jan 04 '16 at 22:48
  • Check updated answer with `N` flag. Last rule will never execute because `N` rule will loop until anything is left after `/folder/` – anubhava Jan 05 '16 at 17:37
  • 1
    Perfect, great idea to leave the trailing part of the string in the path itself. It would still be nice if there was a way for .htaccess to just repeat a single rule indefinitely without considering other rules, but at least this works well as a compromise. – Anonymous Jan 05 '16 at 20:34
0

At least in this specific case, there does appear to be a simple answer by using the [B] flag:

RewriteRule ^/folder/(.+) /folder?query=$1 [B]

However, I'm still curious if there would be a general way to loop on one rule continuously in .htaccess.

Anonymous
  • 11,748
  • 6
  • 35
  • 57