Updated October 2026: code is now inline and tested on .NET 8 and .NET 10. The redirect rule now skips POST requests and handles PathBase, and the IIS rule has been corrected.
Quick Answer
ASP.NET Core routing is case-insensitive by default: /About, /about and /ABOUT all reach the same action. That is convenient for users but creates duplicate URLs for search engines. Fix it in two parts:
- Generated URLs: set
RouteOptions.LowercaseUrls = trueso every link ASP.NET Core builds is lowercase. - Incoming URLs: 301-redirect any request whose path contains uppercase letters to the lowercase version.
Examples:
- Generated URL:
Url.Action("Login", "Account")resolves to/account/logininstead of/Account/Login. - Incoming request:
https://mysite.com/MyPageredirects (301) tohttps://mysite.com/mypage.
ASP.NET Core (.NET 6, 8, 10)
The redirect rule
This rewrite rule lowercases the scheme, host and path but leaves the query string alone, since tokens and other query values are often case-sensitive. It only redirects GET and HEAD requests, because redirecting a POST would lose the form data.
using System.Net;
using Microsoft.AspNetCore.Rewrite;
using Microsoft.Net.Http.Headers;
public sealed class RedirectLowerCaseRule : IRule
{
public void ApplyRule(RewriteContext context)
{
HttpRequest request = context.HttpContext.Request;
// Only redirect safe requests; a 301 on a POST would drop the form body.
if (!HttpMethods.IsGet(request.Method) && !HttpMethods.IsHead(request.Method))
{
context.Result = RuleResult.ContinueRules;
return;
}
string path = request.PathBase.Value + request.Path.Value;
string host = request.Host.Value ?? string.Empty;
if (path.Any(char.IsUpper) || host.Any(char.IsUpper))
{
// Lowercase scheme, host and path. Leave the query string exactly as it was.
string location = $"{request.Scheme}://{host}{path}".ToLowerInvariant() + request.QueryString;
HttpResponse response = context.HttpContext.Response;
response.StatusCode = (int)HttpStatusCode.MovedPermanently;
response.Headers[HeaderNames.Location] = location;
context.Result = RuleResult.EndResponse;
return;
}
context.Result = RuleResult.ContinueRules;
}
}
Program.cs
// Program.cs
using Microsoft.AspNetCore.Rewrite;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
// 1) Make every URL that ASP.NET Core *generates* lowercase
// (Url.Action, asp-action/asp-controller tag helpers, RedirectToAction, LinkGenerator...).
builder.Services.Configure<RouteOptions>(options =>
{
options.LowercaseUrls = true;
options.LowercaseQueryStrings = false; // leave ?Token=AbC alone (often case-sensitive)
});
var app = builder.Build();
// 2) 301-redirect any *incoming* URL that contains uppercase letters.
// Put this early, before static files and routing.
app.UseRewriter(new RewriteOptions().Add(new RedirectLowerCaseRule()));
app.UseStaticFiles();
app.UseRouting();
app.MapDefaultControllerRoute();
app.MapGet("/about-us", () => "About");
app.Run();
With both pieces in place, a request for /About-Us?Token=AbC returns 301 to /about-us?Token=AbC, and every link generated by tag helpers (asp-controller, asp-action, asp-page), Url.Action, RedirectToAction and LinkGenerator is already lowercase, so visitors rarely hit the redirect at all.
ASP.NET (.NET Framework) and Any IIS Site
On IIS you can do the redirect without code using the URL Rewrite module. This also works in front of ASP.NET Core apps hosted on IIS, if you prefer configuration over code:
<!-- web.config (requires the IIS URL Rewrite module) -->
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Lowercase URLs" stopProcessing="true">
<match url="[A-Z]" ignoreCase="false" />
<conditions>
<!-- Only redirect GET/HEAD so form posts aren't broken -->
<add input="{REQUEST_METHOD}" pattern="^(GET|HEAD)$" />
</conditions>
<!-- {URL} is the path only; the query string is appended unchanged -->
<action type="Redirect" url="{ToLower:{URL}}" redirectType="Permanent" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
In classic ASP.NET MVC 5, you can also set routes.LowercaseUrls = true; in RouteConfig so Html.ActionLink and Url.Action produce lowercase links.
Things To Check
- Hard-coded links and JavaScript:
LowercaseUrlsonly affects URLs generated by routing. Links typed into views, fetch/AJAX calls and URLs in JavaScript need to be lowercase too, or they will go through a redirect (and POSTs won't be redirected at all). - Static files: on Linux, file names are case-sensitive. If you have
/images/Logo.pngon disk, the redirect to/images/logo.pngwill 404. Rename files to lowercase, or place the rewriter afterUseStaticFiles()if you can't. - Canonical tags: output a lowercase
<link rel="canonical">on every page as a second signal to search engines. - Query strings: they are left as-is on purpose. If you also want them lowercase, set
LowercaseQueryStrings = trueand lowercaserequest.QueryStringin the rule.
Why Lowercase URLs Matter for SEO
Search engines treat URLs as case-sensitive, so /Blog/My-Post and /blog/my-post are two different URLs with the same content. That splits links and ranking signals between duplicates. People also link to you with whatever casing they type; a 301 to one canonical lowercase URL consolidates all of those links.
Consistent casing also makes analytics cleaner: tools such as Google Analytics report each casing as a separate page unless every URL is normalized.
Frequently Asked Questions
Is ASP.NET Core routing case-sensitive?
No. Route matching in ASP.NET Core is case-insensitive by default, for both conventional and attribute routes. That is why uppercase and lowercase versions of a URL both work, and why you should redirect to one version.
What does LowercaseUrls do?
RouteOptions.LowercaseUrls makes the URLs that ASP.NET Core generates lowercase. It doesn't redirect incoming requests; you need a rewrite rule or middleware for that.
Is RedirectToAction case-sensitive?
No. RedirectToAction("Index", "Home") matches routes case-insensitively, and with LowercaseUrls = true the resulting Location header is lowercase.
Should the redirect be 301 or 302?
Use 301 (permanent). It tells search engines the lowercase URL is the real one and passes ranking signals to it.
Related: redirecting www and non-HTTPS traffic in ASP.NET Core.

