One redirect to rule them all - iis

After trying to help a fellow developer on the iis.net forums, I began to search for a better way to do my redirects. And thus I ended up with this article.
I took a lot of these ideas and began to implement my own version of it. Everything seemed to work just okay, but I hit a bump in the road and I hope you can help.
Okay so, a quick explanation on my rules:
My idea is to test the url, and if I find something wrong with it, I rewrite the url and let it go on, rather than redirect it instantly. If at any point I rewrite the url, I set a custom server variable "Redirect" to true. Then in the end, I test if my custom server variable is true and redirect the user.
The point of it is to only have one 301 redirect, rather than a chain of redirects.
These are my rules (Sorry for the wall):
<rules>
<rule name="WhiteList - resources" stopProcessing="true">
<match url="^resources/" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
<action type="None" />
</rule>
<rule name="Redirect subdomains with www to non-www" stopProcessing="false">
<match url="(.*)" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{HTTP_HOST}" pattern=".*localhost.*" negate="true" />
<add input="{HTTP_HOST}" pattern="^www\.(.*)\.([^\.]+)\.([^\.]+)$" />
</conditions>
<action type="Rewrite" url="http://{C:1}.{C:2}.{C:3}{HTTP_URL}" />
<serverVariables>
<set name="Redirect" value="true" />
</serverVariables>
</rule>
<rule name="Redirect top domains with non-www to www" stopProcessing="false">
<match url="(.*)" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{HTTP_HOST}" pattern=".*localhost.*" negate="true" />
<add input="{HTTP_HOST}" pattern="^([^\.]+)\.([^\.]+)$" />
</conditions>
<action type="Rewrite" url="http://www.{HTTP_HOST}{HTTP_URL}" />
<serverVariables>
<set name="Redirect" value="true" />
</serverVariables>
</rule>
<rule name="SEO - Remove trailing slash" stopProcessing="false">
<match url="(.*)/$" />
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="{R:1}" />
<serverVariables>
<set name="Redirect" value="true" />
</serverVariables>
</rule>
<rule name="SEO - ToLower" stopProcessing="false">
<match url="(.*)" ignoreCase="false" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{URL}" pattern="[A-Z]" ignoreCase="false" />
<add input="{URL}" pattern="^.*?\.(axd|css|js|jpg|jpeg|png|gif|ashx|asmx|svc).*?$" negate="true" />
<add input="{URL}" pattern="^.*/(webshop)/.*$" negate="true" />
</conditions>
<action type="Rewrite" url="{ToLower:{R:1}}" />
<serverVariables>
<set name="Redirect" value="true" />
</serverVariables>
</rule>
<rule name="SEO - remove default.aspx" stopProcessing="false">
<match url="(.*?)/?default\.aspx$" />
<action type="Rewrite" url="{R:1}" />
<serverVariables>
<set name="Redirect" value="true" />
</serverVariables>
</rule>
<rule name="SEO - Trim aspx" stopProcessing="false">
<match url="(.*)\.aspx$" />
<action type="Rewrite" url="{R:1}" />
<serverVariables>
<set name="Redirect" value="true" />
</serverVariables>
</rule>
<rule name="SEO - non-canonical redirect" stopProcessing="true">
<match url="^(.*)" />
<conditions>
<add input="{Redirect}" pattern="true" />
</conditions>
<action type="Redirect" url="{R:1}" />
<serverVariables>
<set name="Redirect" value="false" />
</serverVariables>
</rule>
</rules>
Now most of it actually works pretty well, but I seam to have some issues with a naked domain.
If I take a subdomain with www (which should be redirected to non-www) then it appears to fail. Funny enough, if I go to a subpage, it works fine.
(I can't post more urls because I'm new here :( And I wanted to give credits to the others for starting me on this.)
I've traced it down to the server variable {HTTP_URL} because if I remove it, it doesn't fail. However it of course doesn't do what it is supposed to either (It always redirects to the naked domain). I've tried with various variables: {URL}, {REQUEST_URI} and they all seem to end up with the same error, which is getting a bit annoying.
Should anyone have some improvement for the rules, feel free to respond as well, I love working with them and I would like to make the near perfect redirects, so any suggestions are welcome.

This ended up being more of a version 1 and I have since improved on it to avoid the server variable.
Now I use a series of rewrite rules that transforms the url to something I like, appending underscore.
Then stripping the underscore and then redirecting.
It have worked very well for me for quite some time now and is being used by the company on almost all projects.
It have been made into a nuget package for easy setup.
http://www.nuget.org/packages/RedirectRules/

I believe I actually found the answer now. I worked around with it for some time and ended up with another issue, again with the www vs non-www. It simply didn't do anything.
So I ended up putting it at the end and making that redirects rather than rewrites as well. Apparently that works fine, why? No idea.
cheesemacfly suggested that I match on ^(.+)$ rather than (.*).
I thought it would do the very same thing, but realized that the match isn't on the hostname at all, it is everything after. Big surprise for me there, but it did open up a new possibility:
Instead of matching {URL}, {HTTP_URL} or some other variant of almost the same thing, I could simply refer to the match with {R:1}
I have put in two things since the first post. I put in conditions on some of the rules to exclude them if it contains "umbraco" as I work with that system and most of the rules I don't want to apply to the back-end. I also applied a rewrite map for SSL check, so it automatically puts you to https:// or http:// using the server variable {HTTPS}
So far I haven't had found any bugs in it, so I will mark this as solved and give you my last revision of the rules. Enjoy :)
<rewrite>
<rules>
<rule name="WhiteList - resources" stopProcessing="true">
<match url="^resources/" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
<action type="None" />
</rule>
<rule name="SEO - Remove trailing slash" stopProcessing="false">
<match url="(.*)/$" />
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="{R:1}" />
<serverVariables>
<set name="Redirect" value="true" />
</serverVariables>
</rule>
<rule name="SEO - ToLower" stopProcessing="false">
<match url="(.*)" ignoreCase="false" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{URL}" pattern="[A-Z]" ignoreCase="false" />
<add input="{URL}" pattern="^.*?\.(axd|css|js|jpg|jpeg|png|gif|ashx|asmx|svc).*?$" negate="true" />
<add input="{URL}" pattern="^.*/(webshop)/.*$" negate="true" />
<add input="{URL}" pattern="^/umbraco/" negate="true" />
</conditions>
<action type="Rewrite" url="{ToLower:{R:1}}" />
<serverVariables>
<set name="Redirect" value="true" />
</serverVariables>
</rule>
<rule name="SEO - remove default.aspx" stopProcessing="false">
<match url="(.*?)/?default\.aspx$" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{URL}" pattern="^/umbraco/" negate="true" />
</conditions>
<action type="Rewrite" url="{R:1}" />
<serverVariables>
<set name="Redirect" value="true" />
</serverVariables>
</rule>
<rule name="SEO - Trim aspx" stopProcessing="false">
<match url="(.*)\.aspx$" />
<action type="Rewrite" url="{R:1}" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{URL}" pattern="^/umbraco/" negate="true" />
</conditions>
<serverVariables>
<set name="Redirect" value="true" />
</serverVariables>
</rule>
<rule name="Redirect subdomains with www to non-www" stopProcessing="false">
<match url="(.*)" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{HTTP_HOST}" pattern=".*localhost.*" negate="true" />
<add input="{HTTP_HOST}" pattern="^www\.(.*)\.([^\.]+)\.([^\.]+)$" />
</conditions>
<action type="Redirect" url="{MapSSL:{HTTPS}}{C:1}.{C:2}.{C:3}/{R:1}" redirectType="Permanent" />
<serverVariables>
<set name="Redirect" value="true" />
</serverVariables>
</rule>
<rule name="Redirect top domains with non-www to www" stopProcessing="false">
<match url="(.*)" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{HTTP_HOST}" pattern=".*localhost.*" negate="true" />
<add input="{HTTP_HOST}" pattern="^([^\.]+)\.([^\.]+)$" />
</conditions>
<action type="Rewrite" url="{MapSSL:{HTTPS}}www.{HTTP_HOST}/{R:1}" />
<serverVariables>
<set name="Redirect" value="true" />
</serverVariables>
</rule>
<rule name="SEO - non-canonical redirect" stopProcessing="true">
<match url="(.*)" />
<conditions>
<add input="{Redirect}" pattern="true" />
</conditions>
<action type="Redirect" url="{R:1}" redirectType="Permanent"/>
<serverVariables>
<set name="Redirect" value="false" />
</serverVariables>
</rule>
</rules>
<rewriteMaps>
<rewriteMap name="MapSSL" defaultValue="OFF">
<add key="ON" value="https://" />
<add key="OFF" value="http://" />
</rewriteMap>
</rewriteMaps>
</rewrite>
There is a caveat though: Notice that a custom server variable is used. You cannot use a custom server variable out of the box. You need to add this variable to the <allowedServerVariables/> element in the applicationHost.config file, which is a global configuration file for IIS. (Source)
If you're using shared hosting (Including Azure Web Sites), chances are that you will not be able to change this setting.
If you're using an Azure Web Role, you'll need to add a startup task that modifies the aforementioned configuration file. If you don't do this, you'll get an IIS 500 error.
If someone have suggestions as to what could make the rules better, they are still welcome.

Related

Redirect old page to new page usign web.config

i need to rewrite the url from old page to new page, an example:
old: https://www.mysite.it/scheda.asp?num=123456
new: https://www.mysite.it/scheda/?num=123456
i've put in web.config the following lines but don't work, when i go to https://www.mysite.it/scheda.asp?num=123456 noting happen, site go to the page without rewrite:
<rule name="scheda-rew" stopProcessing="true">
<match url="scheda.asp(.*)" />
<action type="Rewrite" url="scheda/{R:1}" logRewrittenUrl="true" />
</rule>
i try this:
<rule name="scheda-rew2" stopProcessing="true">
<match url="^scheda\.asp" ignoreCase="true" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{QUERY_STRING}" pattern="isbn=([0-9]+)" />
</conditions>
<action type="Redirect" url="scheda/{C:1}" appendQueryString="false" />
</rule>
nothing appen, seem that all change i do haven't effect
You can try below rule, if you have any question please let me know:
<rule name="RewriteUserFriendlyURL2" stopProcessing="true">
<match url="^scheda.asp\?([^/]+)/?$" />
<conditions>
<add input="{​​​​​​​REQUEST_FILENAME}​​​​​​​" matchType="IsFile" negate="true" />
<add input="{​​​​​​​REQUEST_FILENAME}​​​​​​​" matchType="IsDirectory" negate="true" />
<add input="{​​​​​​​REQUEST_URI}​​​​​​​" pattern="^scheda/$" negate="true" />
</conditions>
<action type="Redirect" url="scheda/?num=123456" appendQueryString="false" />
</rule>

Redirect rule in IIS not applying

I have the following problem:
i want to let the users of an exact url redirect to another one.
Note: my URL immediately downloads a file.
What i want is to let the file forward to another file (it needs to download the file i'm redirecting to).
My Rule:
<rule name="File" patternSyntax="ExactMatch" stopProcessing="true">
<match url="https://SITE.be/download/DownloadFile?id=138999" />
<conditions logicalGrouping="MatchAny">
<add input="{URL}" pattern="https://SITE.be/download/DownloadFile?id=138999" />
<add input="{QUERY_STRING}" pattern="https://SITE.be/download/DownloadFile?id=138999" />
<add input="{REQUEST_FILENAME}" matchType="IsFile" />
</conditions>
<action type="Redirect" url="https://SITE.be/download/DownloadFile?id=138111" appendQueryString="false" />
</rule>
my rule isn't working, it doesn't redirect it and when i check 'URL redirect checkers' it mentions that there isn't a redirect set on the URL.
what am i doing wrong?
Edit:
I tried:
<rule name="File" stopProcessing="true">
<match url="download/DownloadFile?id=138999" />
<conditions logicalGrouping="MatchAny">
<add input="{HTTP_HOST}" pattern="^site.be$" />
</conditions>
<action type="Redirect" url="https://site.be/download/DownloadFile?id=138111" appendQueryString="false" />
</rule>
with no improving (redirect checkers on google still don't trigger that theres a redirect set)
According to your iis rewirte code, I find it has problem in match url part.
This part could only match the [download/DownloadFile] not the querystring.
If we want to check the query string id, we should use url rewrite condition.
More details, you could refer to below url rewrite rule.
<rule name="File" stopProcessing="true">
<match url="download/DownloadFile" />
<conditions logicalGrouping="MatchAll">
<add input="{QUERY_STRING}" pattern="id=138999" />
<add input="{HTTP_HOST}" pattern="^site.be$" />
</conditions>
<action type="Redirect" url="https://site.be/download/DownloadFile?id=138111" appendQueryString="false" />
</rule>
Or
<rule name="File" stopProcessing="true">
<match url="(.*)" />
<conditions logicalGrouping="MatchAll">
<add input="{HTTP_HOST}" pattern="sitebe" />
<add input="{REQUEST_URI}" pattern="download/DownloadFile\?id=138999" />
</conditions>
<action type="Redirect" url="https://site.be/download/DownloadFile?id=138111" appendQueryString="false" />
</rule>

Rewriting subdomains to subfolders and parameter in IIS

I'm trying to wrap my head around this problem;
I have a default site with an application that resides in a subfolder, e.g. /app/app.html
It also accepts a language parameter, so for example http://192.168.0.1/app/app.html?language=fi would work.
Now I have two subdomains that I need to not only get rewritten to the correct folder, but also include the language parameter. For example:
fi.domain.com -> http://1.1.1.1/app/app.html?language=fi
swe.domain.com -> http://1.1.1.1/app/app.html?language=swe
I've made A records for both subdomains to point to 1.1.1.1
Currently there are no special bindings (only port 80, no hostnames and all IPs) and no special default pages.
EDIT: I've tried using the URL rewriter module, but I haven't been able to get it work as intended.
EDIT 2: My first example of how I need it to work was a bit flawed, here's a better version;
finnishword.domain.com -> http://1.1.1.1/app/app.html?language=fi
otherwordinswedish.domain.com -> http://1.1.1.1/app/app.html?language=swe
I'm not sure to well understand your question.
It seems that you need URL rewriting rules.
According to this link
<rule name="CName to URL - Rewrite" stopProcessing="true">
<match url=".*" />
<conditions>
<add input="{HTTP_HOST}" pattern="^(?!www)(.*)\.domain\.com$" />
</conditions>
<action type="Rewrite" url="/app/app.html?language={C:1}" />
</rule>
I think it's what you need ;)
Edit 24/08/2016
If you can't have a dynmique pattern with RegEx you have to do as many rules as you have subdomains:
<rule name="finnishRule" stopProcessing="true">
<match url=".*" />
<conditions>
<add input="{HTTP_HOST}" pattern="^(?!www)(finnishword)\.domain\.com$" />
</conditions>
<action type="Rewrite" url="/app/app.html?language=fi" />
</rule>
<rule name="finnishRule" stopProcessing="true">
<match url=".*" />
<conditions>
<add input="{HTTP_HOST}" pattern="^(?!www)(otherwordinswedish)\.domain\.com$" />
</conditions>
<action type="Rewrite" url="/app/app.html?language=swe" />
</rule>
or you can mixup all together if you want to redirect all subdomains that contains the word "swedish" on url with ?
<rule name="finnishRule" stopProcessing="true">
<match url=".*" />
<conditions>
<add input="{HTTP_HOST}" pattern="^(?!www)(.*swedish.*)\.domain\.com$" />
</conditions>
<action type="Rewrite" url="/app/app.html?language=swe" />
</rule>
So after all the RegEx pattern is up to you ;)
Edit 25/08/2016
Maybe you have to add condition to skip static files
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
...
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
</conditions>
<rules>
<clear />
<rule name="swedishMainRule" enabled="true" stopProcessing="true">
<match url="^$" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{HTTP_HOST}" pattern="^(www\.)?swedishword\.domain\.fi$" />
<add input="{QUERY_STRING}" pattern="language=" negate="true" />
</conditions>
<action type="Redirect" url="?language=sve" appendQueryString="false" logRewrittenUrl="false" />
</rule>
<rule name="swedishRule" enabled="true" stopProcessing="true">
<match url="(.*)" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{HTTP_HOST}" pattern="^(www\.)?swedishword\.domain\.fi$" />
</conditions>
<action type="Rewrite" url="/app/{R:1}" />
</rule>
<rule name="finnishRule" enabled="true" stopProcessing="true">
<match url="(.*)" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{HTTP_HOST}" pattern="^(www\.)?anotherfinnishword\.domain\.fi$" />
</conditions>
<action type="Rewrite" url="/app/{R:1}" />
</rule>
</rules>
This is how I ended up doing it, basically the first rule deal with the language parameter, and the other rules just rewrite the URL. The Finnish rule doesn't need an extra language rule since the default language of the app is Finnish.

URL rewrite on IIS 8.0

I have installed URL rewrite module on the IIS 8.0 and configure rules
If user come without www and it will prefix www
If user comes from http then it will redirect to https
If user comes from mobile browser than it sends to mobile website
Below are the rules
<appcmd>
<CONFIG CONFIG.SECTION="system.webServer/rewrite/globalRules" path="MACHINE/WEBROOT/APPHOST" overrideMode="Inherit" locked="false">
<system.webServer-rewrite-globalRules>
<rule name="Mobile Redirect" enabled="true" patternSyntax="ECMAScript" stopProcessing="true">
<match url="^$" ignoreCase="true" />
<conditions logicalGrouping="MatchAny" trackAllCaptures="false">
<add input="{HTTP_USER_AGENT}" pattern="android|blackberry|googlebot-mobile|iemobile|iphone|ipod|opera mobile|palmos|webos" />
<add input="{HTTP_X-Device-User-Agent}" pattern="midp|mobile|phone" />
<add input="{HTTP_X-OperaMini-Phone-UA}" pattern="midp|mobile|phone" />
</conditions>
<serverVariables>
</serverVariables>
<action type="Redirect" url="/en-mobile" appendQueryString="false" />
</rule>
<rule name="Add HTTPS and WWW prefix to website.COM" stopProcessing="true">
<match url="(.*)" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{HTTPS}" pattern="^OFF$" />
<add input="{HTTP_HOST}" pattern="^website\.com" />
</conditions>
<serverVariables>
</serverVariables>
<action type="Redirect" url="https://www.website.com/{R:1}" appendQueryString="false" />
</rule>
<rule name="Add HTTPS to WWW.website.COM" stopProcessing="true">
<match url="(.*)" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{HTTPS}" pattern="^OFF$" />
<add input="{HTTP_HOST}" pattern="^www\.(.+)$" />
</conditions>
<serverVariables>
</serverVariables>
<action type="Redirect" url="https://www.website.com/{R:1}" />
</rule>
</system.webServer-rewrite-globalRules>
</CONFIG>
</appcmd>
In above first rule Mobile redirect is done when user come from mobile browser. I redirect to https://www.website.com/en-mobile but when I do like this https://m.website.com/en-mobile it gives error but when I browse manually it works very good. So how can I redirect to that url when people come from https://www.website.com to https://m.website.com/en-mobile
I could solve this issue may be it will help to somebody so I am adding my solution over here. I change Mobile Redirect rule action to
<action type="Redirect" url="https://m.website.com/en-mobile" appendQueryString="true" redirectType="Found" />
Putting redirectType to found solves my issue.

IIS URL Rewrite https rule ignoring localhost

I'm trying to write a URL rewrite rule to force a HTTPS connection. This should always happen except when a request is using localhost (e.g. http://localhost/mysite).
The rule is configured as following:
<rule name="Redirect to https" enabled="true" stopProcessing="true">
<match url="(.*)" negate="false" />
<conditions trackAllCaptures="false">
<add input="{HTTPS}" pattern="^OFF$" />
<add input="{URL}" pattern="localhost" negate="true" />
</conditions>
<action type="Redirect" url="https://{HTTP_HOST}/{R:1}" />
</rule>
I also tried to use ^localhost and ^localhost/(.*) as a pattern for the URL condition with no help.
Does anyone have an idea why this does not work and what a solution for this problem should be?
Your code should look like this instead
<rule name="Redirect to https" enabled="true" stopProcessing="true">
<match url="(.*)" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{HTTPS}" pattern="off" />
<add input="{HTTP_HOST}" pattern="localhost" negate="true" />
</conditions>
<action type="Redirect" url="https://{HTTP_HOST}/{R:1}" />
</rule>

Resources