Detecting mobile device user agents in ASP.NET

If you're developing a mobile version of your website, usability should be one of your top priorities. Most sites will detect if you're using a mobile device, and automatically redirect you to the mobile version. I'm going to show you how to do this in ASP.NET.

Detecting a mobile device based on user agent

The first issue I ran into was coming up with a reliable way to determine if the device is a mobile device based on its user agent. My first urge was to use the detection built into ASP.NET:


I immediately lost any trust in this property after it failed to correctly identify Opera Mobile on my Windows Mobile phone. I suspect something similar will happen with the iPhone.

After browsing the dozens of common mobile device user agents, I came up with this method:

public static bool IsMobile(string userAgent)
    userAgent = userAgent.ToLower();

    return userAgent.Contains("iphone") '
         userAgent.Contains("ppc") '
         userAgent.Contains("windows ce") '
         userAgent.Contains("blackberry") '
         userAgent.Contains("opera mini") '
         userAgent.Contains("mobile") '
         userAgent.Contains("palm") '

I realize this method isn't perfect, but I believe it will correctly identify 99% of the mobile devices out there. You simply pass in the user agent, which is easy to get:


If you want to be a little more precise, you can come up with your own method by looking at the list of mobile agents, or you can use an updated list of mobile devices with detailed specifications. You can find that type of list here.

Have a better way to do it? Have a common device that my method doesn't work for? Please let me know in the comments!

Like this post? Please share it!

See a mistake? Edit this post!

Response.Redirect and Output Caching Trouble

I ran into an interesting issue with output caching. If you have a page that uses output caching and that page conditionally sends a redirect response, you need to be careful.


Let's say that you have a page that redirects to the mobile version of your website if they have a mobile browser. Let's suppose that you're using a standard, generic output caching directive:

<%@ OutputCache Duration="300" VaryByParam="*" %>

When the page renders, the rendered HTML will be cached. The next incoming request will get the cached HTML, and the page rendering and code execution will be completely avoided. If you get a visitor that is using a mobile device, your code to determine if the request should be redirected will never get executed.

Here is another way to look at this scenario:

  • Request #1: Page makes the decision to redirect to another page.
  • Result: Page not cached.
  • Request #2: Page makes the decision to render HTML.
  • Result: This HTML is now rendered and cached.
  • Request #3: **Doesn't matter.
  • Result: The cached HTML from request #2 is rendered, even if the page should be redirecting.

There are a couple of workarounds to keep your redirects working:

Turn off caching

Of course the simplest option is to simply turn of output caching for the page that needs to perform a conditional redirect. It goes without saying that you'll lose all the wonderful advantages of output caching.

Use a caching seed

Another option is to write some code in your applications "[GetVaryByCustomString]("; method that determines if it's a request that will be redirected. If so, a unique value is returned. This forces the page to re-render because it is not found in the page cache. Unfortunately, the code in your custom string method may get complicated very quickly.

If you're lucky, you'll be making the redirect decision based on something that can be automatically used to vary the output caching. For example, if your page redirects based on a URL parameter, you can simply use the VaryByParam="*" in your output cache directive (or specify the specific parameter). Keep in mind that each set of parameters supplied to the page will result in a separate cache entry. This could lead to excessive memory usage for some pages.

Use fragment caching

Of course you could avoid using output caching on the page itself. You simply use the page as a place for your redirect logic, not the content generation. Then, put the actual page generation logic and content into a UserControl. When you use output caching on that control, it won't affect the redirect logic in the page. The disadvantage of this technique is that you may have to do extra work separating your content into controls. You also won't get the full benefit of caching since some of the control tree still needs to be processed, and some parts of the page such as the MasterPage will not get cached.


Remember, this is only an issue if the page being cached makes a decision to redirect. If the page always performs a redirect, the page will not be cached (why are you using output caching then?). Obviously this issue isn't unique to Response.Redirect, but it is an issue you should be aware of.

One mischievous aspect of this issue is that if you test your site in a certain order, you won't see a problem. For example, if you first view the page under the circumstances needed to make it redirect, it will work. You'll only see the issue if you cause it to not redirect, and then try to get it to redirect.

Like this post? Please share it!

See a mistake? Edit this post!

Removing duplicate page addresses in MVC

As I mentioned before, there are a couple of SEO issues with MVC. I'll discuss two ways to get around the trailing slash issue. Recall what the issue was. MVC will happily serve up your URL's both with and without a trailing slash:


Search engines could potentially index both paths, and count them separately in their search results. The first solution is to make one of the versions return a 404 (page not found) response code. Hopefully, this would deter anyone from linking to the non-preferred version. We can do this by creating a custom route constraint, by implementing the IRouteConstraint interface:

public class TrailingSlashConstraint : IRouteConstraint
    private readonly bool _trailingSlash;

    public TrailingSlashConstraint(bool trailingSlash)
        _trailingSlash = trailingSlash;

    public bool Match(HttpContextBase httpContext, Route route, string parameterName,
        RouteValueDictionary values, RouteDirection routeDirection)
        return _trailingSlash == httpContext.Request.Url.LocalPath.EndsWith("/");

Then, when you define the route, supply an instance of the constraint:

routes.MapRoute("mobileVersion", "m",
    new { controller = "home", action = "mobile", id = "" },
    new TrailingSlashConstraint(false));

The second solution is to redirect the non-preferred version to the preferred version. We can do this by either creating a custom HTTP Module, or by using a canned product like the free UrlRewriter.NET.

Should I use a trailing slash or not?

This is one of the problems with MVC. In ASP.NET webforms, you could use a default page to avoid having to specify a file in the URL. This gets you the nice MVC style paths. The good part was that if someone accessed the version without a trailing slash, it would automatically redirect to the version with the trailing slash. So in ASP.NET WebForms, the trailing slash was obviously preferred since it was the default behavior.

Now, in ASP.NET MVC, they decided to make the default behavior the opposite. It does serve up both versions as I mentioned, but when it generates links, they don't have a trailing slash.

If you're long term solution is to use MVC, I recommend dropping the trailing slashes, just to avoid the pain. I'm really hoping that by the time the final version of MVC comes out, they'll give us an option to define routes that should have a trailing slash.

Like this post? Please share it!

See a mistake? Edit this post!

Jason Young I'm Jason Young, software engineer. This blog contains my opinions, of which my employer - Microsoft - may not share.

@ytechieGitHubLinkedInStack OverflowPersonal VLOG