BlogSEO redirects in .NET

SEO redirects in .NET

SEO.NET
8 May 2024

It's way more complicated to be unique nowadays. Having a website is one of the main ways to represent the company and basically, a must-have. But as important as to HAVE the website, it is also crucial to MAKE it easily searchable, especially in an environment of strong competition.

Photo by Merakist on Unsplash

Apart from that, the website must also be highly positioned in the search results and to make it happen, it must meet many of the SEO conditions.

It usually ends with making the website follow one common pattern in the URLs, such as allowing only lowercase characters, ending (or not) with trailing slashes, or forcing the user to use www at the beginning of the URI. Thankfully, .NET comes with easy-to-use middleware to add rewrite rules, helping meet the SEO requirements.

All that needs to be done is to add URL Rewrite middleware in the Configure method of the Startup file.

       app.UseRewriter(new RewriteOptions()
            .SkipStaticFiles()
            .SkipCmsUrls()
            .AddRedirectToTrailingSlashesPermanent()
            .AddRedirectToLowercaseUrlPermanent()
            .SkipRemainingRulesIfDxp()
            .AddRedirectToWwwPermanent());

Worth mentioning is that the order matters and the first one executed will be the top one, so it might be good to put the most frequently used rule there.

The rules in the example above use extension methods so the Startup file is clean and easy to read. The actual logic can be seen in the implementation of the methods. The first rule skips the static files, so the middleware doesn't apply the next rules when it discovers any file extension.

public static RewriteOptions SkipStaticFiles(this RewriteOptions options)
    {
        options.Add(context =>
        {
            bool hasFileExtension = Regex.IsMatch(context.HttpContext.Request.Path.Value, @".*/[^.]*\.[\d\w]+$");

            if (hasFileExtension)
            {
                context.Result = RuleResult.SkipRemainingRules;
            }
        });

        return options;
    }

The next one is related to the CMS environment where we don't want to force any redirects which probably will result in breaking the platform. It adds URLs patterns that should not be redirected by the next rules. We can see URLs like util or episerver known from Optimizely CMS. It is also important to add URLs of any of the external tools installed in the CMS, like Localization DB provider or GETA Sitemaps. They can malfunction without those rules.

    public static RewriteOptions SkipCmsUrls(this RewriteOptions options)
    {
        options.Add(context =>
        {
            if (context.HttpContext.Request.Path.StartsWithSegments("/util") ||
                context.HttpContext.Request.Path.StartsWithSegments("/episerver") ||
                context.HttpContext.Request.Path.StartsWithSegments("/modules") ||
                context.HttpContext.Request.Path.StartsWithSegments("/localization-admin") ||
                context.HttpContext.Request.Path.StartsWithSegments("/getaoptimizelysitemaps"))
            {
                context.Result = RuleResult.SkipRemainingRules;
            }
        });

        return options;
    }

Next, we have the trailing slashes rule which is just a simple regex redirection

    public static RewriteOptions AddRedirectToTrailingSlashesPermanent(this RewriteOptions options)
    {
        options.AddRedirect("(.*[^/])$", "$1/", (int)HttpStatusCode.MovedPermanently);
        return options;
    }

The AddRedirectToLowercaseUrlPermanent is a bit more complicated as it requires the creation of a custom implementation of the Microsoft.AspNetCore.Rewrite.IRule interface, which exposes a method called ApplyRule

public void ApplyRule(RewriteContext context)
    {
        var request = context.HttpContext.Request;
        var path = context.HttpContext.Request.Path;
        var host = context.HttpContext.Request.Host;

        if (path.HasValue && path.Value.Any(char.IsUpper) || host.HasValue && host.Value.Any(char.IsUpper))
        {
            var response = context.HttpContext.Response;
            response.StatusCode = StatusCode;
            response.Headers[HeaderNames.Location] =
                (request.Scheme + "://" + host.Value + request.PathBase.Value + request.Path.Value).ToLower() +
                request.QueryString;
            context.Result = RuleResult.EndResponse;
        }
        else
        {
            context.Result = RuleResult.ContinueRules;
        }
    }

The last rule is built in .NET and adds a permanent redirect to www. When doing this in the Optimizely environment called DXP it is important to remember that this redirection can break access by using the default DXP URLs. This is the main reason why before the www redirection rule there was a rule checking if there is a URL other than the DXP one. The rule is another implementation of the IRule which looks like this:

public void ApplyRule(RewriteContext context)
    {
        var host = context.HttpContext.Request.Host;

        if (host.HasValue && host.Value.EndsWith(".dxcloud.episerver.net"))
        {
            context.Result = RuleResult.SkipRemainingRules;
        }
        else
        {
            context.Result = RuleResult.ContinueRules;
        }
    }

That would be all. Now, our website can get a few more points in the global SEO race.

More articles

Credit cards
OptmizelyCommerce.NET

Custom payment in Optimizely Commerce 14

Create a payment flow perfectly suitable to your individual needs

Colorful lego bricks
.NETOptimizelyWeb DevelopmentDesign Patterns

Global Components Builders

Implementing global common components every site consists of

Have a question?

Don't hesitate, and send me an email

smutek.damian95@gmail.com