Using Environment Variables in RewriteRule match - easier to maintain and smaller - .htaccess

I have a load of rewrites to do for a very old site which has had many different directory structures. There are about 140 redirects needed, and here is an example of the structure:
# First file set to first destination
RewriteRule ^(dir-one|dir-two|dir-three)\/(file-one|file-two|file-three)\/?$ /destination-one [R=301,L]
# Second file set to second destination
RewriteRule ^(dir-one|dir-two|dir-three)\/(file-four|file-five|file-six)\/?$ /destination-two [R=301,L]
# Third file set to third destination
RewriteRule ^(dir-one|dir-two|dir-three)\/(file-seven|file-eight|file-nine)\/?$ /destination-three [R=301,L]
# etc etc... Same sort of thing another 137 times!
As you can see there is a lot of repeated info here in the initial directory match (dir-one|dir-two|dir-three).
I would like - if possible to make the list of directories easily updatable and wondered if Environment Variables would work. Here's what I've tried:
# setting the dir names in the ENV:
RewriteRule .* - [E=DIRS:"dir-one|dir-two|dir-three"]
# First file set to first destination
RewriteRule ^(%{ENV:DIRS})\/(file-one|file-two|file-three)\/?$ /destination-one [R=301,L]
# Second file set to second destination
RewriteRule ^(%{ENV:DIRS})\/(file-four|file-five|file-six)\/?$ /destination-two [R=301,L]
# Third file set to third destination
RewriteRule ^(%{ENV:DIRS})\/(file-seven|file-eight|file-nine)\/?$ /destination-three [R=301,L]
This is not working. I know the ENV is being set (see screenshot below) but they don't seem to be used in the RewriteRules. What am I doing wrong, is this even possible, is there a better way?
If this works I'd probably expand it to have sets of files saved as ENVs so file sets can be updated in one place too.
Thanks!
Some clarification...
My example didn't show what I'm trying to achieve very well. Here's a more 'real world' example:
# What I have now. Note the repeated 'sections|categories|areas' part:
RewriteRule ^(sections|categories|areas)\/(car|plane|train)\/?$ /transport [R=301,L]
RewriteRule ^(sections|categories|areas)\/(pig|cow|goat|kangaroo)\/?$ /animals [R=301,L]
RewriteRule ^(sections|categories|areas)\/(cheese|fish|turnips)\/?$ /food [R=301,L]
# etc etc... Same sort of thing another 137 times!
# What I'm hoping is possible. It will allow me to add or edit the
# first match (sections|categories|areas) in one place and not 140 places.
RewriteRule .* - [E=DIRS:"sections|categories|areas"]
RewriteRule ^(%{ENV:DIRS})\/(car|plane|train)\/?$ /transport [R=301,L]
RewriteRule ^(%{ENV:DIRS})\/(pig|cow|goat|kangaroo)\/?$ /animals [R=301,L]
RewriteRule ^(%{ENV:DIRS})\/(cheese|fish|turnips)\/?$ /food [R=301,L]
So the first match is always the same, for every rewrite. The different mapping is between the second match and the destination. i.e all the vehicles go to /transport, all the animals go to /animals etc etc.
My main aim here is to avoid repeating that first match 140 times. If my aim is stupid, please say so :)
Thank you!

Match against ENV in RewriteCond not in RewriteRule like this :
RewriteRule ^(dir-one|dir-two|dir-three)/(.*)$ - [E=DIRS:$1]
RewriteCond %{ENV:DIRS} ^dir-one
RewriteRule ^([^\/]+)\/(file-one|file-two|file-three)\/?$ /destination-one [R=301,L]
RewriteCond %{ENV:DIRS} ^dir-two
RewriteRule ^([^\/]+)\/(file-four|file-five|file-six)\/?$ /destination-two [R=301,L]
RewriteCond %{ENV:DIRS} ^dir-three
RewriteRule ^([^\/]+)\/(file-seven|file-eight|file-nine)\/?$ /destination-three [R=301,L]
Moreover , if you only want to redirect them to new distination and there is no relationship between the old & new uri,as i see , make them like this :
RewriteRule ^(dir-one|dir-two|dir-three)/(.*)/?$ - [E=DIRS:$1-$2]
RewriteCond %{ENV:DIRS} ^dir-one\-(file-one|file-two|file-three)
RewriteRule ^ /destination-one [R=301,L]
RewriteCond %{ENV:DIRS} ^dir-two\-(file-four|file-five|file-six)
RewriteRule ^ /destination-two [R=301,L]
RewriteCond %{ENV:DIRS} ^dir-three\-(file-seven|file-eight|file-nine)
RewriteRule ^ /destination-three [R=301,L]
Update :
According to your update try this :
RewriteEngine on
RewriteRule ^(sections|categories|areas)/(.*)/?$ - [E=DIRS:$1-$2]
RewriteCond %{ENV:DIRS} ^(.+)\-(car|plane|train)
RewriteRule ^ /transport [R=301,L]
RewriteCond %{ENV:DIRS} ^(.+)\-(pig|cow|goat|kangaroo)
RewriteRule ^ /animals [R=301,L]
RewriteCond %{ENV:DIRS} ^(.+)\-(cheese|fish|turnips)
RewriteRule ^ /food [R=301,L]
Note: clear browser cache then test

