Rewrite Map Rule to match any URL extension - regex

I'm fairly new to rewrite maps, but we did get ours to work on a very basic level. After a website redesign, we set up an extensive rewrite map (thousands o rules) to point the old pages to the new ones. The trouble we're having is that we're having to add multiple values for the same page in order for the rewrite to work.
Example:
http://www.abc123.com/About --> http://www.abc123.com/about-us
http://www.abc123.com/About.aspx --> http://www.abc123.com/about-us
http://www.abc123.com/about/ --> http://www.abc123.com/about-us
http://www.abc123.com/about.aspx --> http://www.abc123.com/about-us
There should be a way to wildcard anything after the base URL in the regular expression - I'm expecting something like this: ^./[about]$ which would be great if ALL urls contained "about" but they don't.
Also note that we aren't redirecting by directory, but rather by file name. It's that our CMS is set up not to use the .aspx extension, so any extension will work.
What I want is to only have to have ONE rule for each URL that looks like:
"http://www.abc123.com/about" and it will point all of the above variations to the new URL regardless if it does not have an extension or if the extension is .html, .asp, .aspx, or .whatever
Is that beyond the capabilities of the rewrite rules or is there some basic regular expression I am missing?
Here is the rule we are using:
<rule name="Redirect Rule for Legacy Redirects" enabled="true" stopProcessing="true">
<match url=".*" />
<conditions>
<add input="{Redirects:{REQUEST_URI}}" pattern="(.+)" />
</conditions>
<action type="Redirect" url="{C:1}" appendQueryString="false" />
</rule>
Any insight would be much appreciated.

[Hh][Tt][Tt][Pp]://(([^/])/)[Aa][Bb][Oo][Uu][Tt].*
See https://regex101.com/r/rZhJyz/1, just append "about-us" to the Group #1 match.

Had to figure this out today. It can be done by using the <match url> regex to strip off the extension, and then using the matched portion from here as the input for the rewrite map lookup.
The rewrite map keys must NOT have a starting /.
The rule (and sample map) looks like this, example for stripping off .aspx extension (could be generalised):
<rewrite>
<rewriteMaps>
<rewriteMap name="Test">
<add key="test" value="http://www.google.com" />
<add key="test.aspx" value="http://www.google.com" />
</rewriteMap>
</rewriteMaps>
<rules>
<rule name="Rewrite Map Optional Aspx Extension" stopProcessing="true">
<match url="^(.*?)(\.aspx)?$" />
<conditions>
<add input="{Test:{R:1}}" pattern="(.+)" />
</conditions>
<action type="Redirect" url="{C:1}" appendQueryString="false" />
</rule>
</rules>
</rewrite>
The important changes from a standard rewrite map rule are:
Adding (\.aspx)? as an optional part of the match url, and have have added ? to .* to make the initial .* not greedy so it doesn't include the extension.
Changed {Test:{REQUEST_URI}} to {Test:{R:1}} so it uses the matched input from the match url (.*)
Take out leading / from rewrite map keys

Related

IIS: Rewrite URL with regex but keep query strings

I'm have the following link
https://example.com/myapp/green?&lang=en&instance=some%20instance
I need to rewrite it to the following link
https://example.com/myapp?color=green&lang=en&instance=some%20instance
The color in the link can be any color but it needs to be rewritten like in the 2nd link so that the trailing slash is replaced with a ? followed by the word color= and the ? at the end of the color word needs to be removed.
/myapp/green? becomes /myapp?color=green,
/myapp/blue? becomes /myapp?color=blue
and so forth, all while keeping the rest of the query string &lang=en&instance=some%20instance intact
I've tried regexing my way out of this but I usually catch everything or unintentionally omit the rest of the query string.
Any ideas on what's the best approach?
EDIT: noticed that IIS, when applying to application level (not website level), the input URL path is after '/myapp/' and I need that trailing slash removed. Does this mean I'll have to apply it to the website level?
You could use below url rewrite rule(add this rule at site level):
<rule name="color query" enabled="true" stopProcessing="true">
<match url="myapp/(.*)/$" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{QUERY_STRING}" pattern="lang=(.*)&instance=(.*)" />
</conditions>
<action type="Redirect" url="http://www.sample1.com/myapp?color={R:1}&lang={C:1}&instance={C:2}" appendQueryString="false" />
</rule>
Note: you could not remove the myapp/ from URL it will be added automatically in URL.

How to add exclusion/negation to a generic rewrite rule in IIS 8?

