IIS7 URL Rewrite Mod causes unwanted effects - asp.net

This might be more a a regex question, but it is holding up our release. I'm unable to come up with a clever solution.
Basically, we want to rewrite www.oursite.com/Games.aspx?g=classic&m=etc to www.oursite.com/classic/?m=etc
The problem is that the rule itself (at least as generated by the URL Rewrite mod for IIS7) looks like this:
<rewrite>
<rules>
<rule name="RedirectUserFriendlyURL1" stopProcessing="true">
<match url="^Games\.aspx$" />
<conditions>
<add input="{REQUEST_METHOD}" negate="true" pattern="^POST$" />
<add input="{QUERY_STRING}" pattern="^g=([^=&]+)$" />
</conditions>
<action type="Redirect" url="{C:1}" appendQueryString="false" redirectType="Permanent" />
</rule>
<rule name="RewriteUserFriendlyURL2" stopProcessing="true">
<match url="([^/]+)/?$" />
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="Games.aspx?g={R:1}" />
</rule>
</rules>
</rewrite>
And thus, any other file that matches a similar pattern, for example Item/?id=23 is being rewritten. In addition, our script resource file is being rewritten, so the whole site throws 30 javascript errors. According to our specs, it's unacceptable in our specs to have the url www.oursite.com/Games/classic OR www.oursite.com/g/classic. Any solution? Manythanks!

Perhaps instead of matching ([^/]+)/?$ in your second rule, you'd be better of explicitly stating which things you want to rewrite, in a regex OR statement:
(classic|somethingelse|athirdsomething)/?$
That way only the items you explicitly want to rewrite will be modified. I don't know how many of these you're potentially needed to rewrite, so if it's a large number, this might not be viable, but if it's only a few categories, then it's probably the most straightforward.
Alternatively, if there are only certain prefixes that shouldn't be rewritten, you could simply add negated conditions that pattern match those prefixes to your rewrite.

Related

How to do 1 URL re-write rule for multiple ids?

I have these URLs:
https://www.example.com/amp/articles/10/title
https://www.example.com/amp/videos/11/title
I need to redirect only specific ids to non amp as follows:
https://www.example.com/articles/10/title
https://www.example.com/videos/11/title
Here's what I tried but there's no URL redirection, nothing occurs:
<rule name="redirect_to_non_amp_for_specific_ids" stopProcessing="true">
<match url="^amp/(articles|videos)/(10|11)/([^/]+)/?$"/>
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Redirect" redirectType="Permanent" url="{R:1}/{R:2}/{R:3}"/>
</rule>
Note I need to avoid multiple url-rewrite rules.
My rule is correct. I figured out I had to place it before other article rules, for priority, for the rule to take effect.
From microsoft: The rules are evaluated in the same order in which they are specified.
Reference: https://learn.microsoft.com/en-us/iis/extensions/url-rewrite-module/url-rewrite-module-configuration-reference

Combine several 301 redirects into one for ASP.NET URL Rewrite module