Related

htaccess rewrite rule with 2 variables (1 optional)

I'm aware this has already been asked, but I've tried countless solutions but I'm still not getting the desired result.
I've a page which shows a list of names and the variables data are the starting letter (variable l which can also be a number) and the page number (page_no) and the page number is optional as it should not be displayed in the page 1.
The original URL is the following
https://www.example.com/allnames.php?l=A&page_no=1
I've the following rule
RewriteCond %{QUERY_STRING} ^l=([\w]+)$
RewriteRule ^allnames.php$ /allnames/%1? [R=301,L]
which works fine for page 1, but it doesn't include the page_no variable, so I tried this one but I'm unable to understand what's wrong.
RewriteCond %{QUERY_STRING} ^l=([\w]+)?page_no=([0-9]+)$
RewriteRule ^allmovies.php$ /allmovies/%1/%2? [R=301,L]
the result expected would be, for example
allnames/A or allnames/A/1 for letter A, page 1
allnames/S/8 for letter S, page 8
And the following is the content of the htaccess file
RewriteEngine On
Options +FollowSymLinks -MultiViews
# to prevent loops
RewriteCond %{ENV:REDIRECT_STATUS} !200
#remove index.php
RewriteCond %{REQUEST_URI}::$1 ^(/.+)/(.*)::\2$
RewriteRule ^(.*) - [E=BASE:%1]
# Sets the HTTP_AUTHORIZATION header removed by apache
RewriteCond %{HTTP:Authorization} .
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^index\.php(/(.*)|$) %{ENV:BASE}/$2 [R=301,L]
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule .? - [L]
RewriteRule .? %{ENV:BASE}/index.php [L]
# allnames.php
RewriteCond %{QUERY_STRING} ^l=([\w]+)$
RewriteRule ^allnames.php$ /allnames/%1? [R=301,L]
RewriteRule allnames/ /allnames.php?l=$1&page_no=$2 [L]
RewriteCond %{QUERY_STRING} ^l=([\w]+)?page_no=([0-9]+)$
The regex in the condition (CondPattern) does not allow for the & delimiter between URL parameters. You are also allowing the l parameter value to be entirely omitted (which would result in an ambiguous URL) or contain multiple characters (which you've stated should be a single "letter").
You would seem to need something like the following instead:
RewriteCond %{QUERY_STRING} ^l=([\w])&page_no=([0-9]+)$
But note that this will fail if the l parameter value contains more than one character (or is omitted entirely).
the result expected would be, for example
allnames/A or allnames/A/1 for letter A, page 1
Note that you would still need your first rule (without the page_no URL param) to get allnames/A. You could instead make page_no optional in the second rule, but you would still get a trailing slash (ie. allnames/A/) if that is an issue?

ReWrite rule not working with two parametrs

im quite new to rewrite rules.
I can manage with one variable and thats it.
I have webpage Where the rewriterule is:
RewriteCond %{HTTP_HOST} ^www\.someserver\.com$
RewriteRule ^/?$ "http\:\/\/someserver\.com\/" [R=301,L]
RewriteRule ^(\d+)*$ ./index.php?comp=$1
RewriteRule ^(\d+)*/$ ./index.php?comp=$1
And it all work fine as it should. But now as i want 1 more variable into URL
i cant get it to work.
Right now the $1 is only numbers.
someserver.com/1554886
But i want two variable.
someserver.com/1554886-SOMENAME-WHATEVER-WORD-AND-HOW-MANY
But it wont show.
i tried smth like this:
RewriteRule ^([^-]*)-([^-]*)$ ./index.php?comp=$1&string=$2 [L]
How do i get it to work?
Do i have to make some changes in the php side as well?
everything what comes after the number part of the URL is there only for
SEO, the number is Unique
You need one more rule to handle two parameters:
RewriteEngine On
RewriteCond %{HTTP_HOST} ^www\.(someserver\.com)$ [NC]
RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L,NE]
RewriteRule ^(\d+)/?$ index.php?comp=$1 [L,QSA]
RewriteRule ^(\d+)-(.+)/?$ index.php?comp=$1&string=$2 [L,QSA]

.htaccess RewriteRule to remove ? following hostname