I am using a regex in a rewrite rule (on IIS 8) to deny access to all files and folders starting with a dot (.)
This way, these Urls cannot be accessed :
https://example.com/.forbidden_file
https://example.com/.forbidden_folder/somefile.txt
<rule name="Deny access to files and folders starting with . or /." patternSyntax="ECMAScript" enabled="true" stopProcessing="true">
<match url="(^\.|\/\.)" ignoreCase="true" negate="false" />
<action type="AbortRequest" />
</rule>
Today I would like to add an exclusion, in order to allow access to a specific file named ".specific_allowed_file", and a specific folder named ".specific_allowed_folder", like this :
https://example.com/.specific_allowed_file
https://example.com/.specific_allowed_folder/somefile.txt
I have managed to achieve it by adding two conditions, like this :
<rule name="Deny access to files and folders starting with . or /." patternSyntax="ECMAScript" enabled="true" stopProcessing="true">
<match url="(^\.|\/\.)" ignoreCase="true" negate="false" />
<conditions logicalGrouping="MatchAny">
<add input="{PATH_INFO}" pattern="(.*)(\.specific_allowed_file$" negate="true" />
<add input="{PATH_INFO}" pattern="(.*)(\.specific_allowed_folder)(.*)" negate="true" />
</conditions>
<action type="AbortRequest" />
</rule>
Do you know if this is a reliable implementation, or if I should rather use another method, like modifying my matching regex ?
Thank you very much,
Your approach is a very good one since it allows users add conditions using readable and clear code. One thing should be noted: pattern="(.*)(\.specific_allowed_folder)(.*)" will also allow a .specific_allowed_folder_WITH_MORE_TEXT_HERE as well. You may fix it to match only folders by adding / into the first and third groups and making them optional by placing a ? quantifier right after the groups:
<add input="{PATH_INFO}" pattern="^(.*/)?(\.specific_allowed_folder)(/.*)?$" negate="true" />
If you want a single regex to do the whole job, use a regex with a negative lookahead:
<match url="(^|/)\.(?!(specific_allowed_file|specific_allowed_folder)(/|$))" ignoreCase="true" negate="false" />
See the regex demo.
Details
(^|/) - start of string or /
\. - a dot
(?!(specific_allowed_file|specific_allowed_folder)(/|$)) - that is not immediately followed with specific_allowed_file or (|) specific_allowed_folder substrings that are followed with / or end of string ($).

"%" character causing issues in IIS URL rewrite regex

I'm replatforming an online shop, and part of that process is writing 301 redirects for old links customers may have bookmarked to hit their new addresses. All of them work apart from the ones in which the old address contains the % character. It seems to somehow be interfering with the regex. The rule is:
<rule name="Custom 41 redirect" stopProcessing="true">
<match url="^c80/How-to-%20Use-Shop-Name\.aspx" ignoreCase="true" />
<action type="Redirect" url="/how-to-use-shop-name" redirectType="Permanent" />
</rule>
Testing using the built in regex tester in IIS tells me that hitting
/c80/How-to-%20Use-Shop-Name.aspx
Matches the pattern, yet the rule fails to redirect me when I hit that address.
I should also stress that there are about 400 rewrites in my web.config, but only the 3 which contain the % character break.
Since the string that is tested against the regex is already Url-decoded, you no longer need the %20 in your regex. Just use a plain space:
<match url="^c80/How-to- Use-Shop-Name\.aspx" ignoreCase="true" />
Also, in case the space is optional, you may use a ? after it:
<match url="^c80/How-to- ?Use-Shop-Name\.aspx" ignoreCase="true" />

Rewrite maps in IIS7 — how to make the match optionally include a trailing slash?

