Create two environmental variables with one RewriteCond - apache

I have query strings like:
?request=/de/name/dieter
Using the below rule I catch the last part (dieter) and store it as an environmental variable. I'd also like to be able to store the first part of the URL (de) as an environmental variable, but I can't find a way to do that. Is it possible.
Rule at current:
RewriteCond %{QUERY_STRING} ^request=([a-z\/]*)name\/(.*?)([^/]{3})([^/]+) [NC]
RewriteRule .* - [E=N:%2%3/%4]
RewriteRule .* - [E=LANG:%1]

Generally, the "rewrite flags" [] portion of a RewriteRule accepts a comma-separated list of flags, and the RewriteRule docs do not explicitly say that you may not repeat a flag. Since E= is a flag, it should work to provide a comma-separated list of E= along with other rewrite flags (like [L] if needed)
RewriteCond %{QUERY_STRING} ^request=([a-z\/]*)name\/(.*?)([^/]{3})([^/]+) [NC]
RewriteRule .* - [E=N:%2%3/%4,E=LANG:%1]

Related

Grab more than 2 parameters using RewriteCond

I have a URL with 4 query parameters:
https://address.com/bin/servlet?firstKey=firstValue&secondKey=secondValue&thirdKey=thirdValue
I would to map this URL to path variables:
https://address.com/content/firstValue/secondValue/thirdValue
This is what I'm trying to do:
RewriteCond %{QUERY_STRING} &?firstKey=([^&]+)&?secondKey=([^&]+) [NC]
Then rewriting rule:
RewriteRule ^/bin/servlet /content/%1/%2 [QSD,PT]
This is working fine.
But the pain is: Third parameter, when I'm adding 3rd key like other 2 parameters it won't work.
RewriteCond %{QUERY_STRING} &?firstKey=([^&]+)&?secondKey=([^&]+)&?thirdKey=([^&]+) [NC]
RewriteRule ^/bin/servlet /content/%1/%2/%3 [QSD,PT]
There are a number of issues with your implementation, for example the usage of "&" in the condition. But in general there is no limit to the number of parameters you can detect and handle with your chose strategy.
I made a few adjustments, this probably is what you are looking for:
RewriteEngine on
RewriteCond %{QUERY_STRING} ^firstKey=([^&]+)&secondKey=([^&]+)&thirdKey=([^&]+)$
RewriteRule ^/?bin/servlet$ /content/%1/%2/%3 [R=301,QSD,L]
RewriteRule ^/?content/([^/]+)/([^/]+)/([^/]+)/?$ /bin/servlet?firstKey=$1&secondKey=$2&thirdKey=$3 [L,QSA]
This obviously only handles requests using exactly three parameters. It is possible to generalize such approach to be able to handle a dynamic number of parameters, but I would advise against it: often it is better to keep things less complex, so just add corresponding rules for 2 and 4 parameters, if required. That way you will be able to maintain and extend the solution easily.
UPDATE:
In your comment below you ask if it is possible to capture argument values specified in arbitrary order. Sure, everything is possible, but things get a bit more complex then. Take a look at this example:
RewriteEngine on
RewriteCond %{QUERY_STRING} (?:^|&)firstKey=([^&]+)(?:&|$)
RewriteRule ^/?bin/servlet$ - [E=FIRST_VAL:%1]
RewriteCond %{QUERY_STRING} (?:^|&)secondKey=([^&]+)(?:&|$)
RewriteRule ^/?bin/servlet$ - [E=SECOND_VAL:%1]
RewriteCond %{QUERY_STRING} (?:^|&)thirdKey=([^&]+)(?:&|$)
RewriteRule ^/?bin/servlet$ - [E=THIRD_VAL:%1]
RewriteRule ^/?bin/servlet$ /content/%{ENV:FIRST_VAL}/%{ENV:SECOND_VAL}/%{ENV:THIRD_VAL} [R=301,QSD,L]
RewriteRule ^/?content/([^/]+)/([^/]+)/([^/]+)/?$ /bin/servlet?firstKey=$1&secondKey=$2&thirdKey=$3 [L,QSA]

Rewrite Apache Query_String

