Forum Moderators: phranque

Message Too Old, No Replies

Using RewriteMap with only 1 URI section in DBM data

         

dfresh4130

8:32 pm on Dec 4, 2014 (gmt 0)

10+ Year Member



I'm working on a project where they want to implement some 301 redirects for specific products on the site. The current page goes to a basic product page on one backend via proxypass and they'd like it to go to a different backend system with more detailed data. Example URLs are:

http://www.example.com/purchase/inventory/product/123456 --> http://www.example.com/purchase/product-line/details/category/123456


There are SKUs in the first example URI that aren't in the second so that's why they've given me a 1:1 list of URLs they want covered. I've converted it to a DBM using only the SKUs (last URI section) instead of the entire URI like below. The reasoning is because they want me to rewrite the exact URLs and any other URLs containing those SKUs so I figured that would give me more flexibility in the rewrite rule.
123456 123456
123abc 123abc
789def 789def


RewriteMap product_SKUs dbm:/etc/httpd/conf.d/maps/product_SKUs.dbm


The DBM files created are:

/etc/httpd/conf.d/maps/product_SKUs.dbm.pag
/etc/httpd/conf.d/maps/product_SKUs.dbm.dir


The initial rewrite rule I created just to test this out is below, but something isn't right as I'm not seeing the redirect. Is it possible to only use the DBM data in a single section of the URL instead of the entire URL? All examples I've found using rewrite maps so far are complete URLs.


RewriteCond %{REQUEST_URI} ^/purchase/inventory/product/${product_SKUs:$1}
RewriteRule ^.* http://www.example.com/purchase/product-line/details/category/${product_SKUs:$1} [R=301,L]

lucy24

9:10 pm on Dec 4, 2014 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



${product_SKUs:$1}

What version of Apache are you on? What does $1 refer to? It kinda looks as if you're misreading the docs (in their examples, $1 refers as expected to material captured in the pattern) but things are not always as they appear :(

<tangent>
RewriteCond %{REQUEST_URI} ^/purchase/inventory/product/${product_SKUs:$1}
RewriteRule ^.* http://www.example.com/purchase/product-line/details/category/${product_SKUs:$1} [R=301,L]


This is an awkwardly constructed rule in any case. It would be far more efficient as (assuming the rule is in a Directory section)
RewriteCond %{REQUEST_URI} ^/purchase/inventory/product/${product_SKUs:$1}
RewriteRule ^purchase/inventory/product/ http://www.example.com/purchase/product-line/details/category/${product_SKUs:$1} [R=301,L]


(no closing anchor in pattern). Never put something in a Condition that can go in the body of the rule.
</tangent>

:: wandering off to look up RewriteMap syntax, because I think something is missing ::

I'm inclined to think that the error is in the Condition. If I'm reading your prose correctly, you want literal text for the last part. Capture it as
/purchase/inventory/product/(.+)

This will then become %1 where you now have $1 in the target of the Rule. To be safe you may also want to supply a default value. For testing, make up the name of some nonexistent page. Then you can at least see if the rule is executing, even if it can't find the correct value in the map.

Matter of fact-- why do you even need a Condition? Why not capture from the Pattern side of the rule (using $1), and feed that directly into the taget?

dfresh4130

10:23 pm on Dec 4, 2014 (gmt 0)

10+ Year Member



Running version 2.2.26. I believe I need a condition because I only want the rule to match on product SKUs in that list. If I tell it to rewrite on every hit then I'll be directing traffic to a backend where the products don't exist, right? I tried changing the rewrite rule line to use %1, but no difference. I agree it's probably something wrong with the condition, but I'm having trouble finding anything in the docs that say I can use rewritemap values inside a condition.

lucy24

11:44 pm on Dec 4, 2014 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



I'm having trouble finding anything in the docs that say I can use rewritemap values inside a condition

You can absolutely use a RewriteMap in a condition. But, first, you can't say $1 when there's nothing for $1 to refer to. And, second, it doesn't look as if you can use RewriteMap values on the CondPattern (right) side of a Condition, only on the TestString (left) side. So you can say "If the map blahblah is such-and-such string" but you can't say "If such-and-such is the map blahblah".

