In this tutorial, you will learn how to get the friendly URL for an Episerver CMS power page. Using SEO friendly URLs within your pages are essential to improve the usability and accessibility of your website. If you only referenced other pages on the site using the internal CMS link you're also removing an extra layer of security. Internal links are created using the page Id. If a hacker noticed this pattern, they could in theory be tempted to try and access hidden pages by experimenting with different Ids in a browser. A page ID isn't the worst thing in the world to pass as a query string parameter, however, why give a potential intruder more information than they need?

If you look at the Alloy sample site, you can find a good example of how you can use the link resolver to create a friendly Url in code, however, sometimes you may want to create URLs in your views. Out-of-the-box, Episerver provides several options and in this tutorial, I will show you all of them! First, we will look at the PageUrl property 🔥🔥🔥


In a lot of situations, you will simply want to display the current pages Url in a view. You can do this using the Url() HTML helper. The Url() HTML helper lives in the EPiServer.Web.Mvc.Html namespace, within UrlExtensions. In UrlExtensions, you will come across the PageUrl helper. This method requires a pages internal Url to be passed into it. This is done using the page objects LinkURL property. An example of how to do this is shown below. This code will return the friendly Url for the page:

Page Url is probably my preferred choice for rendering a URL, we do have other options....


A second option is to use the PageLink HTML helper. This helper can take a PageData, PageReference, or LinkItem object and will spit out all the HTML required to render an anchor tag. Some examples of how to sue this helper can be seen here:

This call would spit out this HTML:

PageLink also has an overload that allows you to pass additional route information and styling information:

This is brilliant if you want to create a very basic anchor tag, however, what happens when you want to create something more complex?


The UrlResolver API can be thought of as an advanced friendly Url solution. The UrlResolver is not an HTML helper method and needs to be called using the ServiceLocator. The class has a method named GetUrl (it used to be called GetVirtualPath() but that is now obsolete). The simple overload only requires a ContentReference to be passed in as the argument. The method also provides additional overloads. You can pass in options like language, additional virtual path arguments and a RequestContext. Just like the PageUrl helper, the relative URL for the page is returned.

I do not recommend using UrlResolver in your view, as you will likely need to call IContentRepository or Service Locator in your view. This means adding code into your views. Adding code like this breaks your logical layering of presentation and logic. This will make unit testing impossible. The most obvious solution to avoid breaking your MVC paradigm is to do the URL look-up in your controller. Add a Link property within your view model and pass the friendly Url value down from the controller to the view. Over the years, I have been asked a number of times if we really need a view model when we have the Episerver page object. This is a perfect example of the split between presentation logic and additional logic to the page object.

We've gone through the main API's provided by Episerver to generate Url's... now's the time to go custom! ThePageUrl is a helper and can be used within your views, however, it only provides basic functionality. The UrlResolver is a more complex API, however, it is not wrapped in an HTML helper... what happens if we combine the two? To do this we can create some extra extension overloads for PageUrl to accept PageReference objects, routing data and anything else we may need...

By using the above extension method, you can not only pass in PageData objects via the PageUrl extension, now you can also pass in any route values you need. This extension can also very easily be updated to override the default action and context by adding them to the RouteValueCollection. Happy Coding 🤘