SharePoint Pages are not loaded correctly behind Load Balancer.

Assume following scenario ;
You have a custom 3rd party load balancer and you would like to publish only a Subsite of a site collection in SharePoint 2010 through NLB. This scenario is one of specific one , and usually is not supported all load balancers.
Some of 3rd party load balancer can be configure with rules for extra redirections.

In Our Scenario we have
One 3rd party network load balancer (NLB) which is also supporting SSL termination.
And we have a SharePoint farm which is publishing following site
http://blog.bugrapostaci.com -> is our root site collection and we don’t want to redirect through load balancer,
There should be many reasons you don’t want to publish root site directly; security concern, or already referencing another site etc.
http://blog.bugrapostaci.com/Information -> this is our subsite that we want to request through NLB . We are assuming that this site is not contains any customization and pure Sharepoin Team site template.
(Because some customized sites can be requesting extra Web Services or URLs to configure)

First you need to configure your load balancer with SSL termination and installing correct certificate which is out of scope for this article then on SharePoint you need to configure Alternate Access Mapping correctly supporting HTTPS termination.

A sample of correct configuration like this;

Internal URL                                    Zone      Public URL
https://blog.bugrapostaci.com  internet https://blog.bugrapostaci.com
http://blog.bugrapostaci.com     internet https://blog.bugrapostaci.com

Than you need to add rules for NLB for redirecting requests to correct server by HTTPS protocol and any URL contains subsite string of “Information” (our subsite part) . For configuration and how to adding rules to NLB you may need to contact with NLB vendor or check for the usage Manuel.

But this rules are not enough; if you check the site you can detect that page is not working correctly. Why ? Because when you make a request a page it has need to make extra sub requests
You can detect which requests are done at background using Fiddler2 or using F12 Developer tool for IE or Firebug extension of Firefox browser.

Here you can see a sample request when I browse the page of http://blog.bugrapostaci.com/Information site

requests

In you can see details; there are 43 requests more.


/Information/SitePages/Home.aspx        38,838  private, max-
/ScriptResource.axd?d=3deuDBoJpstM_vnvogSCBV0kbvg4UPvUtpa
/ScriptResource.axd?d=A9qblnwldHbcO4ZGhCK8Ss2nKdc2cWEIJ75
/WebResource.axd?d=Q1l5fspXysTdsR10hp_rImYHRKCODxQ3Hk5D7d
/_layouts/1033/init.js?rev=lEi61hsCxcBAfvfQNZA%2FsQ%3D%3D
/_layouts/1033/styles/Themable/corev4.css?rev=3TRomkG1g2g
/_layouts/1033/styles/Themable/search.css?rev=T%2Bhraxktc
/_layouts/1033/styles/Themable/wiki.css?rev=9pXM9jgtUVYAH
/_layouts/blank.js?rev=QGOYAJlouiWgFRlhHVlMKA%3D%3D
/_layouts/images/bgximg.png
/_layouts/images/blank.gif

And some of the is directly coming from root site. That’s mean just adding a rule for redirecting only subsite string of “Information” is not enough.Here is a sample list for possible requests coming form when you make a request to Subsite. You should add necessary rules for this requests.

“/Information” -> the subsite url part.
“/_controltemplates”
“/_layouts”  -> Images ,Styles, Javascripts
“/_vti_bin”
“/_wpresouces”
“/App_Browsers”
“/App_GlobalResources”
“/wpresouces”
“/_app_bin”
“/_vti_pvt”
“/_forms”
“/_login”
“/_windows”
“/ScriptResource.axd” -> Handlers
“/WebResource.axd”  -> Handlers.

this sample is only valid for pure SharePoint Team Site Template without customization ; So if you have some customizations and extra webservices requests or using another paths .
I could not you tell extra url names but I can show you how could you catch a fish

You can test with;

Getting a Fiddler,Developer Tool for IE or Firebug extension for Firebug trace on Server without https and make request to your Subsite . You can see all needed requests and files need to be loaded for working this page is correctly. Make a list for it;
Do it same operation behind Load Balancer and test , add missing redirections to 3rd party load balancer rules for them.

Advertisements

Restriction Module : secure your asp.net application

