Multiple RewriteRules for single RewriteCond in .htaccess - apache

I have following command in my .htaccess
RewriteCond %{HTTP_HOST} ^(www\.)?([a-z0-9-]+)\.example\.com [NC]
RewriteRule ^(.*?)-([a-z]+) %2/$1.$2 [L]
RewriteRule ^(.*?)-([0-9]+)([a-z]) %2/$1$3.$2 [L]
%2 is not working in second and later lines.
Can I define any variable for %2 and use it in all RewriteRule commands?
Following command works
RewriteCond %{HTTP_HOST} ^(www\.)?([a-z0-9-]+)\.example\.com [NC]
RewriteRule ^(.*?)-([a-z]+) %2/$1.$2 [L]
RewriteCond %{HTTP_HOST} ^(www\.)?([a-z0-9-]+)\.example\.com [NC]
RewriteRule ^(.*?)-([0-9]+)([a-z]) %2/$1$3.$2 [L]
But I want use %2 for multiple rule line without duplicating condition.

You can use the RewriteRule flag S|skip to tie multiples RewriteRules to a single RewriteCond (or to a set of RewriteConds). Here is an example that applies one Cond to three Rules:
RewriteCond %{HTTP_HOST} !^www.mydomain.com$
# skip rules if NOT within domain - only way to tie multiple rules to one cond
RewriteRule .? - [S=3]
RewriteRule ^path1(/.*)$ /otherpath1$1
RewriteRule ^path2(/.*)$ /otherpath2$1
RewriteRule ^path3(/.*)$ /otherpath3$1
To change an existing Cond to work for multiple Rules you have to:
Negate the condition (prepend it with !)
If you have multiple RewriteConds: Change logical ANDs in the Conds to ORs and vice versa.
Add a skipping rewrite rule in front of all rules that you want to tie to the condition(s). Set the S parameter to the number of Rule lines to be skipped.
Please be aware that it is not possible to use any backreferences that point back to the RewriteCond (like %1) in the skipped Rules. These are only accessible in the skipping RewriteRule.

The variable must be saved as an Apache var, then that can be used without repeated conditions.
Saving in Apache variables are shown in second line. Usage of saved vars in 3th and 4th lines.
RewriteCond %{HTTP_HOST} ^(www\.)?([a-z0-9-]+)\.example\.com [NC]
RewriteRule .? - [E=Wa:%1,E=Wb:%2]
RewriteRule ^(.*?)-([a-z]+) %{ENV:Wb}/$1.%{ENV:Wb} [L]
RewriteRule ^(.*?)-([0-9]+)([a-z]) %{ENV:Wb}/$1$3.$2 [L]

Clearly, this isn’t much fun, especially as things grow and become more complex. However, there is a less well known option of the RewriteRule statement, that tells apache to skip the next N number of RewriteRule statement. [S=N]. So instead of checking each time if the request is NOT a file AND is NOT a directory, we could do this:
Code block
#
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule .* - [S=3]
RewriteRule ^([^./]+)/$ http://www.santweb.co.uk/$1 [L]
RewriteRule ^([^./]+)/([^./]+)/$ http://www.santweb.co.uk/$1/$2 [L]
RewriteRule ^([^./]+)/([^./]+)/([^./]+)/$ http://www.santweb.co.uk/$1/$2/$3 [L]
#
I found this, from: http://www.sant-media.co.uk/2010/03/applying-rewritecond-to-multiple-rewriterule-in-htaccess/
I think it helpfull

From Apache 2.4 you can use the <If> directive:
RewriteEngine On
<If "%{HTTP:Upgrade} == 'websocket'">
RewriteRule /nidoran/(.*) ws://localhost:8080/nidoran/$1 [P,L]
RewriteRule /kakuna/(.*) ws://localhost:8081/kakuna/$1 [P,L]
RewriteRule /porygon/(.*) ws://localhost:8082/porygon/$1 [P,L]
</If>
http://httpd.apache.org/docs/2.4/expr.html#examples

Related

Multiple RewriteRule to one RewriteCond

On the side I have about 400,000 subdomains. in view of the above, some calls also operate subdomain level, e.g..
subdomain1.example.com/some_action/
Such actions that should be performed only from the domain have 27.
Htaccess file I created a rule:
RewriteEngine On
RewriteCond %{HTTP_HOST} ^(www.)?example.com
RewriteRule ^some_action1/?$ index.php?some_action1 [L]
If i add this line
RewriteRule ^some_action2/?$ index.php?some_action2 [L]
not work in the same way as for some_action3, etc.
I have added for each action separately
RewriteCond %{HTTP_HOST} ^(www.)?example.com
Can you somehow skip to harmonize?
Each RewriteCond condition only applies to the immediately following RewriteRule. That means if you have a bunch of rules, you have to duplicate the conditions. If you really don't want to do that for some reason, you could use a negate at the very beginning of all your rules. This may or may not be a good solution since it could affect how you make future changes to the rules:
RewriteEngine On
RewriteCond %{HTTP_HOST} !^(www.)?example.com
RewriteRule ^ - [L]
RewriteRule ^some_action1/?$ index.php?some_action1 [L]
RewriteRule ^some_action2/?$ index.php?some_action2 [L]
RewriteRule ^some_action3/?$ index.php?some_action3 [L]
etc...
So the first rule checks for the negative of the host being example.com, and skips everything. Then you can add all your rules without having to worry about that condition.
However, if your "some_action" is always going to be part of the GET parameters, you can maybe just use a single rule:
RewriteEngine On
RewriteCond %{HTTP_HOST} ^(www.)?example.com
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)/?$ index.php?$1 [L]