I am trying to move a complex redirect logic implemented in Global.asax into a set of rules for the IIS URL Rewrite module in a classic ASP.NET website. Shortly, the logic must redirect requests like
{http|https}://[www.]ourdomain.com/[Home/]Products/old-product-name/page.aspx
to
https://ourdomain.com/new-product-name/
(optional and variable parts are in square and curly brackets).
The first 3 main things I implemented with URL Rewrite rules are the followings:
<rule name="Redirect to HTTPS">
<match url=".*" />
<conditions>
<add input="{HTTPS}" pattern="off" ignoreCase="true" />
</conditions>
<action type="Redirect" url="https://{HTTP_HOST}{REQUEST_URI}" redirectType="Permanent" />
</rule>
<rule name="Canonical Host Name">
<match url="(.*)" />
<conditions>
<add input="{HTTP_HOST}" negate="true" pattern="^ourdomain\.com$" />
</conditions>
<action type="Redirect" url="https://ourdomain.com/{R:1}" redirectType="Permanent" />
</rule>
<rule name="Default Page">
<match url="(.*)default.aspx" />
<conditions>
<add input="{REQUEST_URI}" negate="true" pattern="-default.aspx$" />
</conditions>
<action type="Redirect" url="{R:1}" redirectType="Permanent" />
</rule>
These rules allow to redirect requests like http://www.ourdomain.com/product-name/default.aspx to https://ourdomain.com/product-name.
However, the browser developer tools report that this conversion causes 301 redirects. I tried to find a solution of this redirect chain problem in the Internet and found this post. After reading it, I managed to recode my rules to have just one final 301 redirect with URL rewriting:
<rule name="Redirect to HTTPS">
<match url=".*" />
<conditions>
<add input="{HTTPS}" pattern="off" ignoreCase="true" />
</conditions>
<action type="Rewrite" url="_{REQUEST_URI}" redirectType="Permanent" />
</rule>
<rule name="Canonical Host Name">
<match url="(.*)" />
<conditions>
<add input="{HTTP_HOST}" negate="true" pattern="^ourdomain\.com$" />
</conditions>
<action type="Rewrite" url="_{R:1}" redirectType="Permanent" />
</rule>
<rule name="Default Page">
<match url="(.*)default.aspx" />
<conditions>
<add input="{REQUEST_URI}" negate="true" pattern="-default.aspx$" />
</conditions>
<action type="Rewrite" url="_{R:1}" redirectType="Permanent" />
</rule>
<rule name="FINAL REDIRECT" stopProcessing="true">
<match url="^(_+)(.*)" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{HTTP_METHOD}" pattern="GET" />
</conditions>
<action type="Redirect" url="https://ourdomain.com{R:2}" />
</rule>
However, this approach looks non-natural. It makes hard adding new rules because of these workarounds with the underscore character. And maybe, such several rewrite operations for one request may impact on the website performance.
Is there another, more elegant solution, of the 301 redirect chain problem for the URL Rewrite module?
This is not a limitation of IIS Url Rewrite, but the way the web works. A redirection sends a header to the browser telling it that it should navigate to another place. If you have 3 rules that apply to the same original request, and the 3 apply a redirection, then you'll always get 3 redirections no matter how you do it: IIS Url Rewrite, Apache .htaccess or your own custom code.
The solution you've implemented is a "patch" to change several redirections into internal rewrites in the server and a final "catch" of those special rewrites to transform them into a unique redirection at the end (BTW, if you have any real page that strats with _ you won't be able to access it with this extra rule). That's not bad and won't affect your performance, but it's ugly.
I'm not a SEO expert but I think the 301 chaining thing to reach a final similar URL won't hurt your SEO at all in recent times, if that's what worries you (read the final part specially). And being 301 redirects they will affect only the first visit of your users, so probably won't hurt your loading times or conversions either anyway.
However, if you want to avoid that, try to make a single rule for your purpose, doing all the transformations at once. In your case, you want to transform this:
{http|https}://[www.]ourdomain.com/[Home/]Products/old-product-name/page.aspx
(I guess /Home/ can be present sometimes or not)
into this:
https://ourdomain.com/new-product-name/
Then you can use this single rule:
<rule name="New URLs!!" stopProcessing="true">
<match url="^(Home/){0,1}Products/(.+?/).+.aspx" />
<conditions logicalGrouping="MatchAny">
<add input="{HTTPS}" pattern="off" ignoreCase="true" />
<add input="{HTTP_HOST}" negate="true" pattern="^ourdomain\.com$" />
</conditions>
<action type="Redirect" url="https://ourdomain.com/{R:2}" redirectType="Permanent" />
</rule>
and you will transform everything with a single rule (so, just one redirect). The regular expression in the match URL part will identify this specific kind of URL (old product URLs) and the conditions (matching any of them) will take care of the HTTPs and domain change. The stopProcessing="true" will keep other rules to act, and you'll have this specific kind of URLs under control.
Please note that I've not tested this rule and maybe it has minor problems. But you get the idea...
You'll need extra rules after this one to take care of other less-specific URLs, such as your general HTTP to HTTPS or canonical domain rules. But in the case you want to resolve, this will achieve what you want in a single step.

Rewrite rule for a url containing space in its end in web.config?

I am working on a rewriting process. I have been stuck on a point that.
<rule name="Uk3" stopProcessing="true">
<match url="^activities/exploring/tours/tabid/3422/id/5205/k/([a-z0-9A-Z-\s]+)/p/1/nickis-beach-rides.aspx$" ignoreCase="true" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{HTTP_HOST}" pattern="^(www.)?abc.co.uk$" />
</conditions>
<action type="Redirect" url="/activities/exploring/tours.aspx" />
</rule>
what does the above rule is doing:
It matches "activities/exploring/tours/tabid/3422/id/5205/k/" as it is and the it may contain anything after this upto "/p/1/nickis-beach-rides.aspx". It is working fine for all URLs except the following :
http://www.abc.co.uk/activities/exploring/tours/tabid/3422/id/5205/k/enjoy%20your%20morning%20cofee%20and%20evening%20cocktails%20on%20/p/1/nickis-beach-rides.aspx
which contains "enjoy%20your%20morning%20cofee%20and%20evening%20cocktails%20on%20" space as the last character that cause to stop this rule to work,
How can I add the above URL to work accordingly?

