How to Get Your Silverlight Pages Indexed By Search Engines

By Peter Bromberg

Use the HTML DOM to add dynamic Title, Keywords and Description tags to your Silverlight Pages

There are a lot of articles and tips about "deep linking" and so on with Silverlight.

But, the bottom line is that if you want your Silverlight pages properly indexed by Google and its brethren, you need to be able to dynamically add TITLE, META KEYWORDS and META DESCRIPTION Tags to the hosting page. Here we will show not only how to do this from within a Silverlight application, but also how to do it in response to a search engine crawler.

In a nutshell, search engines follow hyperlinks, and then index any content they find. They collect any additional links they find on the page and crawl those. If you have a Silverlight application that has multiple user controls that respond to user navigation, but they are all hosted in the same page that has a TITLE tag of "My Cool Silverlight Application" and no dynamic META Description or META keywords tags, you really haven't accomplished much from an SEO (search engine optimization) standpoint. This doesn't even take into account the fact that all your "content" may be inside your Silverlight app, which renders it 100% invisible to search bots. You may actually need to consider having some "relevant content" in the HTML page itself to get real search engine action.

The way that your "Page" gets found by a search engine is either you submitted it, it is listed in your sitemap.xml file, or there is a hyperlink to it from somewhere else, perhaps even from another page in your own website. There are other "deep linking" techniques that will allow Silverlight content to be custom-presented based on what is on the QueryString in a unique search-engine optimized url, but I'll save those for another article.

You can add these tags to a page directly from within your Silverlight Application itself with the following code. You can also use this same technique to inject relevant content into the HTML of the page where it will be indexed. The end result will be that your Silverlight "Page" will end up being the target of more search results on Google, Bing, Yahoo and oher search engines, and you can combine this technique with the deep-linking properties of, for example, a Silverlight Navigation app to get much better search engine result rankings.

First, in your App.Xaml.cs codebehind class, add the following static method:

public partial class App : Application
{
public App()
{
this.Startup += this.Application_Startup;
this.Exit += this.Application_Exit;
this.UnhandledException += this.Application_UnhandledException;
InitializeComponent();
}
public static void SetPageInfo (string keywords, string title, string description)
{
ScriptObjectCollection soc = HtmlPage.Document.GetElementsByTagName("Title");
if (soc != null && soc.Count > 0)
{
HtmlElement titleElement = (HtmlElement)soc[0];
// string mytitle = (string)titleElement.GetProperty("innerHTML");
HtmlPage.Document.SetProperty("title", title );
HtmlElement headElem = HtmlPage.Document.GetElementsByTagName("head")[0] as HtmlElement;
HtmlElement keys = HtmlPage.Document.CreateElement("meta");
keys.SetAttribute("name=\"keywords\" content", keywords);
headElem.AppendChild(keys);
HtmlElement desc = HtmlPage.Document.CreateElement("meta");
desc.SetAttribute("name=\"description\" content", description);
headElem.AppendChild(desc);
}
}

NOTE: Apparently Firefox does not like SetAttribute. Use SetProperty instead.

Then in your Main page codebehind, you can call the method like this:

public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
App.SetPageInfo("test,test2,test3","This is the title", "This is the description");
}
}

Incidentallly, astute readers might wonder why I am doing this, setting both the name attribute and the content attribute "in one go":

keys.SetAttribute("name=\"keywords\" content", keywords);

The answer is, that's the only way I could get this to work! Otherwise, I would get a <meta keywords="X,Y, Z""> without the name attribute. Go figure....

If your Silverlight Application has multiple pages or uses the Navigation framework, you can do this for each page in your application, setting a different Title, meta keywords and meta description tag dynamically.

If you want the search engines to index your Silverlight content, don't expect them to dig into your XAP file and parse the text - not only is it unlikely they'll do it - but you might not want them to anyways.

Search engines want to see, at a minimum, a Page TITLE element and a META Description element. Give them what they want, and your pages that contain Silverlight content will be correctly indexed!

It irks me to no end to see people with blogs that have excellent multiple content pages, and yet every single one has the same Page Title tag that says, "My Blog"! If you give Google, Bing, Yahoo et. al. garbage - they're going to give you garbage back, and your search results will reflect this.