Mod ReWrite to remove component of URL

I must be an idiot because I just can't work this bit out.
I've got a URL:
www.site.com.au/products/product-name.html
I need to redirect these to:
www.site.com.au/product-name.html
All the links are dynamic, the folder doesn't exist. What ReWrite rule do I use to accomplish this?
This is what I've got so far:
RewriteCond %{HTTP_HOST} ^(www|test)\.site\.com\.au
RewriteCond %{REQUEST_URI} ^(/products/)
RewriteCond %{REQUEST_URI} [A-Z]
RewriteRule ^.+\.html$ ${lc:%{REQUEST_URI}} [NC,R=301,L]
Just need to add the bit to remote /products
Thanks.
RewriteRule ^products(/.*)$ http://www.site.com.au$1 [L, R=301]
This replaces everything you listed, except for the first RewriteCond (to match the domain, though if your VirtualHost only answers on those two domains, you can exclude that RewriteCond to simplify it).
RewriteRules are matched first before Apache looks at the RewriteConds, so if you can do the match in the RewriteRule itself it greatly simplifies things. Just for your future reference, if you did need to match in the RewriteCond, it would look something like this:
RewriteCond %{REQUEST_URI} ^/products(/.*)$
RewriteRule ^.*$ http://www.site.com.au%1 [L, R=301]
Note the %1 for matching what's in the parentheses in the RewriteCond vs. the $1 for matching what's in the RewriteRule.
EDIT: Per your comment, the following modification should force lowercase. I haven't had to do that myself, but per this Apache documentation it's an internal function via RewriteMap. Based on your original code it looks like maybe you already have the RewriteMap definition elsewhere. If not, I've included it here.
RewriteMap lc int:tolower
RewriteCond %{REQUEST_URI} ^/products(/.*)$ [NC]
RewriteRule ^.*$ http://www.site.com.au${lc:%1} [L, R=301]
OK, I really don't know enough about Apache Rewrite to figure out the exact formatting. But after much ado these are the results that worked:
# Lowercase all /products/
RewriteCond %{HTTP_HOST} ^(www)\.site\.com\.au
RewriteCond %{REQUEST_URI} ^/products/.+\.html
RewriteCond %{REQUEST_URI} [A-Z]
RewriteRule ^ ${lc:%{REQUEST_URI}} [R=301,L]
# Lowercase all /products/ and strip products/ subfolder
RewriteCond %{HTTP_HOST} ^(www)\.site2\.com\.au
RewriteCond %{REQUEST_URI} ^/products/.+\.html
RewriteCond %{REQUEST_URI} [A-Z]
RewriteRule ^products/(.+\.html)$ /${lc:$1} [R=301,L]
Thanks,
Dom

rewriting rules, excluding condition