I have an htaccess file with some rules, and I now want to add another rule which strips a part of the URL such that
www.mydomain.com/?generations/anything
becomes
www.mydomain.com/anything
(and anything means any other characters). I can make it work without the ?, but I can't seem to match/remove the ?. I tried:
1 failed:
RewriteRule ^\?generations/(.*)$ /$1 [L]
2 failed:
RewriteCond %{QUERY_STRING} ^generations/(.*)$
RewriteRule ^generations/(.*)$ $1 [L]
3 failed:
RewriteCond %{QUERY_STRING} ^generations/(.*)$
RewriteRule ^([^?]*)?generations/(.*)$ $2%1 [L]
4 failed:
RewriteRule ^([^?]*)?generations/(.*)$ $1$2 [QSA,L]
Can someone create a working rule - and explain why my third attempt above is not working? I can potentially see problems with the first two, but the third and fourth should work...
You're getting there with 3, but the query string is not included in a RewriteRule match. So you just want to match the empty string:
RewriteCond %{QUERY_STRING} ^generations/(.*)$
RewriteRule ^$ %1? [L]
But you also have it the wrong way round I think, if you want /anything to be the URL that is visited in the browser. You actually want just this:
RewriteRule ^(.*)$ ?generations/$1 [L]

Is it possible to have nested ReWrite Conditions in htaccess?

I have the following situation where I want to have nested ReWrite Conditions, and I have come across a situation where I am not able to see a proper documentation for the same.
RewriteCond %{REQUEST_URI} ^(robots.txt|favicon|ico)$ [NC]
RewriteRule . - [S=3]
# Nested ReWrite Condition
RewriteCond %{HTTP_HOST} ^www
RewriteRule .* http://%{SERVER_NAME}%{REQUEST_URI} [R=301,L]
RewriteRule .* http://%{SERVER_NAME}%{REQUEST_URI_1} [R=301,L]
RewriteRule .* http://%{SERVER_NAME}%{REQUEST_URI_2} [R=301,L] # and so on
Therefore, the question comes up that whether the number of skip rules will comprise of the nested ReWrite Conditions, that is, in this case, should the number of skipped rewrite rules be 4 or 5(if including the rewrite condition).
RewriteCond %{REQUEST_URI} ^(robots.txt|favicon|ico)$ [NC]
RewriteCond %{HTTP_HOST} !^www
RewriteRule .* - [S=3]
# the following rules are run only if the first 2 conditions don't match
RewriteRule .* http://%{SERVER_NAME}%{REQUEST_URI} [R=301,L]
RewriteRule .* http://%{SERVER_NAME}%{REQUEST_URI_1} [R=301,L]
RewriteRule .* http://%{SERVER_NAME}%{REQUEST_URI_2} [R=301,L]
notice the ! negation in the 2nd cond
documentation:
This technique is useful because a RewriteCond only applies to the
RewriteRule immediately following it. Thus, if you want to make a
RewriteCond apply to several RewriteRules, one possible technique is
to negate those conditions and add a RewriteRule with a [Skip] flag.
Okay as you only have posted an example, I show you an example how it works. It's with comments, but if you still don't find it speaking enough, there is a lot more explanation available here.
# Does the file exist?
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# Create an if-then-else construct by skipping 3 lines if we meant to
# go to the "else" stanza.
RewriteRule .? - [S=3]
# IF the file exists, then:
RewriteRule (.*\.gif) images.php?$1
RewriteRule (.*\.html) docs.php?$1
# Skip past the "else" stanza.
RewriteRule .? - [S=1]
# ELSE...
Rewri
This should solve your issue. If not, please update your example in the question so it's clear what you're missing.
And yes, it skips Rules and not Conditions.

mod_reweite htaccess not work with a point in var

I creating a mod_rewrite rule like this
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteCond %{SCRIPT_FILENAME}\.php !-f
RewriteRule ^(.*)$ ./page.php?key=$1
it works! But I want to do this:
www.site.com/var1.var2.var3
(variables are separated by point) separated by - or _ no problems, but if I put the point does not work why?
I assume that you are trying a rule like this
RewriteRule ^(.*).(.*).(.*)$ ./page.php?key=$1&key2=$2&key3=$3
but you should remember to escape the dots which you want to interpret as periods. There are many ways to right this but here is a simple way if you are new to .htaccess files:
RewriteEngine On
RewriteBase /
# Skip existing files or directories
RewriteCond %{SCRIPT_FILENAME} -d [OR]
RewriteCond %{SCRIPT_FILENAME} -f
RewriteRule ^ - [L]
# Add default PHP extension when appropriate
RewriteCond %{SCRIPT_FILENAME}.php -f
RewriteRule ^.* $0.php [L]
# Pick up 1,2 and 3 parameter versions
RewriteRule ^(.*?)\.^(.*?)\.(.*)$ page.php?key=$1&key2=$2&key3=$3 [QSA,L]
RewriteRule ^(.*?)\.(.*?)$ page.php?key=$11&key2=$2 [QSA,L]
RewriteRule ^(.*?)\.$ page.php?key=$1 [QSA,L]
Note:
the first rule uses "^" as a pattern which always matches and the "-" means don't replace it.
the second uses $0 which means the entire match pattern
rule 3-5 match the 3,2,1 parameter options
the [L] (last) flag means skip the rest of the rules
you will need the QSA flag is you use other parameters.
The reason for splitting the existance check in to is that you need to handle files such as CSS and image (PNG/GIF/PNG) files as well as your application syntax.

Resources