Orchard Core URL Rewriting - Prompt Templates
Configure URL Rewriting and Redirects
You are an Orchard Core expert. Generate URL rewrite rules, redirect configurations, and custom rule sources for Orchard Core.
Guidelines
- Enable
OrchardCore.UrlRewriting for URL rewrite and redirect support.
- Rules are processed sequentially based on their position; the first matching rule wins.
- Use Redirect Rules (301/302/307/308) to send users to a new URL (browser address bar changes).
- Use Rewrite Rules to serve content from a different URL without changing the browser address bar.
- Patterns use regular expressions; use
^ and $ for exact matches.
- Use
$1, $2, etc. in substitution patterns to reference regex capture groups.
- Set
IsCaseInsensitive to true for case-insensitive pattern matching.
- Configure
QueryStringPolicy to Append (keep query strings) or Drop (discard them).
- Custom rule sources implement
IUrlRewriteRuleSource and are registered with services.AddRewriteRuleSource<T>().
- Use the admin UI drag-and-drop to reorder rules.
- Always seal classes.
Enabling URL Rewriting
{
"steps": [
{
"name": "Feature",
"enable": [
"OrchardCore.UrlRewriting"
],
"disable": []
}
]
}
Available Rule Types
| Rule Type |
Description |
Use Case |
| Redirect Rule |
Sends an HTTP redirect response (301/302/307/308) to the client. The browser URL changes. |
URL migrations, domain changes, SEO canonical URLs. |
| Rewrite Rule |
Modifies the request URL server-side. The browser URL stays the same. |
URL aliasing, serving content from different paths. |
Redirect Rule Properties
| Property |
Description |
Id |
Unique identifier. Leave empty to create new; match existing ID to update. |
Name |
Descriptive name for the rule. |
Pattern |
Regex pattern to match against the request URL. |
SubstitutionPattern |
Target URL for the redirect. Supports $1, $2 capture groups. |
IsCaseInsensitive |
When true, pattern matching ignores case. |
QueryStringPolicy |
Append keeps original query string; Drop discards it. |
RedirectType |
MovedPermanently (301), Found (302), TemporaryRedirect (307), or PermanentRedirect (308). |
Rewrite Rule Properties
| Property |
Description |
Id |
Unique identifier. Leave empty to create new; match existing ID to update. |
Name |
Descriptive name for the rule. |
Pattern |
Regex pattern to match against the request URL. |
SubstitutionPattern |
Target URL for the rewrite. Supports $1, $2 capture groups. |
IsCaseInsensitive |
When true, pattern matching ignores case. |
QueryStringPolicy |
Append keeps original query string; Drop discards it. |
SkipFurtherRules |
When true, no subsequent rules are processed if this rule matches. |
Redirect Type Reference
| Value |
HTTP Status |
Description |
Found |
302 |
Temporary redirect. |
MovedPermanently |
301 |
Permanent redirect; clients should update bookmarks. |
TemporaryRedirect |
307 |
Temporary redirect; preserves HTTP method (POST stays POST). |
PermanentRedirect |
308 |
Permanent redirect; preserves HTTP method. |
Recipe: Create Redirect and Rewrite Rules
{
"steps": [
{
"name": "UrlRewriting",
"Rules": [
{
"Source": "Redirect",
"Name": "Redirect old-page to new-page",
"Pattern": "^/old-page$",
"SubstitutionPattern": "/new-page",
"IsCaseInsensitive": true,
"QueryStringPolicy": "Append",
"RedirectType": "MovedPermanently"
},
{
"Source": "Redirect",
"Name": "Redirect legacy blog URLs",
"Pattern": "^/blog/post/(\\d+)$",
"SubstitutionPattern": "/articles/$1",
"IsCaseInsensitive": true,
"QueryStringPolicy": "Drop",
"RedirectType": "MovedPermanently"
},
{
"Source": "Rewrite",
"Name": "Serve media from img path",
"Pattern": "^/img/(.*)$",
"SubstitutionPattern": "/media/$1",
"IsCaseInsensitive": true,
"QueryStringPolicy": "Drop",
"SkipFurtherRules": true
}
]
}
]
}
Custom Rule Source Implementation
using Microsoft.AspNetCore.Rewrite;
using Microsoft.Extensions.Localization;
using OrchardCore.UrlRewriting;
using OrchardCore.UrlRewriting.Models;
namespace MyModule.UrlRewriting;
public sealed class CustomRuleSource : IUrlRewriteRuleSource
{
public const string SourceName = "CustomRule";
public CustomRuleSource(IStringLocalizer<CustomRuleSource> stringLocalizer)
{
DisplayName = stringLocalizer["Custom rule"];
Description = stringLocalizer["Custom URL rewrite rule"];
}
public string TechnicalName => SourceName;
public LocalizedString DisplayName { get; }
public LocalizedString Description { get; }
public void Configure(RewriteOptions options, RewriteRule rule)
{
// Read custom metadata from rule.Properties and add the corresponding ASP.NET Core rewrite rules to options.
}
}
Custom Rule Display Driver
using Microsoft.Extensions.DependencyInjection;
using OrchardCore.DisplayManagement.Handlers;
using OrchardCore.DisplayManagement.Views;
using OrchardCore.UrlRewriting;
using OrchardCore.UrlRewriting.Models;
namespace MyModule.UrlRewriting;
public sealed class CustomRuleDisplayDriver : DisplayDriver<RewriteRule>
{
public override IDisplayResult Edit(RewriteRule model, BuildEditorContext context)
{
return Initialize<CustomRuleViewModel>("CustomRule_Fields_Edit", viewModel =>
{
// Populate view model from model properties.
}).Location("Content");
}
public override async Task<IDisplayResult> UpdateAsync(RewriteRule model, UpdateEditorContext context)
{
var viewModel = new CustomRuleViewModel();
await context.Updater.TryUpdateModelAsync(viewModel, Prefix);
// Apply view model values to the model.
return Edit(model, context);
}
}
public class CustomRuleViewModel
{
public string CustomProperty { get; set; }
}
Registering a Custom Rule Source in Startup
using OrchardCore.DisplayManagement.Handlers;
using OrchardCore.Modules;
using OrchardCore.UrlRewriting.Models;
namespace MyModule;
public sealed class Startup : StartupBase
{
public override void ConfigureServices(IServiceCollection services)
{
services.AddRewriteRuleSource<CustomRuleSource>(CustomRuleSource.SourceName)
.AddScoped<IDisplayDriver<RewriteRule>, CustomRuleDisplayDriver>();
}
}
Common Redirect Patterns
| Scenario |
Pattern |
Substitution |
Type |
| Exact page redirect |
^/about-us$ |
/about |
MovedPermanently |
| Path prefix redirect |
^/old-blog/(.*)$ |
/blog/$1 |
MovedPermanently |
| Trailing slash removal |
^(.+)/$ |
$1 |
MovedPermanently |
| Legacy ID to slug |
^/post/(\\d+)$ |
/articles/$1 |
MovedPermanently |
Common Rewrite Patterns
| Scenario |
Pattern |
Substitution |
SkipFurtherRules |
| Media alias |
^/img/(.*)$ |
/media/$1 |
true |
| API versioning |
^/api/v1/(.*)$ |
/api/v2/$1 |
true |
| Clean URLs |
^/page/(.*)$ |
/content/$1 |
false |
1---2name: orchardcore-url-rewriting3description: Skill for configuring URL rewriting in Orchard Core. Covers redirect vs rewrite rules, regex pattern matching, rule source implementation, display drivers for custom rule types, recipe-based rule configuration, and URL rewriting best practices. Use this skill when requests mention Orchard Core URL Rewriting, Configure URL Rewriting and Redirects, Enabling URL Rewriting, Available Rule Types, Redirect Rule Properties, Rewrite Rule Properties, or closely related Orchard Core implementation, setup, extension, or troubleshooting work. Strong matches include work with OrchardCore.UrlRewriting, OrchardCore.UrlRewriting.Models, OrchardCore.UrlRewriting.Services, OrchardCore.DisplayManagement.Handlers, OrchardCore.DisplayManagement.Views, OrchardCore.Modules, IUrlRewriteRuleSource, CustomRule. It also helps with Redirect Rule Properties, Rewrite Rule Properties, Redirect Type Reference, plus the code patterns, admin flows, recipe steps, and referenced examples captured in this skill.4---56# Orchard Core URL Rewriting - Prompt Templates78## Configure URL Rewriting and Redirects910You are an Orchard Core expert. Generate URL rewrite rules, redirect configurations, and custom rule sources for Orchard Core.1112### Guidelines1314- Enable `OrchardCore.UrlRewriting` for URL rewrite and redirect support.15- Rules are processed sequentially based on their position; the first matching rule wins.16- Use **Redirect Rules** (301/302/307/308) to send users to a new URL (browser address bar changes).17- Use **Rewrite Rules** to serve content from a different URL without changing the browser address bar.18- Patterns use regular expressions; use `^` and `$` for exact matches.19- Use `$1`, `$2`, etc. in substitution patterns to reference regex capture groups.20- Set `IsCaseInsensitive` to `true` for case-insensitive pattern matching.21- Configure `QueryStringPolicy` to `Append` (keep query strings) or `Drop` (discard them).22- Custom rule sources implement `IUrlRewriteRuleSource` and are registered with `services.AddRewriteRuleSource<T>()`.23- Use the admin UI drag-and-drop to reorder rules.24- Always seal classes.2526### Enabling URL Rewriting2728```json29{30 "steps": [31 {32 "name": "Feature",33 "enable": [34 "OrchardCore.UrlRewriting"35 ],36 "disable": []37 }38 ]39}40```4142### Available Rule Types4344| Rule Type | Description | Use Case |45|-----------|-------------|----------|46| **Redirect Rule** | Sends an HTTP redirect response (301/302/307/308) to the client. The browser URL changes. | URL migrations, domain changes, SEO canonical URLs. |47| **Rewrite Rule** | Modifies the request URL server-side. The browser URL stays the same. | URL aliasing, serving content from different paths. |4849### Redirect Rule Properties5051| Property | Description |52|----------|-------------|53| `Id` | Unique identifier. Leave empty to create new; match existing ID to update. |54| `Name` | Descriptive name for the rule. |55| `Pattern` | Regex pattern to match against the request URL. |56| `SubstitutionPattern` | Target URL for the redirect. Supports `$1`, `$2` capture groups. |57| `IsCaseInsensitive` | When `true`, pattern matching ignores case. |58| `QueryStringPolicy` | `Append` keeps original query string; `Drop` discards it. |59| `RedirectType` | `MovedPermanently` (301), `Found` (302), `TemporaryRedirect` (307), or `PermanentRedirect` (308). |6061### Rewrite Rule Properties6263| Property | Description |64|----------|-------------|65| `Id` | Unique identifier. Leave empty to create new; match existing ID to update. |66| `Name` | Descriptive name for the rule. |67| `Pattern` | Regex pattern to match against the request URL. |68| `SubstitutionPattern` | Target URL for the rewrite. Supports `$1`, `$2` capture groups. |69| `IsCaseInsensitive` | When `true`, pattern matching ignores case. |70| `QueryStringPolicy` | `Append` keeps original query string; `Drop` discards it. |71| `SkipFurtherRules` | When `true`, no subsequent rules are processed if this rule matches. |7273### Redirect Type Reference7475| Value | HTTP Status | Description |76|-------|-------------|-------------|77| `Found` | 302 | Temporary redirect. |78| `MovedPermanently` | 301 | Permanent redirect; clients should update bookmarks. |79| `TemporaryRedirect` | 307 | Temporary redirect; preserves HTTP method (POST stays POST). |80| `PermanentRedirect` | 308 | Permanent redirect; preserves HTTP method. |8182### Recipe: Create Redirect and Rewrite Rules8384```json85{86 "steps": [87 {88 "name": "UrlRewriting",89 "Rules": [90 {91 "Source": "Redirect",92 "Name": "Redirect old-page to new-page",93 "Pattern": "^/old-page$",94 "SubstitutionPattern": "/new-page",95 "IsCaseInsensitive": true,96 "QueryStringPolicy": "Append",97 "RedirectType": "MovedPermanently"98 },99 {100 "Source": "Redirect",101 "Name": "Redirect legacy blog URLs",102 "Pattern": "^/blog/post/(\\d+)$",103 "SubstitutionPattern": "/articles/$1",104 "IsCaseInsensitive": true,105 "QueryStringPolicy": "Drop",106 "RedirectType": "MovedPermanently"107 },108 {109 "Source": "Rewrite",110 "Name": "Serve media from img path",111 "Pattern": "^/img/(.*)$",112 "SubstitutionPattern": "/media/$1",113 "IsCaseInsensitive": true,114 "QueryStringPolicy": "Drop",115 "SkipFurtherRules": true116 }117 ]118 }119 ]120}121```122123### Custom Rule Source Implementation124125```csharp126using Microsoft.AspNetCore.Rewrite;127using Microsoft.Extensions.Localization;128using OrchardCore.UrlRewriting;129using OrchardCore.UrlRewriting.Models;130131namespace MyModule.UrlRewriting;132133public sealed class CustomRuleSource : IUrlRewriteRuleSource134{135 public const string SourceName = "CustomRule";136137 public CustomRuleSource(IStringLocalizer<CustomRuleSource> stringLocalizer)138 {139 DisplayName = stringLocalizer["Custom rule"];140 Description = stringLocalizer["Custom URL rewrite rule"];141 }142143 public string TechnicalName => SourceName;144145 public LocalizedString DisplayName { get; }146147 public LocalizedString Description { get; }148149 public void Configure(RewriteOptions options, RewriteRule rule)150 {151 // Read custom metadata from rule.Properties and add the corresponding ASP.NET Core rewrite rules to options.152 }153}154```155156### Custom Rule Display Driver157158```csharp159using Microsoft.Extensions.DependencyInjection;160using OrchardCore.DisplayManagement.Handlers;161using OrchardCore.DisplayManagement.Views;162using OrchardCore.UrlRewriting;163using OrchardCore.UrlRewriting.Models;164165namespace MyModule.UrlRewriting;166167public sealed class CustomRuleDisplayDriver : DisplayDriver<RewriteRule>168{169 public override IDisplayResult Edit(RewriteRule model, BuildEditorContext context)170 {171 return Initialize<CustomRuleViewModel>("CustomRule_Fields_Edit", viewModel =>172 {173 // Populate view model from model properties.174 }).Location("Content");175 }176177 public override async Task<IDisplayResult> UpdateAsync(RewriteRule model, UpdateEditorContext context)178 {179 var viewModel = new CustomRuleViewModel();180 await context.Updater.TryUpdateModelAsync(viewModel, Prefix);181182 // Apply view model values to the model.183184 return Edit(model, context);185 }186}187188public class CustomRuleViewModel189{190 public string CustomProperty { get; set; }191}192```193194### Registering a Custom Rule Source in Startup195196```csharp197using OrchardCore.DisplayManagement.Handlers;198using OrchardCore.Modules;199using OrchardCore.UrlRewriting.Models;200201namespace MyModule;202203public sealed class Startup : StartupBase204{205 public override void ConfigureServices(IServiceCollection services)206 {207 services.AddRewriteRuleSource<CustomRuleSource>(CustomRuleSource.SourceName)208 .AddScoped<IDisplayDriver<RewriteRule>, CustomRuleDisplayDriver>();209 }210}211```212213### Common Redirect Patterns214215| Scenario | Pattern | Substitution | Type |216|----------|---------|--------------|------|217| Exact page redirect | `^/about-us$` | `/about` | `MovedPermanently` |218| Path prefix redirect | `^/old-blog/(.*)$` | `/blog/$1` | `MovedPermanently` |219| Trailing slash removal | `^(.+)/$` | `$1` | `MovedPermanently` |220| Legacy ID to slug | `^/post/(\\d+)$` | `/articles/$1` | `MovedPermanently` |221222### Common Rewrite Patterns223224| Scenario | Pattern | Substitution | SkipFurtherRules |225|----------|---------|--------------|------------------|226| Media alias | `^/img/(.*)$` | `/media/$1` | `true` |227| API versioning | `^/api/v1/(.*)$` | `/api/v2/$1` | `true` |228| Clean URLs | `^/page/(.*)$` | `/content/$1` | `false` |