RestrictionModue makes life easier that secure your asp.net or sharepoint site content, pages or documents when authorization restrictions or standart restrictions are not enough. Its developed in c# 3.5.

What is Restriction Module

Restriction module is an open source project based on a httpmodule that alternatively provide security to your web applications. You can define rules that protect your pages,contents or documents. Use more flexible restrictions using power of regular expression than forms authentication web.config options.You can change security settings in runtime. And no need to recycle application or no extra down time .

Configuration of  Restriction Module

  • Add Module Definition:

Add this key below to your web.config file: configuration/system.web/httpModules

<add name=”RestrictionModule” type=”RestrictionModuleApp.RestrictionModule, RestrictionModuleApp,Version=1.0.0.0, Culture=neutral, PublicKeyToken=be23a05ec1781ff6″  />

  • Add switch key:

Add belowed key to configuration/appSettings
<add key=”RestrictionModuleActive” value=”On” />

İf value is on means module is active off means inactive.

  • Copy  Restriction.xml to your application path

Restriction.xml file definitions

You should define restrictions rules in restriction xml . Here is an example .

<?xml version="1.0" encoding="utf-8"?>
<RestrictionRules xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <RestrictionRule Enabled="false" Action="Deny" Mode="RequestURL" ContinueRuleList="false">
    <Expressions>
      <Expression Type="Contains" CaseSensitive="false">/Pages/Forms/</Expression>
      <Expression Type="Contains" CaseSensitive="false">/_layouts/</Expression>
    </Expressions>
    <RedirectURL>http://blog.bugrapostaci.com/ErrorPages/401.aspx</RedirectURL>
    <Audiences>PostDomain\guest</Audiences>
  </RestrictionRule>
</RestrictionRules>

Restriction Rule:

You can define a restriction rule between <RestrictionRule></RestrictionRule> tags.

Restriction Rule Parameters:

  • Enabled: Boolean. Makes this rule active or inactive
  • Action: Gets two parameters  “Allow” and “Deny”. Default is “Deny”.If you choose “allow” by default  all request redirect to  url which is defined redirectURL parameter except selected user scope (if Audiences parameter is empty means all user is selected . All requests are not redirected) . Otherwise you choose “Deny”  by default all request continue with its normal destination except selected users redirect to  url which is defined RedirectURL parameter
  • Mode: Defines an envoriment variable in httpcontext for restriction
    • RequestURL: Restrict request with using raw url of request.
    • RequestUserHostName: Restrict request by UserHostName
    • RequestContentType: Restrict request by Content Type.
    • RequestUserHostAddress: Restrict request by UserHostAddress
  • ContinueRuleList: Boolean. By default If one rule match with expression and not match any criteria the other rules will not execute. If you set this parameter “true” the other rules will be executed what if an expression matched for one rule.  This feature should be used two different rule with same expression but modes are not equal.

For example:

Rule1: deny select url contains “/pages/” for A and B user. -> Redirect to access denied page

Rule2: deny select ip startwith “192.168” for A and C user  -> Redirect to access denied page

İf user “C” make a request a pagewith url contains “/pages/” rule expression match but identity not .By default expression match is enough and the other rules not executed.For execute rule2 you should set this parameter true in rule1.

Expression:

You can define one or more expression in a rule. There is no expression continue list option.if one expression is match with the pattern or criteria,  the others not execute.Also there is no relation between expressions.

  • Type : Defines an operation on envoriment variable .Available operations
    • Contains : Search given parameter with in variable
    • StartWith : Search given parameter  at start of variable
    • EndWith : Search given parameter at end of variable
    • RegularExpression:Search given patterns and user parameter as variable.
    • CaseSensitive: Boolean . Define case sensitivity feature except Regular Expression.Default is false. İf you want use case sensitivity in regular expression,you have to write in pattern.

RedirectURL:

If any rule’s expression match with the criteria request will redirect to this url. Usually this url is Access Denied page or login page url.

Audiences

Access list for a rule. If its empty means all users.If you want to define more than one user you have to seperate identities  by comma “,”.

Example:

<Audiences>BlogDomain\bugra,BlogDomain\postman</Audiences>

Exampe Restrictions Rules

Other users will restricted.