In the IIS7 URL Rewrite module, can I specify in a redirect rule to not apply to http-post requests?

In IIS7 URL Rewrite module, can I specify in a redirect rule to not apply to http-post requests? I am using the templates provided by Microsoft to lowercase all urls and to append a trailing slash. However I have a AJAX post requests that don't meet this specification but they break we they are rewritten as 301s. I am not worried about POST requests for SEO so I would prefer if I could just specify in the rule to ignore it. Below are my rules:
<rule name="AddTrailingSlashRule" stopProcessing="true">
<match url="(.*[^/])$" />
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
</conditions>
<action type="Redirect" url="{R:1}/" />
</rule>
<rule name="LowerCaseRule" stopProcessing="true">
<match url="[A-Z]" ignoreCase="false" />
<action type="Redirect" url="{ToLower:{URL}}" />
</rule>
You have access to that in the {REQUEST_METHOD} variable under the conditions.
<add input="{REQUEST_METHOD}" matchType="Pattern" pattern="POST" ignoreCase="true" negate="true" />
We've had the same problem as the OP a while back, and then applied patridge's solution, which worked fine until we noticed some REST DELETE calls would fail. Turned out to be the trailing slash redirect making GETs out of the DELETE requests.
So I modified the solution to make the redirect rule to apply only to GET requests.
<add input="{REQUEST_METHOD}" matchType="Pattern" pattern="GET" ignoreCase="true" />

Append Query String to IIS Rewrite Map

I have a ReWrite Map and I would like to append any query parameters in the requested URL to the rewritten URL.
For instance:
/page/abc/ ---> /index.cfm?page=abc (works)
/page/abc/?param1=111 ---> /index.cfm?page=abc&param1=111 (doesn't work)
/page/abc/?param3=333&param4=444 ---> /index.cfm?page=abc&param3=333&param4=444 (doesn't work)
My web.config is:
[...]
<rules>
<clear />
<rule name="Rewrite rule1 for SiteMapEngine">
<match url=".*" />
<conditions>
<add input="{SiteMapEngine:{REQUEST_URI}}" pattern="(.+)" />
</conditions>
<action type="Rewrite" url="{C:1}" appendQueryString="true" />
</rule>
</rules>
[...]
Short answer:
Use PATH_INFO server variable instead of REQUEST_URI, as you do not want to include the query string in the matching.
Full explanation:
This has caught me out before - basically it is a subtlety of using Rewrite Maps in the IIS URL Rewrite Module.
In your case, SiteMapEngine will be a static key-value list of URLs:
<rewrite>
<rewriteMaps>
<rewriteMap name="SiteMapEngine" defaultValue="">
<add key="/page/abc/" value="/index.cfm?page=abc" />
...
</rewriteMap>
</rewriteMaps>
...
</rewrite>
The {SiteMapEngine:{REQUEST_URI}} condition in your rule checks whether there is a key in this rewrite map matching the REQUEST_URI server variable:
{REQUEST_URI} = /page/abc/?param1=111
Notice that this variable includes the query string - it therefore fails to find a matching key.
Instead, use the PATH_INFO server variable, which is the equivalent of REQUEST_URI but without the query string:
{PATH_INFO} = /page/abc/
So the correct rule is:
<rule name="Rewrite rule1 for SiteMapEngine">
<match url=".*" />
<conditions>
<add input="{SiteMapEngine:{PATH_INFO}}" pattern="(.+)" />
</conditions>
<action type="Rewrite" url="{C:1}" />
</rule>
I'll be damned if I can find a reference for this, but it's my understanding that in some versions of IIS {REQUEST_URI} comes back without it's query string, and will be empty entirely if rewriting is enabled.
You should be able to use {PATH_INFO} instead.
This bug report (against Drupal!) is the issue you're describing, I think: http://drupal.org/node/298016
There's a hotfix from Microsoft, but I haven't tried it: http://support.microsoft.com/kb/954946
Here is my rule. It seems to work as expected:
<rule name="Insert index.cfm" enabled="true" stopProcessing="true">
<match url="^(.*)$" ignoreCase="false" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="index.cfm/{PATH_INFO}" appendQueryString="true" />
</rule>

Resources