I have read the top 30 Google hits for several combinations of IIS rewrite map condition and so on, but I can't find any decent documentation, either on a microsoft.com site or elsewhere.
I have a bunch of rewrite maps in IIS7 that I would like to process irrespective of whether or not they are followed by a trailing slash. So www.foo.com/bar and www.foo.com/bar/ should both match the rule.
<rewrite>
<rewriteMaps>
<rewriteMap name="ShortURLs">
<add key="/terms" value="/en-us/terms-and-conditions/"/>
<add key="/privacy" value="/en-us/privacy-and-cookies/"/>
<add key="/buy" value="/en-us/where-to-buy/"/>
</rewriteMap>
</rewriteMaps>
<rules>
<rule name="Short URL redirects">
<match url="^/?(.+)/?$" />
<conditions>
<add input="{ShortURLs:{REQUEST_URI}}" pattern="(.+)"/>
</conditions>
<action type="Redirect" url="{C:1}" appendQueryString="true"/>
</rule>
</rules>
</rewrite>
Now this works perfectly well, except that the only way I can find to make /terms/ match the first key in the rewrite map is to duplicate the map, so that it reads:
<rewriteMap name="ShortURLs">
<add key="/terms" value="/en-us/terms-and-conditions/"/>
<add key="/privacy" value="/en-us/privacy-and-cookies/"/>
<add key="/buy" value="/en-us/where-to-buy/"/>
<add key="/terms/" value="/en-us/terms-and-conditions/"/>
<add key="/privacy/" value="/en-us/privacy-and-cookies/"/>
<add key="/buy/" value="/en-us/where-to-buy/"/>
</rewriteMap>
This seems ridiculously inelegant, given that I'm using regular expressions to match them in the first place. Adding /? to the condition input or the condition pattern doesn't seem to work.
I have seen the answer to IIS7 Rewrite Map Regex? that mentions regular expressions cannot be used (quoting from Using Rewrite Maps in URL Rewrite Module) but, as I have commented there, this seems to relate to the specific examples being given before that text, rather than a wholesale "this can never work".
What am I missing? There must be some means of doing this; am I missing something obvious?
This should do it:
<rewrite>
<rewriteMaps>
<rewriteMap name="ShortURLs">
<add key="terms" value="/en-us/terms-and-conditions/"/>
<add key="privacy" value="/en-us/privacy-and-cookies/"/>
<add key="buy" value="/en-us/where-to-buy/"/>
</rewriteMap>
</rewriteMaps>
<rules>
<rule name="Short URL redirects">
<match url="^(.+?)/?$" />
<conditions>
<add input="{ShortURLs:{R:1}}" pattern="(.+)" />
</conditions>
<action type="Redirect" url="{C:1}" appendQueryString="true"/>
</rule>
</rules>
</rewrite>
You were quite close; I only needed to make three small changes:
removed the leading slashes in the keys in the rewrite map
used the non-greedy quantifier +? in the rule's match
used a back reference to the match {R:1} in the condition input
I share your experience in having trouble finding decent documentation; I had to experiment my way through, with help from the following articles:
http://www.iis.net/learn/extensions/url-rewrite-module/using-rewrite-maps-in-url-rewrite-module
http://technet.microsoft.com/en-us/library/ee215190(v=ws.10).aspx
Firstly, don't use dot, it matches everything and is naturally greedy. Use character negation instead: ([^\n]+).
Try this, then re-run, and if that doesn't work, try adding /? again on the pattern attribute.

How to redirect subfolder to query in Mod Rewrite for IIS 7.0?

I'm using Mod Rewrite for IIS 7.0 from iis.net and want to redirect requests:
http://example.com/users/foo to http://example.com/User.aspx?name=foo
http://example.com/users/1 to http://example.com/User.aspx?id=1
I have created 2 rules:
<rule name="ID">
<match url="/users/([0-9])" />
<action type="Rewrite" url="/User.aspx?id={R:1}" />
</rule>
<rule name="Name">
<match url="/users/([a-z])" ignoreCase="true" />
<action type="Rewrite" url="/User.aspx?name={R:1}" />
</rule>
It passes a test into iis mmc test dialog, but doesn't in debug (URL like http://localhost:9080/example.com/users/1 or …/users/foo) and doesn't on real IIS!
What have I done wrong?
The obvious problem is that your current regexes only match one character in the user name or one number. You'll need to add a plus quantifier inside the parentheses in order to match multiple letters or numbers. See this page for more info about regex quantifiers. Note that you won't be matching plain URLs like "/users/" (no ID or name). Make sure this is what you intended.
The other problem you're running into is that IIS evaluates rewrite rules starting from the first character after the initial slash. So your rule to match /users/([0-9]) won't match anything because when the regex evaluation happens, the URL looks like users/foo not /users/foo. The solution is to use ^ (which is the regex character that means "start of string") at the start of the pattern instead of a slash. Like this:
<rule name="ID">
<match url="^users/([0-9]+)" />
<action type="Rewrite" url="/User.aspx?id={R:1}" />
</rule>
<rule name="Name">
<match url="^users/([a-z]+)" ignoreCase="true" />
<action type="Rewrite" url="/Users.aspx?name={R:1}" />
</rule>
Note that you're choosing Users.aspx for one of these URLs and User.aspx (no plural) for the other. Make sure this is what you intended.
BTW, the way I figured these things out was by using IIS Failed Request Tracing to troubleshoot rewrite rules. This made diagnosing this really easy. I was able to make a test request and look through the trace to find where each rewrite rule is being evaluated (it's in a section of the trace called "PATTERN_MATCH". For the particular PATTERN_MATCH for one of your rules, I saw this:
-PATTERN_MATCH
Pattern /users/([0-9]+?)
InputURL users/1
Negate false
Matched false
Note the lack of the beginning slash.
You should use <match url="/users/([0-9]+)" /> and <match url="/users/([a-z]+)" ignoreCase="true" />, respectively, to match the complete id/user and not just their first letter/digit. But I don't know why your regex would have failed on a single digit, so there must be another issue, too.
As for your second question, I'm not sure I understand completely. How can you tell the difference between a folder name and a user name? Will a folder always have a trailing slash?