I have a query string URL: http://localhost/cgi-bin/qgis_mapserv.fcgi?map=/home/qgis/project/map.qgs. I want to hind the map.qgs path in the variable MAP. Besides the map variable, there are some variables (version, request, service, etc).
Here is what I need:
RewriteRule:
Pattern: ^cgi-bin/qgis_mapserv.fcgi?map=map.qgs&(.)$
Substitution: ^cgi-bin/qgis_mapserv.fcgi?map=/home/qgis/project/map.qgs&(.)$
Find bellow my unsuccessful attempt:
RewriteEngine On
RewriteCond %{REQUEST_URI} ^/cgi-bin/qgis_mapserv.fcgi$
RewriteCond %{QUERY_STRING} ^map=([A-Za-z0-9.-_]+)$
RewriteRule ^cgi-bin/qgis_mapserv.fcgi?(.*)$ cgi-bin/qgis_mapserv.fcgi?$1
Note: The map variable can show up in the pattern uRL anywhere among the other variables.
I wonder what I am missing on the code above
You are almost there.
The reason why your rule isn't working is because you can't test queryString in pattern of a RewriteRule.
You need to change your rule's pattern to
RewriteRule ^cgi-bin/qgis_mapserv.fcgi$
With this change your htaccess rules will look like :
RewriteEngine On
RewriteCond %{REQUEST_URI} ^/cgi-bin/qgis_mapserv.fcgi$
RewriteCond %{QUERY_STRING} ^map=([A-Za-z0-9./-_]+)/map.qgs$
RewriteRule ^/?cgi-bin/qgis_mapserv.fcgi$ /cgi-bin/qgis_mapserv.fcgi?%1 [R,L]

htaccess environment variable based on query string without some pamameters

I want set a apache environment variable based on query string variable but without some parameters.
for example, i have this query string:
utm_source=foo&my_param=baz&utm_medium=bar&_t=9999
now, i want set a variable without utm_source and utm_medium:
my_param=baz&_t=9999
i know only the parameters to remove (utm_source & utm_medium)... the other are only an example...
i have wtrite this code:
RewriteEngine On
RewriteBase /
RewriteRule ^ - [E=CustomQueryString:%{QUERY_STRING}]
RewriteCond %{ENV:CustomQueryString} ^(.*)&?(utm_source|utm_medium)=[^&]+&?(.*)$ [NC]
RewriteRule ^ - [E=CustomQueryString:%1%3]
the output is
utm_source=foo&my_param=baz&_t=9999
why utm_source param persist?
i have also tried:
RewriteRule ^ - [E=CustomQueryString:%{QUERY_STRING}]
RewriteCond %{ENV:CustomQueryString} ^(.*)&?utm_source=[^&]+&?(.*)$ [NC]
RewriteRule ^ - [E=CustomQueryString:%1%2]
RewriteCond %{ENV:CustomQueryString} ^(.*)&?utm_medium=[^&]+&?(.*)$ [NC]
RewriteRule ^ - [E=CustomQueryString:%1%2]
the output is right!
a=b&my_param=baz&_t=9999
i don't undertand why this version not work!
RewriteCond %{ENV:CustomQueryString} ^(.*)&?(utm_source|utm_medium)=[^&]+&?(.*)$ [NC]
RewriteRule ^ - [E=CustomQueryString:%1%3]
This condition
RewriteCond %{ENV:CustomQueryString} ^(.*)&?(utm_source|utm_medium)=[^&]+&?(.*)$ [NC]
doesn't remove both utm_source and utm_medium, because it matches only one of them.
.* matches the longest possible string, including one of utm_source or utm_medium, whichever comes first. See also Repetition with Star and Plus, especially section Watch Out for The Greediness! and following, for a detailed explanation.
utm_source|utm_medium means in plain english: match either utm_source or utm_medium. So the regular expression matches
(.*) - %1, including utm_source
(utm_source|utm_medium)=[^&]+ - utm_medium=...
(.*) - %3, everything else after utm_medium
The second variant with two conditions and rules works, because first you remove utm_source=..., and then pass the remaining string to the second condition. There the utm_medium=... part is removed, and finally you have the desired string without both utm_source and utm_medium.
You may enclose the test string with &s. This guarantees, that you have always an ampersand before and after the utm_source=... part and allows to simplify the regular expression a little bit.
RewriteCond &%{ENV:CustomQueryString}& ^(.*)&utm_source=.+?&(.*)$ [NC]
Same goes for the second RewriteCond with utm_medium.
You can use these 2 rule to remove both query parameters:
RewriteRule ^ - [E=CustomQueryString:%{QUERY_STRING}]
# remove utm_source from query string and set env var
RewriteCond %{ENV:CustomQueryString} ^(.*&)?utm_source=[^&]*(?:&(.*))?$ [NC]
RewriteRule ^ - [E=CustomQueryString:%1%2]
# remove utm_medium from query string and reset env var
RewriteCond %{ENV:CustomQueryString} ^(.*&)?utm_medium=[^&]*(?:&(.*))?$ [NC]
RewriteRule ^ - [E=CustomQueryString:%1%2]
If we start with URL as /?utm_source=foo&my_param=baz&utm_medium=bar&_t=9999
After 2nd rule we will have:
$_SERVER["CustomQueryString"] becomes `my_param=baz&_t=9999`