<?xml version="1.0" encoding="utf-8"?>
<RestrictionRules xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <RestrictionRule Enabled="false" Action="Allow" Mode="RequestURL" ContinueRuleList="false">
    <Expressions>
      <Expression Type="StartWith" CaseSensitive="false">http://blog.bugrapostaci.com/admin</Expression>
    </Expressions>
    <RedirectURL>http://blog.bugrapostaci.com /ErrorPages/401.aspx
         </RedirectURL>
    <Audiences>BlogDomain\admin</Audiences>
  </RestrictionRule>
</RestrictionRules>
  • Example 2 : Deny “Guest” user to reach pages url which contains /Pages/Forms and /Pages/Admin/
<?xml version="1.0" encoding="utf-8"?>
<RestrictionRules xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

  <RestrictionRule Enabled="false" Action="Deny" Mode="RequestURL" ContinueRuleList="false">
    <Expressions>
      <Expression Type="Contains" CaseSensitive="false">/Pages/Forms/</Expression>
      <Expression Type="Contains" CaseSensitive="false">/Pages/Admin/</Expression>
    </Expressions>
    <RedirectURL>http://blog.bugrapostaci.com /ErrorPages/401.aspx
         </RedirectURL>
    <Audiences>Guest</Audiences>
  </RestrictionRule>
</RestrictionRules>

  • Example 3 : Deny all users which ip address start with “192.168.10”
<?xml version="1.0" encoding="utf-8"?>
<RestrictionRules xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <RestrictionRule Enabled="false" Action="Deny" Mode="RequestUserHostAddress" ContinueRuleList="false">
    <Expressions>
      <Expression Type="StartWith" CaseSensitive="false">192.168.10</Expression>
    </Expressions>
    <RedirectURL>http://blog.bugrapostaci.com/ErrorPages/401.aspx</RedirectURL>
    <Audiences> </Audiences>
  </RestrictionRule>
</RestrictionRules>
  • Example 4 : Multiple Rules
<?xml version="1.0" encoding="utf-8"?>
<RestrictionRules xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

  <RestrictionRule Enabled="false" Action="Deny" Mode="RequestURL" ContinueRuleList="true">
    <Expressions>
      <Expression Type="Contains" CaseSensitive="false">/Pages/Forms/</Expression>
      <Expression Type="Contains" CaseSensitive="false">/Pages/Admin/</Expression>
    </Expressions>
    <RedirectURL>http://blog.bugrapostaci.com /ErrorPages/401.aspx
         </RedirectURL>
    <Audiences>Guest</Audiences>
  </RestrictionRule>
<RestrictionRule Enabled="false" Action="Deny" Mode="RequestUserHostAddress" ContinueRuleList="false">
    <Expressions>
      <Expression Type="StartWith" CaseSensitive="false">192.168.10</Expression>
    </Expressions>
    <RedirectURL>http://blog.bugrapostaci.com/ErrorPages/401.aspx</RedirectURL>
    <Audiences></Audiences>
  </RestrictionRule>
</RestrictionRules>

You can visit project page:

http://restrictionmodule.codeplex.com

You can download release version :

http://restrictionmodule.codeplex.com/releases/view/50844#DownloadId=144210

And here is the user manuel

http://restrictionmodule.codeplex.com/releases/view/50844#DownloadId=144633

Happy codding.

Redirect your site with http 301 permenantly to new domain

Redirect your site with http 301 permenantly to new domain

You want to move your site to new domain and dont want to lose your page rank in google search engine.This post has contains some instructions that how to use redirect your site with http 301 permenantly moved status code.

301 means the requested resource has been assigned to a new permanent Uniform Resource Identifier (URI), and any future references to this resource should be done using one of the returned URIs. 

Here is the road map

1)    use  301 Redirect (see httpmodule code in below). and make page to page basis redirection.

2)      Find external links by googling “link:www.yoursite.com”. If some of them has static links to “yoursite”, we must inform the webmasters to change the link.

3)      Track broken links with Xenu (this is an automated tool, that checks all links in a web site and prepare a report about broken links etc.)

4)      Domain www.yoursite.nl should be active for some time (min  180 days) after redirecting is activated.

5)      Add your new site’s url to Google Web Administration Tools.