:: detour here to deal as expeditiously as possibly with phone call from ex-boyfriend involving residual guilt over (more recent) girlfriend who recently drank herself to death* ::

I only want the rule to match on product SKUs in that list. If I tell it to rewrite on every hit then I'll be directing traffic to a backend where the products don't exist, right?

That would be where the default value comes in. If a given SKU doesn't have an entry in the RewriteMap, send everyone to some generic page. But this won't work if the non-mapped URL values are supposed to stay on the originally requested URL.

Honestly I'm not sure a RewriteMap is what you need. At some point there's got to be some php-or-equivalent involvement, right? Can't you dump everything on the php and let it deal with issuing the redirects? To avoid infinite loops you might need to do something like setting an environmental variable on the initial request, and then only rerouting to php if the environment doesn't (yet) exist.

Sometimes it's a question of how many possible values are involved. If you've got hundreds or thousands of possible SKUs, and hundreds or thousands of those are not included on the RewriteMap list, then a RewriteMap is probably not the best approach. But if you only sell ten items, and two of them are to be excluded, then you can achieve it in mod_rewrite.


* Unfortunately I am not making any of this up.

dfresh4130

12:18 am on Dec 5, 2014 (gmt 0)

10+ Year Member



I came across this URL [willnorris.com ] which gets me pretty close to the solution. We can use the below condition, but I can't find a way to have it use data from the line itself for the first part of the URI then the rewritemap value for the last part.

RewriteCond ${product_SKUs:%{REQUEST_URI}} (.+)


Unfortuantely this is a large site with tomcat backends so PHP isn't an option here. Apache is just acting as a reverse proxy in this case. The main reason I'm going this route is they want me to catch any URL using that SKU so I thought it'd be more flexible to only enter the SKUs in the map and do other rewrites for URLs like /purchase/item=12345. Worst case scenario is I just do a 1:1 mapping and we can't this to work on those indirect URLs.

lucy24

3:35 am on Dec 5, 2014 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



I can't find a way to have it use data from the line itself for the first part of the URI then the rewritemap value for the last part.
<snip>
RewriteCond ${product_SKUs:%{REQUEST_URI}} (.+)

Ooh, interesting, I wouldn't have thought of nesting things that way.

But can it be made to work? The %{REQUEST_URI} is the whole path-- regardless of where the rule is located-- so you'd have to change the map to make it match a full URLpath. Or make a preceding condition whose sole purpose is to capture:

RewriteCond %{REQUEST_URI} /purchase/inventory/product/(\d+) 
RewriteCond ${product_SKUs:%1} (..+)
RewriteRule ^purchase/inventory/product/ http://www.example.com/purchase/product-line/details/category/%1 [R=301,L]

Disclaimer: I obviously couldn't test with your map, but this works:

RewriteCond %{REQUEST_URI} /trythis/(\d+)
RewriteCond %1 (\d\d)
RewriteRule trythis http://www.example.com/%1.html [R=301,L]

meaning:
IF (first condition) the requested URL contains /trythis/ followed by some numbers, then capture the numbers
and then
IF (second condition) the capture is at least two digits
THEN redirect to the first two digits of this newly captured content

In other words, you're cascading your captures. In the second Condition, %1 refers to material captured in the first condition; in the target of the rule, %1 refers to material captured in the second Condition. In the example I made up
RewriteCond ${product_SKUs:%1} (..+)

the function of ..+ is just to ensure that the map returns some value at this position, and to be safe it has to be at least two characters.

:: looking vaguely around for someone who speaks Apache, which I don't ::

Quick edit: Seems like this should also work, with less going around in circles:

RewriteCond ${product_SKUs:$1} (..+)
RewriteRule ^purchase/inventory/product/(.+) http://www.example.com/purchase/product-line/details/category/%1 [R=301,L]


Here you're taking advantage of the confusing fact that, while captures from Conditions only apply to the following line, captures from the pattern in the body of the rule can be used both backward (in conditions) and forward (in the target).