You MUST have a unique, keyword - dense Page TITLE and META Description tag for every page on your blog or website. Most search engines will use your META Description tag content verbatim. If this tag is missing, they'll come up with their own, and you may not like it.

If you View Source from the browser, you will not see these elements because they have been dynamically added to the page. But by using a little Javascript, you can easily see that they are in the page:


javascript:document.write(%22%3Cxmp%3E%22+document.documentElement.innerHTML+%20%22%3C/xmp%3E%22);

You can copy and paste the above snippet into the address bar of your browser to see this at work.

The downloadable Visual Studio 2008 solution has the above javascript in an A HREF link right in the test page.


Next Stage: Feed the Bots!

Jim McCurdy astutely pointed out that while the above may be useful, it is still not visible to crawlers. The crawler simply downloads all the markup in the page, but the Silverlight plugin is never instantiated. In order for a Silverlight Application to "come to life", it must exist in a live browser DOM. Jim shows a solution using urlrewriting. However, it can be done with an even simpler solution - detection of bots via their User-Agent and writing the custom content directly into the same page.

Let's add a couple of utility methods to our ASP.NET web project's Global.asax codebehind:

public static bool IsBot()
{
if( HttpContext.Current.Request.UserAgent==null)
return false;
string userAgent = HttpContext.Current.Request.UserAgent.ToLower();
string[] botKeywords = new string[] { "bot", "spider", "google", "yahoo", "search", "crawl", "slurp", "msn", "teoma", "ask.com" };
bool isBot = false;
foreach (string bot in botKeywords)
{
if (userAgent.Contains(bot))
{
isBot = true;
break;
}
}
return isBot;
}
public static void PlaceMetaTags (string title, string keywords, string description, Page page )
{
System.Web.UI.HtmlControls.HtmlMeta metaKeywords = new System.Web.UI.HtmlControls.HtmlMeta();
System.Web.UI.HtmlControls.HtmlMeta metaDescription = new System.Web.UI.HtmlControls.HtmlMeta();
page.Title = title;
metaKeywords.Name = "Keywords";
metaKeywords.Content = keywords;
metaDescription.Name = "Description";
metaDescription.Content = description;
page.Header.Controls.Add(metaDescription);
page.Header.Controls.Add(metaKeywords);
}

"IsBot" simply compares the User-Agent string to a known list of keywords, and returns true or false. And "PlaceMetaTags" provides a convenient way to write a custom title, meta - keywords and meta -description tag. Both of these methods, since they are static and public, will be availalble from any page in your web site via "Global.MethodName..."

So to test this, I've constructed a "test page" that uses the WebClient class to request our Silverlight page after setting a "fake" GoogleBot User-Agent:

WebClient wc = new WebClient();
wc.Headers.Add("user-agent", "Googlebot/2.1 (+http://www.google.com/bot.html)");
string s = wc.DownloadString("http://localhost:4403/Default.aspx");
this.txtBox1.Text = s; // show the crawled HTML

what will happen now if you run the "WebForm1.aspx" page in the sample solution is that the page detects that the request is from a "bot", and you will see the crawled HTML in the textBox with:

<head id="Head1">
<title> Title for Bots!</title>
....
<meta name="Description" content="The description I want bots to see" /><meta name="Keywords" content="BotKeyword1, botkeyword2" />
</head>

And all is good. We've accomplished two goals: Be able to dynamically write Title and Meta tags from within a Silverlight application (useful when the page is viewed in a browser, as in "real life"), and also the ability to detect when a search engine crawler has requested the page, and provide the crawler with meaningful tags to index. Both of these approaches represent useful tools you'll definitely want to include in your Silverlight Application "toolkit".


You can download the complete solution here.



Popularity  (4102 Views)
Picture
Biography - Peter Bromberg
Peter Bromberg is a C# MVP, MCP, and .NET expert who has worked in banking, financial and telephony for over 20 years. Pete focuses exclusively on the .NET Platform, and currently develops SOA and other .NET applications for a Fortune 500 clientele. Peter enjoys producing digital photo collage with Maya,playing jazz flute, the beach, and fine wines. You can view Peter's UnBlog and IttyUrl sites. Follow Microsoft MVP
Create New Account
Article Discussion: How to Get Your Silverlight Pages Indexed By Search Engines
Peter Bromberg posted at Thursday, November 05, 2009 7:35 PM
reply
Not sure how this will work
Jim McCurdy replied to Peter Bromberg at Saturday, October 16, 2010 1:37 AM