Escape string within RewriteRule

I am currently chewing on this problem: I am reworking a sort of CMS system, the result will be that URLs will look entire differently, but I want to save links and bookmarks, so I scrathced this .htaccess file together:
RewriteEngine on
# Rewrite old links to match with new URL syntax:
# universal rewrite
RewriteCond %{REQUEST_URI}~%{QUERY_STRING} ^(.*?\/*)? (viewuser|viewstory|reviews|news)\.php~(.+)$ [NC]
RewriteRule ^(.+).php$ %1?action=redirect&source=$1&%3 [R=301,L,NE]
And it works, http://example.com/news.php?action=newsstory&nid=51 actually becomes http://example.com/?action=redirect&source=news&action=newsstory&nid=51
But you can see the problem, a double action. Using 'action' is not the most inventive term, but it is what the script I need to feed is working with, and also what I am getting from the old one, so I need to either:
replace the second 'action' with anything else
or serialize/escape the entire part which is defined by %3 from, the RewriteCond
In case you are wondering, the question mark required the use of a RewriteCond, couldn't get it to work any other way, so any solution that gets rid of it is just as welcome.
Well, thanks for looking into and maybe even shedding a light onto things, I just made my way into the whole regex thing, but havewn't entirely figured how variables are passed from chained RewriteRule to another, maybe that would have been the way to got, but try and error didn't get me anywhere.
Try removing the NE flag and use a B:
RewriteCond %{REQUEST_URI}~%{QUERY_STRING} ^(.*?\/*)?(viewuser|viewstory|reviews|news)\.php~(.+)$ [NC]
RewriteRule ^(.+).php$ /?action=redirect&source=$1&%3 [R=301,L,B]
The only problem is that the %1 backreference will get encoded as well so I left that out. If you must have it there, you can try doing a two step rewrite:
RewriteCond %{REQUEST_URI}~%{QUERY_STRING} ^(.*?\/*)?(viewuser|viewstory|reviews|news)\.php~(.+)$ [NC]
RewriteRule ^(.+).php$ /special-rewrite-here?action=redirect&source=$1&%3 [L,B]
RewriteCond %{THE_REQUEST} ^[A-Z]+\ (.*?\/*)?(viewuser|viewstory|reviews|news)\.php~(.+)$ [NC]
RewriteRule ^special-rewrite-here$ /%1 [L,R=301]
Fixed with help from Jon Lin:
RewriteCond %{REQUEST_URI}~%{QUERY_STRING} ^(.*?\/*)? (viewuser|viewstory|reviews|news)\.php~(.+)$ [NC]
RewriteRule ^(.+).php$ /special-rewrite-here?action=redirect&source=$1&old_data=%3 [B]
RewriteCond %{REQUEST_URI}~%{QUERY_STRING} ^(.*?\/*)? (viewuser|viewstory|reviews|news)\.php~(.+)$ [NC]
RewriteRule ^/.+$ %1 [L,R=301]
I had to do the RewriteCond again to preserve %1, and had to modify the second RewriteRule slightly, but he sure got me on the right track, so: problem solved, the rest will take place in PHP, that should be no problem then.

RewriteCond to match query string parameters in any order

I have a URL which may contain three parameters:
?category=computers
&subcategory=laptops
&product=dell-inspiron-15
I need 301 redirect this URL to its friendly version:
http://store.example.com/computers/laptops/dell-inspiron-15/
I have this but cannot make it to work if the query string parameters are in any other order:
RewriteCond %{QUERY_STRING} ^category=(\w+)&subcategory=(\w+)&product=(\w+) [NC]
RewriteRule ^index\.php$ http://store.example.com/%1/%2/%3/? [R,L]
You can achieve this with multiple steps, by detecting one parameter and then forwarding to the next step and then redirecting to the final destination
RewriteEngine On
RewriteCond %{QUERY_STRING} ^category=([^&]+) [NC,OR]
RewriteCond %{QUERY_STRING} &category=([^&]+) [NC]
RewriteRule ^index\.php$ $0/%1
RewriteCond %{QUERY_STRING} ^subcategory=([^&]+) [NC,OR]
RewriteCond %{QUERY_STRING} &subcategory=([^&]+) [NC]
RewriteRule ^index\.php/[^/]+$ $0/%1
RewriteCond %{QUERY_STRING} ^product=([^&]+) [NC,OR]
RewriteCond %{QUERY_STRING} &product=([^&]+) [NC]
RewriteRule ^index\.php/([^/]+/[^/]+)$ http://store.example.com/$1/%1/? [R,L]
To avoid the OR and double condition, you can use
RewriteCond %{QUERY_STRING} (?:^|&)category=([^&]+) [NC]
as #TrueBlue suggested.
Another approach is to prefix the TestString QUERY_STRING with an ampersand &, and check always
RewriteCond &%{QUERY_STRING} &category=([^&]+) [NC]
This technique (prefixing the TestString) can also be used to carry forward already found parameters to the next RewriteCond. This lets us simplify the three rules to just one
RewriteCond &%{QUERY_STRING} &category=([^&]+) [NC]
RewriteCond %1!&%{QUERY_STRING} (.+)!.*&subcategory=([^&]+) [NC]
RewriteCond %1/%2!&%{QUERY_STRING} (.+)!.*&product=([^&]+) [NC]
RewriteRule ^index\.php$ http://store.example.com/%1/%2/? [R,L]
The ! is only used to separate the already found and reordered parameters from the QUERY_STRING.
I take a slightly different approach for this sort of thing, leveraging ENV VARs set and read by mod_rewrite. I find it more readable / maintainable to refer to the backreferences by name like this, and these ENV VARs can be reused later in request processing too. Overall I think it's a more powerful and flexible approach than the accepted answer here. In any case, it works well for me. I've copied my gist below in its entirety:
From https://gist.github.com/cweekly/5ee064ddd551e1997d4c
# Mod_rewrite is great at manipulating HTTP requests.
# Using it to set and read temp env vars is a helpful technique.
#
# This example walks through fixing a query string:
# Extract good query params, discard unwanted ones, reorder good ones, append one new one.
#
# Before: /before?badparam=here&baz=w00t&foo=1&bar=good&mood=bad
# After: /after?foo=1&bar=good&baz=w00t&mood=happy
#
# Storing parts of the request (or anything you want to insert into it) in ENV VARs is convenient.
# Note the special RewriteRule target of "-" which means "no redirect; simply apply side effects"
# This lets you manipulate the request at will over multiple steps.
#
# In a RewriteRule, set custom temp ENV VARs via [E=NAME:value]
# Note it's also possible to set multiple env vars
# like [E=VAR_ONE:hi,E=VAR_TWO:bye]
#
# You can read these values using %{ENV:VAR_NAME}e <- little "e" is not a typo
#
# Tangent:
# Note you can also read these env vars the same way, if you set them via SetEnvIf[NoCase]
# (It won't work to use SetEnv, which runs too early for mod_rewrite to pair with it.)
#
# Regex details:
# (?:) syntax means "match but don't store group in %1 backreference"
# so (?:^|&) is simply the ^ beginning or an & delimiter
# (the only 2 possibilities for the start of a qs param)
# ([^&]+) means 1 or more chars that are not an & delimiter
RewriteCond %{QUERY_STRING} (?:^|&)foo=([^&]+)
RewriteRule ^/before - [E=FOO_VAL:%1]
RewriteCond %{QUERY_STRING} (?:^|&)bar=([^&]+)
RewriteRule ^/before - [E=BAR_VAL:%1]
RewriteCond %{QUERY_STRING} (?:^|&)baz=([^&]+)
RewriteRule ^/before - [E=BAZ_VAL:%1]
RewriteRule ^/before /after?foo=%{FOO_VAL}e&bar=%{BAR_VAL}e&baz=%{BAZ_VAL}e&mood=happy [R=301,L]
P.S. This is not a copy/pasteable solution to your question, but rather shows exactly how to handle this kind of problem. Armed w this understanding, leveraging it for your example will be completely trivial. :)
1) In case You just need to check that all parameters are in url:
RewriteCond %{QUERY_STRING} (^|&)category\=computers($|&)
RewriteCond %{QUERY_STRING} (^|&)subcategory\=laptops($|&)
RewriteCond %{QUERY_STRING} (^|&)product\=dell\-inspiron\-15($|&)
RewriteRule ^$ http://store.example.com/computers/laptops/dell-inspiron-15/? [R=301,L]
2) In case You need exact set of parameters:
RewriteCond %{QUERY_STRING} ^&*(?:category\=computers|subcategory\=laptops|product\=dell\-inspiron\-15)(?!.*&\1(?:&|$))(?:&+(category\=computers|subcategory\=laptops|product\=dell\-inspiron\-15)(?!.*&\1(?:&|$))){2}&*$
RewriteRule ^$ http://store.example.com/computers/laptops/dell-inspiron-15/? [R=301,L]
This rule is generated by 301 redirect generator