I've got a RewriteMap that looks like this:
Guide 1
Mini-Guide 2
White Paper 3
and I'm including it into Apache via
RewriteMap legacy txt:/var/www/site/var/rewrite_map.txt
I want to create a RewriteRule that will allow only values from the left side of said RewriteMap to be in this position;
RewriteRule ^/section/downloads/(${legacy})/(.*)$ /blah.php?subsection=${legacy:%1}&title=$2
I know I can use ${legacy} on the right side, but can I use it on the left, and if so, how?
In your map file, the left side is the key and the right side is the value. When you create a rule for matching against a map, you input the key and it outputs the value.
Change your RewriteRule to this:
# Put these on one line
RewriteRule ^/section/downloads/([a-zA-Z-]+)/(.*)$
/blah.php?subsection=${legacy:$1}&title=$2
The first grouping captures the string in the incoming URL. The $1 in the replacement applies it to the named map. To make a default value, change ${legacy:$1} to ${legacy:$1|Unknown}.
Finally, if you only want the rule to work on values that are in the map file, add a RewriteCond:
RewriteCond ${legacy:$1|Unknown} !Unknown
# Put these on one line
RewriteRule ^/section/downloads/([a-zA-Z-]+)/(.*)$
/blah.php?subsection=${legacy:$1}&title=$2
The condition says if the map does not return the default value (Unknown), then run the next rule. Otherwise, skip the rule and move on.
Apache RewriteMap
another variant:
# %1 will be the subpattern number1 afterwards
RewriteCond %{REQUEST_URI} ^/section/downloads/(.*)
# check if there is no mapping for %1
RewriteCond ${legacy:%1} !^$
# if there is rewrite it
RewriteRule ^/(.*) /blah.php?subsection=${legacy:%1}&title=$2 [R]
You said, you want to only allow values found in the map. This isn't possible unless you specify an additional restriction in regex for the capture group. There's no way to do it with the map itself. There's no "map.keys" syntax, as far as I know, that you can apply in the left hand side, the pattern.
BUT,
You can specify a default value if the captured value is not found. This way:
## all on one line
RewriteRule ^/section/downloads/([a-zA-Z-]+)/(.*)$
/blah.php?subsection=${legacy:$1|defaultValue}&title=$2
Replace "defaultValue" with whatever you like. For example 0 (zero), or "notfound", if the given arg is not found in the map.
You can then either rewrite the result of that, with another rule, or just allow it to flow through and provide a "404" message at the URL with the default value.
If you choose to use another rule, then it would look like this:
## all on one line
RewriteRule ^/section/downloads/([a-zA-Z-]+)/(.*)$
/blah.php?subsection=${legacy:$1|notFoundMarker}&title=$2
## This rule fires if the lookupKey was not found in the map in the prior rule.
RewriteRule ^/blah.php?subsection=notFoundMarker /404.php [L]
Related
I was looking for a way to prevent my PDF files to be accessed from direct URL on my website and I found theses htaccess rules :
RewriteEngine On
RewriteCond %{HTTP_HOST}##%{HTTP_REFERER} !^([^#]*)##http?://\1/.*
RewriteRule .*\.pdf [NC,F]
Even though it seems to work perfectly, I don't really understand what these # symbols mean on the RewriteCond rule. I've some basics with regex but I haven't found anything related to these on apache and regex docs, and the article where I found the rules doesn't provide any info.
Any ideas?
Short answer: Its basically a segregator between values of HTTP_HOST and %{HTTP_REFERER}. To match their values while performing condition check in RewriteCond directive.
Explained answer: Now why we are putting these ## characters as a segregator between 2 apache variables. Its simple whenever we want to compare if 2 values are EQUAL or SAME then we use it, because this helps us to catch value in capturing group and then later if back reference value used in condition is NOT same then our condition will fail.
Now come on to this current scenario:
let's say our domain name is: www.example.com
and HTTP_REFERER value is: http://www.example.com/en-US/JavaScript
Then what %{HTTP_HOST}##%{HTTP_REFERER} will do is:
it will make value as:
www.example.com##http://www.example.com/en-US/JavaScript
Now come on the right side of Cond line:
!^([^#]*)##http?://\1/.*
You see capturing group will have value as www.example.com and when we are using it as \1 in http?://\1 its actually checking if URL is http://www.example.com/.* or not. if its NOT EQUAL then go ahead with the request of URI.
Basically why we are doing this because there is NO direct way to check if 2 values are equal or not in URI.
Suggestion on improving your Rules:
RewriteEngine On
RewriteCond %{HTTP_HOST}##%{HTTP_REFERER} !^([^#]*)##http?://\1/.*
RewriteRule .*\.pdf/?$ - [NC,F]
I'm using the rewrite_mod to rewrite web service requests. If the QUERY_STRING of the request contains a certain parameter then the URL get some additional parameters. This is my Rewrite Condition. I also use a RewriteMap that defines a key-value pair. When the RewriteCond matchs then the value of the key will be added to the rewritten URL.
That all works fine but now I want to add a default value into the substitution of the RewriteRule in case the RewriteMap cannot find the key in the RewriteMap file. The syntax in the RewriteRule looks like ${ MapName : LookupKey | DefaultValue }. When the Look-up-Key will not be found the DefaultValue will be used instead.
What I exactly want is that when the key is not found the original request URL will be used and no substitution will be conducted.
My first approaches were to use ${MapName:$1|http://%{HTTP_HOST}%{REQUEST_URI}?{QUERY_STRING}} or ${MapName:$1|http://%{HTTP_HOST}%{REQUEST_URI}} but none of it works. I don't know what to put as a DefaultValue after the |.
I just found a solution for my problem. It is necessary to define a RewriteCond that checks if the correct key is in the RewriteMap file. If it is not in the map file the RewriteRule will be scipt and the original request URL will be used.
The RewriteCond looks like RewriteCond ${MapName:$1|NOT_FOUND} !NOT_FOUND .
I want to associate user Ids to a specific application Id like:
<user_id> <app_id>
615 1
616 7
617 3
618 3
My URIs looks like:
/<app_id>/<user_id>/...
Now, I want to be able to easily change the application without impacting the user bookmarks. In my example, I want both
/1/615/index.html or /3/615/index.html
to be served as
/1/615/index.html
With the following rule, I get infinite loop:
RewriteMap map dbm:user-application.map
RewriteRule ^/([0-9]+)/([0-9]+)/(.*)$ /${map:$2}/$2/$3 [R,L]
...
#other proxy code to forward request to applications
I understand that after the redirection, Apache will always execute the same rule.
I then tried to add a rewrite condition to block the loop, like
RewriteMap map dbm:user-application.map
RewriteCond %{REQUEST_URI} !^/${map:$2}
RewriteRule ^/([0-9]+)/([0-9]+)/(.*)$ /${map:$2}/$2/$3 [R,L]
If I read correctly my rewrite logs, I can see that the variable !^/${map:$2} is not replaced in the condition pattern, but checked "as it". And then the condition is always true, and I still get my infinite loop.
Any idea to block the loop as soon as the application id match my map?
/3/615/index.html is correctly redirecting to /1/615/index.html
The problem is that you are redirecting /1/615/index.html to /1/615/index.html as well - you want to detect the case in which the map transform is a no-op and not redirect at all in that case.
If you don't care about the user-facing URL, just change the [R,L] to [L] (removing the R) and you should be fine since it won't trigger a new round-trip from the client.
You're right that the $2 backreference won't work in a RewriteCond expression; this is because the RewriteRule hasn't yet been evaluated. You might be able to use %n - style backreferences to a regex in a previous RewriteCond...
RewriteCond {%REQUEST_URI} ^/([0-9]+)/
RewriteCond {%REQUEST_URI} !^/${map:%1}
But I have not tested this, so YMMV.
How can i catch the prams after file name with extension like service.php/view1
for ex:
service.php/newview1
I want to get it like
service.php?view=newview1
how do i write mod-rewrite for this
I tried like
RewriteRule ^services.php/?([a-zA-Z_]+)$ /services.php?category=$1
its not matching the service.php/newview1
When the replacement URI contains a query string, the default behavior
of RewriteRule is to discard the existing query string, and replace it
with the newly generated one. Using the [QSA] flag causes the query
strings to be combined.
You need to add [QSA] flag to end of your current line
Try adding this regex:
([a-zA-Z0-9-_]+) as this also matches any integers and hyphens also.
RewriteRule ^services.php/?([a-zA-Z0-9-_]+)$ /services.php?category=$1 [QSA,L]
I also put the [L] flag so it stop processing further rules.
Flash movies are called based on dynamic links on mypage.php. mypage.php has the flash player embedded. The links look like mypage.php?moviefolder=folder1/folder2&swfTitle=sometitle.swf. mypage.php is parsed on each link click (per the href). Folder2 is always the same but movieTitle.swf is dynamic. Sometimes subfolders will be called (folder2/subfolder2/sometitle.swf).
Can mod_rewrite allow the query string to reflect folder2 but instead silently serve folder3 as well as occasional subfolders? I would place all files in folder3. The goal is to have the user not know where the swfs are. Thanks in advance again!
Using a RewriteCond to match the contents of the query string (since they are not read in a RewriteRule directive, you can extract swfTitle=sometitle.swf and substitute folder1/folder3 for folder1/folder2 in the moviefolder.
This will use a regex pattern like ([^&]+) to match everything up to the next & (which denotes another query param).
# Capture everything after folder2 into %1
RewriteCond %{QUERY_STRING} moviefolder=folder1/folder2([^&]+) [NC]
# Capture everything in the swfTitle param into %2
# Both conditions must be matched...
RewriteCond %{QUERY_STRING} swfTitle=([^&]+) [NC]
# Then silently rewrite mypage.php to substitute folder3,
# and pass in the original swfTitle captured above
RewriteRule ^mypage\.php$ mypage.php?moviefolder=folder1/folder3%1&swfTitle=%2 [L]
Hopefully, you won't get a rewrite loop, since the rewritten folder1/folder3 won't match the second time. [NC] allows for a case-insensitive match.
I did manage to successfully test this over at http://htaccess.madewithlove.be/, using the sample input:
http://example.com/mypage.php?swfTitle=thetitle.swf&moviefolder=folder1/folder2/thing
---> http://example.com/mypage.php?moviefolder=folder1/folder3/thing&swfTitle=thetitle.swf
http://example.com/mypage.php?moviefolder=folder1/folder2/thing999zzz&swfTitle=thetitle.swf
---> http://example.com/mypage.php?moviefolder=folder1/folder3/thing999zzz&swfTitle=thetitle.swf