Have a following rewriting rule
RewriteEngine On
RewriteCond %{REQUEST_URI} !/(js|css|images)/
RewriteRule section/([0-9a-zA-Z_-]+)$ /index.php?controller=section&method=index&param=$1
RewriteRule category/([0-9a-zA-Z_-]+)$ /index.php?controller=category&method=products&param=$1
RewriteRule ([0-9a-zA-Z_-]+)/([0-9a-zA-Z_-]+)/(.*)$ /index.php?controller=$1&method=$2&param=$3
RewriteRule ([0-9a-zA-Z_-]+)/([0-9a-zA-Z_-]+)$ /index.php?controller=$1&method=$2
Now, when I call for /js/jquery.js, I get the file fine. But if the file is one folder level deeper, eg. /js/fancybox/jquery.fancybox-1.3.4.pack.js, it doesn't. It has something to do with section and category rules as everything works fine when I remove those two lines
You need to repeat the condition for each rule, the condition only applies to the immediately following rule. The reason why it works when you remove the 2 section and category rules is because the condition is being applied to the 3rd rule (which is what's messing you up). You need the condition repeated for each rule:
RewriteEngine On
RewriteCond %{REQUEST_URI} !/(js|css|images)/
RewriteRule section/([0-9a-zA-Z_-]+)$ /index.php?controller=section&method=index&param=$1
RewriteCond %{REQUEST_URI} !/(js|css|images)/
RewriteRule category/([0-9a-zA-Z_-]+)$ /index.php?controller=category&method=products&param=$1
RewriteCond %{REQUEST_URI} !/(js|css|images)/
RewriteRule ([0-9a-zA-Z_-]+)/([0-9a-zA-Z_-]+)/(.*)$ /index.php?controller=$1&method=$2&param=$3
RewriteCond %{REQUEST_URI} !/(js|css|images)/
RewriteRule ([0-9a-zA-Z_-]+)/([0-9a-zA-Z_-]+)$ /index.php?controller=$1&method=$2

Apache rewrite merge rules

Earlier the admin RewriteRule was effecting scripts/admin.js until I added the RewriteCond.
Then I noticed that scripts/members.js was being effected by the same rule with the member context.
I added the exact same RewriteCond after as is below...
RewriteEngine on
RewriteCond %{REQUEST_URI} !\.(css|js|zip)$
RewriteRule .*/admin(.+) admin$1 [QSA]
RewriteCond %{REQUEST_URI} !\.(css|js|zip)$
RewriteRule .*/members(.+) members$1 [QSA]
Instead of having to manually add a RewriteCond every time to deal with issues how can I merge these rules please?
RewriteRule .*/admin(.+) admin$1 [QSA]
RewriteRule .*/blog(.+) blog$1 [QSA]
RewriteRule .*/forums(.+) forums$1 [QSA]
You can merge your three checks into one like this:
RewriteCond %{REQUEST_URI} !\.(css|js|zip)$
RewriteRule .*/(admin|blog|forms)(.+) $1$2 [QSA]
But it does seem like a rule to simply stop processing for js/css files will be a better option for you, unless you actually need rules applied to them for some reason.
You can invert the condition and make a pass-through rule that essentially tells mod_rewrite to stop. This way, you have 1 RewriteCond, 1 extra RewriteRule that is applied via the condition, and then everything else:
# Just one of these, note that there is no !
RewriteCond %{REQUEST_URI} \.(css|js|zip)$
# pass through, if the request is for a css/js/zip, stop rewriting
RewriteRule ^ - [L]
# otherwise, do some rewriting
RewriteRule .*/admin(.+) admin$1 [QSA]
RewriteRule .*/blog(.+) blog$1 [QSA]
RewriteRule .*/forums(.+) forums$1 [QSA]
This will, however, affect any other rules you have that could apply to requests to css/js/zip, because this will cause all requests for those extensions to bypass all rewriting.

Simulating a 2-level If-Else using RewriteCond

I'm trying to get my head around RewriteCond, and want to rewrite any requests either to a static html page (if it exists), or to a specific index.php (so long as the requested file doesn't exist).
To illustrate the logic:
if HTTP_HOST is '(www\.)?mydomain.com'
if file exists: "/default/static/{REQUEST_URI}.html", then
rewrite .* to /default/static/{REQUEST_URI}.html
else if file exists: {REQUEST_FILENAME}, then
do not rewrite
else
rewrite .* to /default/index.php
I don't seem to have much trouble doing it when I don't need to test for the HTTP_HOST. Ultimately, this one .htaccess file will be handling requests for several domains.
I know I could get around this with vhosts, but I'd like to figure out how to do it this way.
I'm not too familiar with some of the other flags, will any of them be of use here (like chain|C, next|N or skip|S)?
Thanks in advance!
UPDATE: I've managed to do it, but would appreciate alternatives:
RewriteCond %{HTTP_HOST} ^(domainA|domainB)\..* [NC]
RewriteCond %{DOCUMENT_ROOT}/%1/static/%{REQUEST_URI}.html -f
RewriteRule (.*)? /%1/static/$1.html [NC,L]
RewriteCond %{HTTP_HOST} ^(domainA|domainB)\..* [NC]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule .* /%1/index.php [L,QSA]
UPDATE #2: With help from Gumbo's answer, came up with another. I like that this would would require less maintenance in the case of added domains. (Thanks Gumbo!)
Are there any reasons why I shouldn't set ENV variables?
RewriteCond %{HTTP_HOST} ^(domainA|domainB)\..*$ [NC]
RewriteRule ^ - [E=APP:%1]
RewriteCond %{DOCUMENT_ROOT}/%{ENV:APP}/static/%{REQUEST_URI}.html -f
RewriteRule (.*)? /%{ENV:APP}/static/$1.html [NC,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule .* /%{ENV:APP}/index.php [L,QSA]
I would probably do it like this:
RewriteCond %{HTTP_HOST} !^(www\.)?example\.com$
RewriteRule ^ - [S=2]
RewriteCond %{DOCUMENT_ROOT}/default/static%{REQUEST_URI}.html -f
RewriteRule !^default/static/ default/static%{REQUEST_URI}.html [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule !^default/static/ default/index.php [L]
This is similar to your updated example. Except that the first rule will skip the following two rules if the host is not appropriate. And the RewriteRule patterns exclude any path that starts with /default/static/. But your rules are already pretty good.