Peter,

I am not sure how your Silverlight app code (to set the keywords, title, and description) could ever work in a situation where a search engine renders a page.   When a search engine crawls a page with a Silverlight <object> tag, the Sliverlight plugin will not be instantiated, and the <object> tag will only render its alternate content.  As far as I know, no search engines instantiate Silverlight apps. 

So the real solution is to have your ASP.NET page dynamically change the <object>'s alternate content and internal links based on navigational clues like the url path or the querystring.

Check out my financial planning app at http://yinyangmoney.com.  If you run an instamce of IE or Firefox without Add-ins, you will see the page the way the search engines see it.

For launch Internet Explorer with no add-ons:

         Start, Run, "%ProgramFiles%\Internet Explorer\iexplore.exe" -extoff

For launch Firefox with no add-ons:

         Start, Run, "%ProgramFiles%\Mozilla Firefox\firefox.exe" -safe-mode

Then view my site at http://yinyangmoney.com.  What I do is generate the <object>'s alternate content and nternal links for the "page" based on the url path.  When you visit the site when the Silverlight plugin instantiates, url rewriting can map the search engines urls into url fragments and feed the Silverlight navigation mechanism. 

Jim McCurdy
Face To Face Software

 

reply
You are right
Peter Bromberg replied to Jim McCurdy at Saturday, October 16, 2010 1:37 AM
This only occurred to me after finishing the article. I will revise it based on bot recognition to present alternate indexable content.  Thanks.
reply
fails for FireFox
Arun Mahendrakar replied to Peter Bromberg at Saturday, October 16, 2010 1:37 AM
The SetAttribute line fails when run using FireFox.
reply
Try using SetProperty instead of SetAttribute.
Peter Bromberg replied to Arun Mahendrakar at Saturday, October 16, 2010 1:37 AM

with Firefox, that should do the trick.

reply
this is what worked for me
Arun Mahendrakar replied to Peter Bromberg at Saturday, October 16, 2010 1:37 AM
Instead of
keys.SetAttribute("name=\"keywords\" content", keywords);
you can do:
metaKeywords = HtmlPage.Document.CreateElement("meta");
metaKeywords.SetAttribute("content", "keywords go here");
metaKeywords.SetAttribute("name", "keywords");
headElem.AppendChild(metaKeywords);

SetProperty can be used in the same way and it works for both IE and FF.

If in case someone needs to get the existing tag, they can use the below code:

// get the meta elements collection
var metaCollection = HtmlPage.Document.GetElementsByTagName("meta");
HtmlElement metaKeywords;
bool metaKeywordsFound = false;
bool metaDescriptionFound = false;

// i've missed to check if metaCollection is null here
// then loop through the elements
for (int i = 0; i < metaCollection.Count; i++)
{
    switch (metaCollection[i].GetProperty("name").ToString())
    {
        case "keywords":
            // cast a ScriptObject to HtmlElement
            metaKeywords = (HtmlElement) metaCollection[i];
            metaKeywordsFound = true;
            metaKeywords.SetAttribute("content", "new keywords go here");
            break;
        case "description":
            metaDescriptionFound = true;
            break;
    }
}

if (!metaKeywordsFound)
{
    metaKeywords = HtmlPage.Document.CreateElement("meta");
    metaKeywords.SetAttribute("content", "original keywords go here");
    metaKeywords.SetAttribute("name", "keywords");
    headElem.AppendChild(metaKeywords);
}
reply
moutlou dogan replied to Peter Bromberg at Saturday, October 16, 2010 1:37 AM
Hi, when i try this on local as well as a client server i get FF does not loading the sl4 page at all! is there anything i 've done wrong here? as soon as i delete or remove the Global.asax page then goes back to normal. Any ideas or help will appreciated. Thanks.
reply