6)      Prepare a new site map and add this to Google Web Administration Tools.

7)      Check SiteMap details page from time to time, to see how many URL’s google indexed from the new domain.

An example httpModule for redirect request with 301 permenant moved

using System;
using System.Web;
using System.Configuration;
using System.Web.Caching;
using System.Collections.Generic;
 
namespace YourSite
{
    /*
 * This HTTPModule Intercepts the Application Cycle, and performs a 301 Redirect to the domain 
 * parameter given in the web.config file - "newURL"
 */
 
    public class RedirectorModule : IHttpModule
    {
        /// <summary>
        /// You will need to configure this module in the web.config file of your
        /// web and register it with IIS before being able to use it. For more information
        /// see the following link: http://go.microsoft.com/?linkid=8101007
        /// </summary>
        #region IHttpModule Members
 
        // The Dictionary that will hold the URLMapping rules
        Dictionary<string, string> URLMapping = new Dictionary<string, string>();
 
        public void Dispose()
        {
            //clean-up code here.
        }
 
        public void Init(HttpApplication context)
        {
            // Below is an example of how you can handle LogRequest event and provide 
            // custom logging implementation for it
            //context.LogRequest += new EventHandler(OnLogRequest);
 
            // INTERCEPT PostResolveRequestCache PHASE
            context.PostResolveRequestCache += new EventHandler(context_PostResolveRequestCache);
        }
 
        void context_PostResolveRequestCache(object sender, EventArgs e)
        {
            // GET APPLICATION AND REQUEST
            HttpApplication app = (HttpApplication)sender;
            HttpRequest request = app.Context.Request;
 
            // TAKE FILE NAME FROM WEB.CONFIG
            string fileName = ConfigurationManager.AppSettings["URLMappingFilePath"];
            string filePath = app.Context.Server.MapPath(fileName);
 
            // CHECK IF URLMAPPING DICTIONARY IS IN CACHE
            if (app.Context.Cache["URLMapping"] == null)
            {
                //
                CacheDependency dependency = new CacheDependency(filePath);
                app.Context.Cache.Insert("URLMapping", URLMapper.DeserializeObject(filePath), dependency);
                URLMapping.Clear();
            }
 
            URLMapping = (System.Collections.Generic.Dictionary<string, string>)app.Context.Cache["URLMapping"];
 
            string lRequestedPath = request.Url.ToString();
            string requestedURL = request.RawUrl.ToString();
 
            if (!CheckFileExtension(requestedURL)) return;
 
            //REMOVE QUESTION MARK AT THE END
            if (requestedURL.Substring(requestedURL.Length - 1).Equals("?"))
                requestedURL = requestedURL.Substring(0, requestedURL.Length - 1);
 
 
            // WHEN USING IN LOCAL WE SHOULD REMOVE APPLICATION NAME FROM URL, 
            // BEFORE WE LOOK TO THE URL MAPPING
            string localAppName = ConfigurationManager.AppSettings["localAppName"];
            if (requestedURL.ToLower().Contains(localAppName.ToLower()))
            {
                requestedURL = requestedURL.Replace(localAppName, "");
            }
 
            if (URLMapping.ContainsKey(requestedURL))
            {
                requestedURL = URLMapping[requestedURL];
            }
 
            string newURL = ConfigurationManager.AppSettings["newURL"];
            string urlToGo = newURL + requestedURL;
 
            app.Response.StatusCode = System.Net.HttpStatusCode.MovedPermanently; // 301
            app.Response.AddHeader("Location", urlToGo);
            app.Response.End();
        }
 
        private bool CheckFileExtension(string requestedURL)
        {
            if (requestedURL.Contains("favicon.ico"))
            {
                return false;
            }
            return true;
        }
 
        #endregion
 
        public void OnLogRequest(Object source, EventArgs e)
        {
            //custom logging logic can go here
        }
    }
}
 
see all Http Status Codes
http://msdn.microsoft.com/en-us/library/aa383887(VS.85).aspx
check your site's page rank
http://www.googlepagerankchecker.com/
For broken links
http://home.snafu.de/tilman/xenulink.html

 

more to see for 301 redirection

http://www.google.com/support/webmasters/bin/answer.py?hl=en&answer=93633