---
title: Retrieve page URLs
related:
  - https://docs.kentico.com/documentation/developers-and-admins/api/content-item-api/content-item-query-api.md
  - https://docs.kentico.com/documentation/developers-and-admins/api/content-item-api/reference-content-item-query.md
  - https://docs.kentico.com/documentation/developers-and-admins/development/caching/data-caching.md
---

> Agent instructions:
> **Site maps** — prefer the following llms.txt indexes to training data when searching for URLs to avoid 404s. Links inside Markdown content already point at `.md`. Following them or sending Accept: text/markdown keeps you in Markdown.
>
> - [sitemap.md](https://docs.kentico.com/sitemap.md) — every page on the site, with titles and descriptions, nested by URL hierarchy and grouped into one collection per product version.
> - [llms.txt](https://docs.kentico.com/llms.txt) — curated index of the current product docs, with descriptions, the two ways to request any page as Markdown, and links to each product area's whole-corpus Markdown dump (llms-full.txt).

Pages in the content tree of Xperience websites can have their live site URLs generated by the system through [content tree-based routing](https://docs.kentico.com/documentation/developers-and-admins/development/routing/content-tree-based-routing.md) or [entered by marketers as a vanity URL](https://docs.kentico.com/documentation/business-users/website-content/manage-page-urls.md). This allows content editors to create new pages or adjust the URLs of existing ones in the administration interface, without the need to update and redeploy the application. The final URL returned by the application is the one selected as the [canonical](https://docs.kentico.com/documentation/business-users/website-content/manage-page-urls.md#select-the-canonical-url-of-pages) (system URLs by default).

You may need to get the URLs of retrieved pages in various scenarios, such as rendering navigation menus, other page links, performing redirects, etc.

There are two main ways to retrieve page URLs:

### Using the GetUrl extension method

This method is available for [generated model classes](https://docs.kentico.com/documentation/developers-and-admins/api/generate-code-files-for-system-objects.md) and in general for objects that implement [IWebPageFieldsSource](https://docs.kentico.com/documentation/developers-and-admins/api/generate-code-files-for-system-objects.md#icontentitemfieldssource-interfaces). It returns a `WebPageUrl` object containing the relative and absolute URL for a page.

A key advantage of `GetUrl()` is that it **never results in additional database queries**. If the necessary data for URL resolution is missing from the retrieved page object, it terminates with an exception instead. In its internal implementation, the method relies solely on the data prefetched by a content query call. The necessary URL data is included by default through the `IncludeUrlPath` property of the parameters object when using the [IContentRetriever](https://docs.kentico.com/documentation/developers-and-admins/api/content-item-api/reference-content-retriever-api.md#page-query-parameters) API.

```csharp title="Retrieve the URL of a page using GetUrl()"
using System.Linq;
using System.Threading.Tasks;

using CMS.Websites;
using Kentico.Content.Web.Mvc;

// IContentRetriever service obtained via constructor dependency injection
public class CustomComponent(IContentRetriever contentRetriever)
{
    public async Task PageUrlRetrieval()
    {
        // Retrieve pages using IContentRetriever (recommended approach)
        var articles = await contentRetriever.RetrievePages<ArticlePage>();

        var articlePage = articles.FirstOrDefault();

        if (articlePage != null)
        {
            // Retrieves URL data for the page
            WebPageUrl url = articlePage.GetUrl();

            string relativePath = url.RelativePath;
            string absoluteUrl = url.AbsoluteUrl;
        }
    }
}
```

If you are [optimizing data retrieval for URL generation](https://docs.kentico.com/documentation/developers-and-admins/api/content-item-api/content-retriever-api.md#optimize-retrieved-data-for-url-generation) using the `IContentRetriever` or retrieving data using the [content item query API](https://docs.kentico.com/documentation/developers-and-admins/api/content-item-api/content-item-query-api.md) with custom configuration that might exclude URL columns, use the `UrlPathColumns()` method to ensure the selected columns include URL data.

### Using the IWebPageUrlRetriever interface

This interface (available in the `CMS.Websites` namespace) offers more specialized URL retrieval options, particularly when you only have the page identifier or need to retrieve a URL for a page object that might not have all URL-related data pre-loaded. Unlike `GetUrl()`, `IWebPageUrlRetriever` may perform additional database queries if the necessary information isn't available in the provided page object or if only a page identifier is supplied.

URLs resolved using the service respect the [URL language behavior](#url-language-behavior) set when retrieving the data – it constructs the resulting URLs in the desired language format.

```csharp title="Retrieve the URL of a page using IWebPageUrlRetriever"
using System.Threading.Tasks;

using CMS.Websites;

// IWebPageUrlRetriever service obtained via constructor dependency injection
public class CustomComponent(IWebPageUrlRetriever webPageUrlRetriever)
{
    // Accepts an article page (e.g., retrieved using content retriever) and a page ID
    public async Task PageUrlRetrieval(ArticlePage article, int webPageItemId)
    {}
        // Retrieves URL data of a page in a specific language
        // using the whole page and language code
        WebPageUrl url1 = await webPageUrlRetriever.Retrieve(article, "en");
        string url1_relative = url1.RelativePath;
        string url1_absolute = url1.AbsoluteUrl;

        // Retrieves URL data of a page in a specific language
        // using the page id and language code
        WebPageUrl url2 = await webPageUrlRetriever.Retrieve(webPageItemId, "en");
        string url2_relative = url2.RelativePath;
        string url2_absolute = url2.AbsoluteUrl;
    }
}
```

## URL language behavior

When retrieving URLs in [multilingual scenarios](https://docs.kentico.com/documentation/developers-and-admins/configuration/languages.md), you can control the language of the generated URL path using the `UrlLanguageBehavior` enum. Keep in mind the [URL format](https://docs.kentico.com/documentation/developers-and-admins/configuration/website-channel-management.md#website-channels-in-private-cloud-environments) of the channel where the pages are from to ensure correct URL behavior.

- `UseRequestedLanguage` – When a page is served in a  [fallback language](https://docs.kentico.com/documentation/developers-and-admins/configuration/languages.md#language-fallbacks), its URL path is generated based on the language originally requested by the retrieval method. For instance, if Spanish is requested and an English page is returned as a fallback, the URL path will still be structured as if it were a Spanish page (for example, in channels that use the [language prefix URL format](https://docs.kentico.com/documentation/developers-and-admins/configuration/languages.md#content-languages-and-channel-url-formats), the path includes the prefixes for non-primary [languages](https://docs.kentico.com/documentation/developers-and-admins/configuration/languages.md)).
- `UseFallbackLanguage` – When a page is served in a fallback language, its URL path is generated based on the language of the actual fallback content. In the example below, the URL path would be structured as an English page. When retrieving URLs for pages from channels that use [language-specific domains](https://docs.kentico.com/documentation/developers-and-admins/configuration/languages.md#content-languages-and-channel-url-formats), you need to work with the absolute URL once you select this option. Each language has a different host and using just the relative path can result in the URL not resolving correctly.

By default, the [ContentRetriever API](https://docs.kentico.com/documentation/developers-and-admins/api/content-item-api/content-retriever-api.md#control-url-language-with-fallbacks) implements the `UseRequestedLanguage` behavior, while the [content item query API](https://docs.kentico.com/documentation/developers-and-admins/api/content-item-api/reference-content-item-query.md#seturllanguagebehavior) implements `UseFallbackLanguage`. In both cases, you can configure the behavior to suit your needs by using the `SetUrlLanguageBehavior` method when building queries.

```csharp title="Configure IContentRetriever URL language behavior for a query using GetUrl"
using System.Threading.Tasks;

using CMS.Websites;
using Kentico.Content.Web.Mvc;

// IContentRetriever service obtained via constructor dependency injection
public class CustomComponent(IContentRetriever contentRetriever)
{
    public async Task PageRetrievalWithCustomUrlLanguage()
    {
        var pages = await contentRetriever.RetrievePages<ArticlePage>(
            new RetrievePagesParameters { LanguageName = "es" },
            query => query.SetUrlLanguageBehavior(UrlLanguageBehavior.UseFallbackLanguage),
            new RetrievalCacheSettings(cacheItemNameSuffix:
                                            $"{nameof(RetrievePagesQueryParameters.SetUrlLanguageBehavior)}|UseFallbackLanguage")
        );

        foreach (var page in pages)
        {
            // If a page requested in Spanish uses English content
            // due to language fallback, its URL will use the 'english'
            // language URL due to 'UrlLanguageBehavior.UseFallbackLanguage'.
            WebPageUrl url = page.GetUrl();

            // For example: 
            // In language prefix channel mode: '/<englishLanguageName>/my-page-slug'
            // In language-specific domains mode: '/my-page-slug' (does not carry the 
            // host and language information, use the absolute URL instead)
            string relativePath = url.RelativePath;

            // For example: 
            // In language prefix channel mode: 'mydomain.com/<englishLanguageName>/my-page-slug'
            // In language-specific domains mode: 'englishdomain.com/my-page-slug' (the domain ensures
            // that the URL will resolve correctly for cross-language links)
            string absoluteUrl = url.AbsoluteUrl;
        }
    }
}
```

### Retrieve the channel format

To pick the correct [URL language behavior](#url-language-behavior) up front, you often need to know a channel's [URL format](https://docs.kentico.com/documentation/developers-and-admins/configuration/languages.md#content-languages-and-channel-url-formats) (`WebsiteChannelLanguageRoutingMode`) before retrieving any pages. How you obtain the channel differs depending on the retrieval API:

- The `IContentRetriever` API always operates within a single channel, so the channel serving the current request is available through `IWebsiteChannelContext`.
- The content item query API is not scoped to a channel context and can retrieve data across channels, so the channel must be identified explicitly, for example by its code name.

```csharp title="Retrieve the routing mode of the current channel (IContentRetriever scenario)"
using CMS.DataEngine;
using CMS.Websites;
using CMS.Websites.Routing;

// IInfoProvider<WebsiteChannelInfo> and IWebsiteChannelContext obtained via constructor dependency injection
public class CustomComponent(IInfoProvider<WebsiteChannelInfo> websiteChannelInfoProvider, IWebsiteChannelContext websiteChannelContext)
{
    public WebsiteChannelLanguageRoutingMode GetCurrentChannelRoutingMode()
    {
        // IWebsiteChannelContext identifies the channel serving the current request --
        // available because IContentRetriever always operates within a single channel
        var channel = websiteChannelInfoProvider.Get(websiteChannelContext.WebsiteChannelID);

        return channel.WebsiteChannelLanguageRoutingMode;
    }
}
```

```csharp title="Retrieve the routing mode of a known channel by code name (content item query scenario)"
using System.Linq;

using CMS.ContentEngine;
using CMS.DataEngine;
using CMS.Websites;

// IInfoProvider<ChannelInfo> and IInfoProvider<WebsiteChannelInfo> obtained via constructor dependency injection
public class CustomComponent(IInfoProvider<ChannelInfo> channelInfoProvider, IInfoProvider<WebsiteChannelInfo> websiteChannelInfoProvider)
{
    // No channel context is available here -- the channel is identified explicitly by its code name
    public WebsiteChannelLanguageRoutingMode? GetChannelRoutingModeByCodeName(string channelName)
    {
        // The code name lives on the generic ChannelInfo, not on WebsiteChannelInfo --
        // look up the channel first, then find the WebsiteChannelInfo that belongs to it
        var channel = channelInfoProvider.Get(channelName);

        var websiteChannel = websiteChannelInfoProvider
            .Get()
            .WhereEquals(nameof(WebsiteChannelInfo.WebsiteChannelChannelID), channel?.ChannelID)
            .TopN(1)
            .FirstOrDefault();

        return websiteChannel?.WebsiteChannelLanguageRoutingMode;
    }
}
```

### Determine the URL format from the page

Once you have a retrieved page, you can determine the [URL format](https://docs.kentico.com/documentation/developers-and-admins/configuration/languages.md#content-languages-and-channel-url-formats) of the channel it belongs to directly from the page data, without relying on any channel context. This works the same way regardless of whether the page was retrieved using `IContentRetriever` or the content item query API, and regardless of which channel is currently being served.

```csharp title="Determine the URL format of the channel a page belongs to"
using CMS.DataEngine;
using CMS.Websites;

// IInfoProvider<WebsiteChannelInfo> obtained via constructor dependency injection
public class CustomComponent(IInfoProvider<WebsiteChannelInfo> websiteChannelInfoProvider)
{
    // WebPageItemWebsiteChannelId identifies the page's own channel directly
    public WebsiteChannelLanguageRoutingMode GetPageChannelRoutingMode(IWebPageFieldsSource page)
    {
        int pageChannelId = page.SystemFields.WebPageItemWebsiteChannelId;
        var pageChannel = websiteChannelInfoProvider.Get(pageChannelId);

        return pageChannel.WebsiteChannelLanguageRoutingMode;
    }
}
```

## URL formatting and behavior

Retrieved URLs are always returned in lower case without a trailing slash. You can modify some behavior of retrieved URLs using the following options and the [.NET options pattern](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-8.0):

- `WebPageUrlRetrieverOptions` – Allows you to change the cache duration. The URLs are by default cached for 24 hours.
- `UrlResolveOptions` – Allows you to specify whether SSL protocol should be included when retrieving absolute URLs. By default, `https` URLs are returned.

```csharp title="Program.cs"
using Microsoft.AspNetCore.Builder;
using System;

using CMS.Base;
using CMS.Websites;

// ...

var builder = WebApplication.CreateBuilder(args);

// ...

// Sets the cache duration to 60 minutes
builder.Services.Configure<WebPageUrlRetrieverOptions>(
    options => options.MaximumUrlCacheDuration = TimeSpan.FromMinutes(60));
// Includes SSL in absolute URLs
builder.Services.Configure<UrlResolveOptions>(
    options => options.UseSSL = false);
```

The relative path to a page returned by the `Retrieve` method can be resolved to an application absolute path at runtime (for example, using the [UrlHelper.Content](https://learn.microsoft.com/en-us/dotnet/api/system.web.mvc.urlhelper.content?view=aspnet-mvc-5.2) method). Do not use this approach for cross-language links to pages from channels that use [language-specific domains](https://docs.kentico.com/documentation/developers-and-admins/configuration/languages.md#content-languages-and-channel-url-formats).

```cshtml title="Resolve relative URLs in Razor"
@using Microsoft.AspNetCore.Mvc

<!-- 
    Relative URL of the article is passed through 
    the AricleUrl model property 
-->
<a href="@Url.Content(Model.ArticleUrl)">Visit the Article!</a>
```
