ASP.NET Core에서 URL 재작성 미들웨어

작성자: Kirk LarkinRick Anderson

이 문서에서는 URL 재작성을 소개하며, ASP.NET Core 애플리케이션에서 URL 재작성 미들웨어를 사용하는 방법에 관한 지침을 제공합니다.

URL 재작성은 하나 이상의 미리 정의된 규칙을 기반으로 하는 요청 URL을 수정하는 작업입니다. URL 재작성은 위치와 주소가 밀접하게 연결되지 않도록 리소스 위치와 해당 주소 간의 추상화를 만듭니다. URL 재작성이 중요한 몇 가지 시나리오는 다음과 같습니다.

  • 서버 리소스를 일시적 또는 영구적으로 이동하거나 대체하고, 해당 리소스에 대한 안정적인 로케이터를 유지 관리합니다.
  • 여러 앱 또는 한 앱의 여러 영역 간에 요청 처리를 분리합니다.
  • 들어오는 요청의 URL 세그먼트를 제거, 추가, 또는 다시 구성합니다.
  • SEO(검색 엔진 최적화)에 맞게 공용 URL을 최적화합니다.
  • 친숙한 공용 URL을 사용하여 방문자가 리소스를 요청함으로써 반환되는 콘텐츠를 예측할 수 있도록 합니다.
  • 안전하지 않은 요청을 보안 엔드포인트로 리디렉션합니다.
  • 외부 사이트에서 자산을 자체의 콘텐츠에 연결하여 다른 사이트에서 호스팅된 정적 자산을 사용하는 핫 링크를 방지합니다.

URL 재작성은 애플리케이션의 성능을 저하할 수 있습니다. 규칙의 수와 복잡성을 제한하세요.

URL 리디렉션 및 URL 재작성

URL 리디렉션URL 재작성 간의 표현 차이는 미묘하지만 클라이언트에 리소스를 제공하는 데 더 중요한 의미가 있습니다. ASP.NET Core의 URL 재작성 미들웨어는 두 가지 모두에 대한 요구를 만족합니다.

URL 리디렉션은 클라이언트 쪽 작업과 관련되어 있습니다. 여기서 클라이언트는 원래 요청한 클라이언트와는 다른 주소로 리소스에 액세스하도록 지시받습니다. 이 경우 서버를 왕복해야 합니다. 클라이언트로 반환된 리디렉션 URL은 클라이언트가 리소스에 대한 새로운 요청을 만들 때 브라우저의 주소 표시줄에 나타나게 됩니다.

/resource/different-resource리디렉션되는 경우 서버는 임시 또는 영구 리디렉션을 나타내는 상태 코드와 함께 클라이언트가 /different-resource에서 리소스를 가져와야 한다고 응답합니다.

A WebAPI service endpoint has been temporarily changed from version 1 (v1) to version 2 (v2) on the server. A client makes a request to the service at the version 1 path /v1/api. The server sends back a 302 (Found) response with the new, temporary path for the service at version 2 /v2/api. The client makes a second request to the service at the redirect URL. The server responds with a 200 (OK) status code.

요청을 다른 URL로 리디렉션하는 경우 응답과 함께 상태 코드를 지정하여 영구 리디렉션 또는 임시 리디렉션인지의 여부를 나타낼 수 있습니다.

  • 301 - Moved Permanently 상태 코드는 리소스에 새 영구 URL이 있고, 리소스에 대한 이후의 모든 요청에서 새 URL을 사용해야 하는 경우에 사용합니다. 301 상태 코드를 받으면 클라이언트에서 응답을 캐시하고 다시 사용할 수 있습니다.

  • 302 - Found 상태 코드는 리디렉션이 일시적이거나 일반적으로 변경될 수 있는 위치에 사용됩니다. 302 상태 코드는 클라이언트에서 URL을 저장하지 않고 나중에 사용하지 못하도록 지시합니다.

상태 코드에 대한 자세한 내용은 RFC 9110: 상태 코드 정의를 참조하세요.

URL 재작성은 요청한 클라이언트와 다른 리소스 주소에서 리소스를 제공하는 서버 쪽 작업입니다. URL을 다시 작성하는 경우 서버를 왕복할 필요가 없습니다. 다시 작성된 URL은 클라이언트에 반환되지 않고 브라우저의 주소 표시줄에도 표시되지 않습니다.

/resource/different-resource다시 작성되면 서버에서 내부적으로/different-resource에 있는 리소스를 가져와서 반환합니다.

클라이언트는 다시 작성된 URL에서 리소스를 검색할 수 있지만, 요청을 만들고 응답을 받을 때 리소스가 다시 작성된 URL에 있음을 클라이언트에 알리지 않습니다.

A WebAPI service endpoint has been changed from version 1 (v1) to version 2 (v2) on the server. A client makes a request to the service at the version 1 path /v1/api. The request URL is rewritten to access the service at the version 2 path /v2/api. The service responds to the client with a 200 (OK) status code.

URL 재작성 예제 응용 프로그램

샘플 앱을 사용하여 URL 재작성 미들웨어의 기능을 살펴봅니다. 이 앱은 리디렉션 및 재작성 규칙을 적용하고, 여러 시나리오에 대해 리디렉션되거나 다시 작성된 URL을 표시합니다.

URL 재작성 미들웨어를 사용해야 하는 경우

다음 방법이 만족스럽지 않은 경우 URL 재작성 미들웨어를 사용합니다.

URL 재작성 미들웨어는 앱이 HTTP.sys 서버에서 호스팅될 때 사용합니다.

IIS, Apache 및 Nginx에서 서버 기반 URL 재작성 기술을 사용하는 주요 이유는 다음과 같습니다.

  • 미들웨어에서 이러한 모듈의 전체 기능을 지원하지 않습니다.

    서버 모듈의 일부 기능이 IIS 재작성 모듈의 IsFileIsDirectory 제약 조건과 같은 ASP.NET Core 프로젝트에서 작동하지 않습니다. 바로 이런 시나리오에서 대신 미들웨어를 사용합니다.

  • 미들웨어의 성능이 아마도 모듈의 성능과 일치하지 않습니다.

    벤치마킹은 성능을 가장 많이 저하하는 방법 또는 저하된 성능을 무시할 수 있는 경우를 확인하는 유일한 방법입니다.

확장 및 옵션

각각의 재작성 규칙에 대한 확장 메서드를 사용하여 RewriteOptions 클래스의 인스턴스를 만들어 URL 재작성 및 리디렉션 규칙을 설정합니다. 여러 규칙을 처리 순서대로 연결합니다. RewriteOptionsUseRewriter를 사용하여 요청 파이프라인에 추가될 때 URL 재작성 미들웨어에 전달됩니다.

using Microsoft.AspNetCore.Rewrite;
using RewriteRules;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

using (StreamReader apacheModRewriteStreamReader =
    File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
    File.OpenText("IISUrlRewrite.xml"))
{
    var options = new RewriteOptions()
        .AddRedirect("redirect-rule/(.*)", "redirected/$1")
        .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
            skipRemainingRules: true)
        .AddApacheModRewrite(apacheModRewriteStreamReader)
        .AddIISUrlRewrite(iisUrlRewriteStreamReader)
        .Add(MethodRules.RedirectXmlFileRequests)
        .Add(MethodRules.RewriteTextFileRequests)
        .Add(new RedirectImageRequests(".png", "/png-images"))
        .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

    app.UseRewriter(options);
}

app.UseStaticFiles();

app.Run(context => context.Response.WriteAsync(
    $"Rewritten or Redirected Url: " +
    $"{context.Request.Path + context.Request.QueryString}"));

app.Run();

앞의 코드에서 MethodRules는 사용자가 정의한 클래스입니다. 자세한 내용은 이 문서의 RewriteRules.cs를 참조하세요.

www 이외 요청을 www로 리디렉션

이러한 옵션은 앱이 www 이외 요청을 www로 리디렉션하도록 허용합니다.

  • AddRedirectToWwwPermanent: 요청 www 이 아닌www 경우 하위 기본 요청을 영구적으로 리디렉션합니다. Status308PermanentRedirect 상태 코드를 사용하여 리디렉션합니다.

  • AddRedirectToWww: 들어오는 요청 www 이 아닌 경우 하위 기본 요청을www 리디렉션합니다. Status307TemporaryRedirect 상태 코드를 사용하여 리디렉션합니다. 오버로드를 사용하면 응답에 대한 상태 코드를 제공할 수 있습니다. 상태 코드 할당을 위해 StatusCodes 클래스의 필드를 사용합니다.

URL 리디렉션

요청을 리디렉션하려면 AddRedirect를 사용합니다. 첫 번째 매개 변수에는 들어오는 URL의 경로와 일치하는 .NET 정규식(Regex)이 있습니다. 두 번째 매개 변수는 대체 문자열입니다. 필요한 경우 세 번째 매개 변수로 상태 코드를 지정할 수 있습니다. 상태 코드를 지정하지 않으면 상태 코드는 기본적으로 302 - 있음으로 설정되며, 이는 리소스가 일시적으로 이동하거나 대체되었음을 나타냅니다.

using Microsoft.AspNetCore.Rewrite;
using RewriteRules;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

using (StreamReader apacheModRewriteStreamReader =
    File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
    File.OpenText("IISUrlRewrite.xml"))
{
    var options = new RewriteOptions()
        .AddRedirect("redirect-rule/(.*)", "redirected/$1")
        .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
            skipRemainingRules: true)
        .AddApacheModRewrite(apacheModRewriteStreamReader)
        .AddIISUrlRewrite(iisUrlRewriteStreamReader)
        .Add(MethodRules.RedirectXmlFileRequests)
        .Add(MethodRules.RewriteTextFileRequests)
        .Add(new RedirectImageRequests(".png", "/png-images"))
        .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

    app.UseRewriter(options);
}

app.UseStaticFiles();

app.Run(context => context.Response.WriteAsync(
    $"Rewritten or Redirected Url: " +
    $"{context.Request.Path + context.Request.QueryString}"));

app.Run();

개발자 도구가 활성화된 브라우저에서 예제 응용 프로그램에 /redirect-rule/1234/5678 경로를 요청합니다. 정규식이 redirect-rule/(.*)의 요청 경로와 일치하므로 /redirected/1234/5678로 경로가 치환됩니다. 리디렉션 URL은 302 - 있음 상태 코드와 함께 클라이언트로 다시 전송됩니다. 브라우저는 리디렉션 URL에 대한 새로운 요청을 만들고 이 주소는 브라우저의 주소 표시줄에 출력됩니다. 샘플 앱의 규칙이 리디렉션 URL에서 일치하지 않으므로 다음과 같이 수행됩니다.

  • 두 번째 요청에서 앱의 200 - 정상 응답을 받습니다.
  • 응답 본문에 리디렉션 URL이 표시됩니다.

URL이 리디렉션되면 서버로의 왕복이 수행됩니다.

Warning

리디렉션 규칙을 설정할 때에는 주의하세요. 리디렉션 규칙은 리디렉션 이후를 포함하여 앱에 대한 모든 요청에서 평가됩니다. 따라서 무한 리디렉션 루프가 의도치 않게 자주 생성됩니다.

표현식에서 괄호로 둘러쌓인 부분을 캡처 그룹(Capture Group)이라고 합니다. 그리고 표현식에서 마침표(.)는 모든 문자와 일치함을 뜻합니다. 마지막으로 별표(*)는 앞의 문자와 0번 이상 일치함을 나타냅니다. 따라서 URL의 마지막 두 세그먼트, 1234/5678은 캡쳐 그룹 (.*)에 의해 캡쳐됩니다. 요청 URL에서 redirect-rule/ 이후에 제공하는 모든 값은 이 단일 캡처 그룹에 의해 캡처됩니다.

대체 문자열에서, 캡처된 그룹은 캡처의 일련번호가 뒤에 붙는 달러 기호($)를 통해서 문자열에 삽입됩니다. 첫 번째 캡처 그룹 값은 $1로 얻을 수 있고, 두 번째 캡처 그룹 값은 $2로 얻을 수 있습니다. 이 작업은 정규식에 포함된 캡처 그룹에 대해 순차적으로 계속 진행됩니다. redirect-rule/(.*)의 리디렉션 규칙 정규식에 캡처된 그룹이 하나뿐이므로, 대체 문자열에 삽입되는 그룹도 $1 하나뿐입니다. 규칙이 적용되고 나면 URL은 /redirected/1234/5678로 변환됩니다.

네트워크 탭에서 브라우저 도구를 이용해 /redirect-rule/1234/5678을 사용해 보세요.

보안 엔드포인트에 대한 URL 리디렉션

AddRedirectToHttps를 사용하여 HTTPS 프로토콜을 통해 HTTP 요청을 동일한 호스트 및 경로로 리디렉션할 수 있습니다. 상태 코드가 제공되지 않는 경우 미들웨어는 기본적으로 302 - 있음으로 설정됩니다. 포트가 제공되지 않는 경우 다음과 같이 수행됩니다.

  • 미들웨어가 기본적으로 null로 설정됩니다.
  • 체계가 https(HTTPS 프로토콜)로 변경되고 클라이언트에서 443 포트의 리소스에 액세스합니다.

다음 예제에서는 상태 코드를 301 - Moved Permanently로 설정하고 localhost에서 Kestrel이 사용하는 HTTPS 포트로 포트를 변경하는 방법을 확인할 수 있습니다. 프로덕션에서는 HTTPS 포트가 null로 설정됩니다.

using Microsoft.AspNetCore.Rewrite;
using RewriteRules;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

int? localhostHTTPSport = null;
if (app.Environment.IsDevelopment())
{
    localhostHTTPSport = Int32.Parse(Environment.GetEnvironmentVariable(
                   "ASPNETCORE_URLS")!.Split(new Char[] { ':', ';' })[2]);
}

using (StreamReader apacheModRewriteStreamReader =
    File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
    File.OpenText("IISUrlRewrite.xml"))
{
    var options = new RewriteOptions()
        // localhostHTTPport not needed for production, used only with localhost.
        .AddRedirectToHttps(301, localhostHTTPSport)
        .AddRedirect("redirect-rule/(.*)", "redirected/$1")
        .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
            skipRemainingRules: true)
        .AddApacheModRewrite(apacheModRewriteStreamReader)
        .AddIISUrlRewrite(iisUrlRewriteStreamReader)
        .Add(MethodRules.RedirectXmlFileRequests)
        .Add(MethodRules.RewriteTextFileRequests)
        .Add(new RedirectImageRequests(".png", "/png-images"))
        .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

    app.UseRewriter(options);
}

app.UseStaticFiles();

app.Run(context => context.Response.WriteAsync(
    $"Rewritten or Redirected Url: " +
    $"{context.Request.Path + context.Request.QueryString}"));

app.Run();

AddRedirectToHttpsPermanent를 사용하여 443 포트의 HTTPS 프로토콜을 통해 안전하지 않은 요청을 동일한 호스트 및 경로로 리디렉션합니다. 미들웨어는 상태 코드를 301 - Moved Permanently로 설정합니다.

using Microsoft.AspNetCore.Rewrite;
using RewriteRules;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

using (StreamReader apacheModRewriteStreamReader =
    File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
    File.OpenText("IISUrlRewrite.xml"))
{
    var options = new RewriteOptions()
        .AddRedirectToHttpsPermanent()
        .AddRedirect("redirect-rule/(.*)", "redirected/$1")
        .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
            skipRemainingRules: true)
        .AddApacheModRewrite(apacheModRewriteStreamReader)
        .AddIISUrlRewrite(iisUrlRewriteStreamReader)
        .Add(MethodRules.RedirectXmlFileRequests)
        .Add(MethodRules.RewriteTextFileRequests)
        .Add(new RedirectImageRequests(".png", "/png-images"))
        .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

    app.UseRewriter(options);
}

app.UseStaticFiles();

app.Run(context => context.Response.WriteAsync(
    $"Rewritten or Redirected Url: " +
    $"{context.Request.Path + context.Request.QueryString}"));

app.Run();

참고 항목

추가 리디렉션 규칙을 요구하지 않고 보안 엔드포인트로 리디렉션하는 경우 HTTPS 리디렉션 미들웨어를 사용하는 것이 좋습니다. 자세한 내용은 HTTPS 적용을 참조하세요.

샘플 앱은 AddRedirectToHttps 또는 AddRedirectToHttpsPermanent를 사용하는 방법을 보여줍니다. 에서 앱에 안전하지 않은 HTTP 요청을 만듭니다 http://redirect6.azurewebsites.net/iis-rules-rewrite/xyz. localhost를 사용하여 HTTP에서 HTTPS로의 리디렉션을 테스트하는 경우:

  • HTTPS URL과는 다른 포트를 사용하는 HTTP URL을 사용합니다. HTTP URL은 Properties/launchSettings.json 파일에 있습니다.
  • localhost가 HTTPS 포트에 대한 HTTP에서 응답하지 않으므로 https://localhost/{port}에서 s를 제거하지 못하게 됩니다.

다음 이미지는 앞의 코드를 사용하여 http://redirect6.azurewebsites.net/iis-rules-rewrite/xyz에 요청하는 F12 브라우저 도구의 이미지입니다.

Browser window with developer tools tracking the requests and responses: Add redirect to HTTPS

URL 재작성

URL을 재작성하는 규칙을 만들려면 AddRewrite를 사용합니다. 첫 번째 매개 변수에는 들어오는 URL의 경로에서 일치하는 정규식이 포함되어 있습니다. 두 번째 매개 변수는 대체 문자열입니다. 세 번째 매개 변수, skipRemainingRules: {true|false}는 현재 규칙이 적용되는 경우에 추가 재작성 규칙을 건너뛸 것인지 여부를 미들웨어에 나타냅니다.

using Microsoft.AspNetCore.Rewrite;
using RewriteRules;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

using (StreamReader apacheModRewriteStreamReader =
    File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
    File.OpenText("IISUrlRewrite.xml"))
{
    var options = new RewriteOptions()
        .AddRedirectToHttpsPermanent()
        .AddRedirect("redirect-rule/(.*)", "redirected/$1")
        .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
            skipRemainingRules: true)
        .AddApacheModRewrite(apacheModRewriteStreamReader)
        .AddIISUrlRewrite(iisUrlRewriteStreamReader)
        .Add(MethodRules.RedirectXmlFileRequests)
        .Add(MethodRules.RewriteTextFileRequests)
        .Add(new RedirectImageRequests(".png", "/png-images"))
        .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

    app.UseRewriter(options);
}

app.UseStaticFiles();

app.Run(context => context.Response.WriteAsync(
    $"Rewritten or Redirected Url: " +
    $"{context.Request.Path + context.Request.QueryString}"));

app.Run();

https://redirect6.azurewebsites.net/rewrite-rule/1234/5678에 요청 시도

식의 시작 부분에 있는 캐럿(^)은 URL 경로의 시작 부분에서 일치가 시작됨을 의미합니다.

redirect-rule/(.*) 리디렉션 규칙이 있는 이전 예제에는 정규식의 시작 부분에 캐럿(^)이 없습니다. 따라서 일치하는 모든 문자가 경로의 redirect-rule/ 앞에 나올 수 있습니다.

Path Match
/redirect-rule/1234/5678
/my-cool-redirect-rule/1234/5678
/anotherredirect-rule/1234/5678

반면 ^rewrite-rule/(\d+)/(\d+) 재작성 규칙의 경우에는 오로지 rewrite-rule/로 시작하는 경로만 일치합니다. 다음 표에는 일치에서의 차이가 나와 있습니다.

Path Match
/rewrite-rule/1234/5678
/my-cool-rewrite-rule/1234/5678 없음
/anotherrewrite-rule/1234/5678 아니요

식의 ^rewrite-rule/ 부분을 따릅니다. 두 개의 캡처 그룹, (\d+)/(\d+)가 있습니다. 여기서 \d숫자 하나와 일치함을 뜻합니다. 그리고 더하기 기호(+)는 앞의 문자와 한 번 이상 일치함을 나타냅니다. 따라서 URL은 반드시 숫자 뒤에 슬래시와 다른 숫자가 연이어 나타나는 부분을 포함해야 합니다. 이 캡쳐 그룹들은 $1$2를 통해서 재작성 URL에 삽입됩니다. 재작성 규칙의 대체 문자열은 캡처된 그룹을 쿼리 문자열에 배치합니다. 요청 경로 /rewrite-rule/1234/5678/rewritten?var1=1234&var2=5678에서 리소스를 반환하도록 재작성됩니다. 원본 요청에 쿼리 문자열이 있으면 URL을 다시 작성할 때 유지됩니다.

리소스를 반환하기 위해 서버를 왕복하지는 않습니다. 리소스가 있으면 이를 가져와서 200 - 정상 상태 코드와 함께 클라이언트에 반환합니다. 클라이언트는 리디렉션 되지 않으므로 브라우저 주소 표시줄의 URL은 변경되지 않습니다. 클라이언트는 서버에서 URL 재작성 작업이 발생했음을 검색할 수 없습니다.

URL 재작성 및 리디렉션 관련 성능 팁

가장 빠른 응답을 원하는 경우:

  • 재작성 규칙을 가장 자주 일치하는 규칙에서 가장 드물게 일치하는 규칙으로의 순서로 정렬합니다.
  • 일치 규칙은 컴퓨팅 측면에서 비용이 많이 들고 앱의 응답 속도가 증가되므로 가능한 경우 skipRemainingRules: true를 사용합니다. 일치가 발생하고 추가적인 규칙 처리가 필요하지 않다면 나머지 규칙의 처리를 생략합니다.

Warning

악의적인 사용자가 RegularExpressions에 비용이 많이 드는 프로세스 입력을 제공하여 서비스 거부 공격을 일으킬 수 있습니다. RegularExpressions를 사용하는 ASP.NET Core Framework API는 시간 제한을 전달합니다. 예를 들어 RedirectRuleRewriteRule 클래스는 모두 1초 제한 시간을 전달합니다.

Apache mod_rewrite

AddApacheModRewrite를 사용하면 Apache mod_rewrite 규칙을 적용할 수 있습니다. 규칙 파일이 응용 프로그램과 함께 배포되고 있는지 확인하시기 바랍니다. mod_rewrite 규칙에 대한 자세한 내용 및 예제는 Apache mod_rewrite를 참조하세요.

StreamReaderApacheModRewrite.txt 규칙 파일에서 규칙을 읽는 데 사용됩니다.

using Microsoft.AspNetCore.Rewrite;
using RewriteRules;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

using (StreamReader apacheModRewriteStreamReader =
    File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
    File.OpenText("IISUrlRewrite.xml"))
{
    var options = new RewriteOptions()
        .AddRedirectToHttpsPermanent()
        .AddRedirect("redirect-rule/(.*)", "redirected/$1")
        .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
            skipRemainingRules: true)
        .AddApacheModRewrite(apacheModRewriteStreamReader)
        .AddIISUrlRewrite(iisUrlRewriteStreamReader)
        .Add(MethodRules.RedirectXmlFileRequests)
        .Add(MethodRules.RewriteTextFileRequests)
        .Add(new RedirectImageRequests(".png", "/png-images"))
        .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

    app.UseRewriter(options);
}

app.UseStaticFiles();

app.Run(context => context.Response.WriteAsync(
    $"Rewritten or Redirected Url: " +
    $"{context.Request.Path + context.Request.QueryString}"));

app.Run();

예제 응용 프로그램은 /apache-mod-rules-redirect/(.\*)에서 /redirected?id=$1로 요청을 리디렉션합니다. 응답 상태 코드는 302 - 있음입니다.

# Rewrite path with additional sub directory
RewriteRule ^/apache-mod-rules-redirect/(.*) /redirected?id=$1 [L,R=302]

https://redirect6.azurewebsites.net/apache-mod-rules-redirect/1234에 요청 시도

Apache 미들웨어는 다음 Apache mod_rewrite 서버 변수를 지원합니다.

  • CONN_REMOTE_ADDR
  • HTTP_ACCEPT
  • HTTP_CONNECTION
  • HTTP_COOKIE
  • HTTP_FORWARDED
  • HTTP_HOST
  • HTTP_REFERER
  • HTTP_USER_AGENT
  • HTTPS
  • IPV6
  • QUERY_STRING
  • REMOTE_ADDR
  • REMOTE_PORT
  • REQUEST_FILENAME
  • REQUEST_METHOD
  • REQUEST_SCHEME
  • REQUEST_URI
  • SCRIPT_FILENAME
  • SERVER_ADDR
  • SERVER_PORT
  • SERVER_PROTOCOL
  • TIME
  • TIME_DAY
  • TIME_HOUR
  • TIME_MIN
  • TIME_MON
  • TIME_SEC
  • TIME_WDAY
  • TIME_YEAR

IIS URL 재작성 모듈 규칙

IIS URL 재작성 모듈에 적용되는 것과 동일한 규칙 세트를 사용하려면 AddIISUrlRewrite를 사용합니다. 규칙 파일이 응용 프로그램과 함께 배포되고 있는지 확인하시기 바랍니다. Windows Server IIS에서 실행하는 경우 미들웨어에서 앱의 web.config 파일을 사용하도록 지시하지 않습니다. IIS를 사용하는 경우 IIS 재작성 모듈과 충돌하지 않도록 이러한 규칙을 앱의 web.config 파일 외부에 저장해야 합니다. IIS URL 재작성 모듈 규칙에 대한 자세한 내용 및 예제는 Url 재작성 모듈 2.0 사용URL 재작성 모듈 구성 참조를 참조하세요.

A StreamReader 는 규칙 파일에서 규칙을 읽는 IISUrlRewrite.xml 데 사용됩니다.

using Microsoft.AspNetCore.Rewrite;
using RewriteRules;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

using (StreamReader apacheModRewriteStreamReader =
    File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
    File.OpenText("IISUrlRewrite.xml"))
{
    var options = new RewriteOptions()
        .AddRedirectToHttpsPermanent()
        .AddRedirect("redirect-rule/(.*)", "redirected/$1")
        .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
            skipRemainingRules: true)
        .AddApacheModRewrite(apacheModRewriteStreamReader)
        .AddIISUrlRewrite(iisUrlRewriteStreamReader)
        .Add(MethodRules.RedirectXmlFileRequests)
        .Add(MethodRules.RewriteTextFileRequests)
        .Add(new RedirectImageRequests(".png", "/png-images"))
        .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

    app.UseRewriter(options);
}

app.UseStaticFiles();

app.Run(context => context.Response.WriteAsync(
    $"Rewritten or Redirected Url: " +
    $"{context.Request.Path + context.Request.QueryString}"));

app.Run();

예제 응용 프로그램은 /iis-rules-rewrite/(.*)에서 /rewritten?id=$1로 요청을 재작성합니다. 응답이 200 - 정상 상태 코드와 함께 클라이언트에 전송됩니다.

<rewrite>
  <rules>
    <rule name="Rewrite segment to id querystring" stopProcessing="true">
      <match url="^iis-rules-rewrite/(.*)$" />
      <action type="Rewrite" url="rewritten?id={R:1}" appendQueryString="false"/>
    </rule>
  </rules>
</rewrite>

https://redirect6.azurewebsites.net/iis-rules-rewrite/xyz에 요청 시도

서버 수준 규칙이 바람직하지 않은 방식으로 앱에 영향을 주도록 구성된 활성 IIS 재작성 모듈이 있는 앱:

  • 앱의 IIS 재작성 모듈을 사용하지 않도록 설정하는 것이 좋습니다.
  • 보다 자세한 내용은 IIS 모듈 비활성화를 참고하시기 바랍니다.

지원되지 않는 기능

미들웨어는 다음과 같은 IIS URL 재작성 모듈 기능을 지원하지 않습니다:

  • 아웃바운드 규칙
  • 사용자 지정 서버 변수
  • 와일드카드
  • LogRewrittenUrl

지원되는 서버 변수

미들웨어 다음 IIS URL 재작성 모듈 서버 변수를 지원합니다.

  • CONTENT_LENGTH
  • CONTENT_TYPE
  • HTTP_ACCEPT
  • HTTP_CONNECTION
  • HTTP_COOKIE
  • HTTP_HOST
  • HTTP_REFERER
  • HTTP_URL
  • HTTP_USER_AGENT
  • HTTPS
  • LOCAL_ADDR
  • QUERY_STRING
  • REMOTE_ADDR
  • REMOTE_PORT
  • REQUEST_FILENAME
  • REQUEST_URI

IFileProviderPhysicalFileProvider를 통해 얻을 수 있습니다. 이 방식이 재작성 규칙 파일의 위치에 대해 더 많은 유연성을 제공할 수 있습니다. 재작성 규칙 파일이 서버의 지정한 경로에 배포되는지 확인하시기 바랍니다.

var fileProvider = new PhysicalFileProvider(Directory.GetCurrentDirectory());

메서드 기반 규칙

메서드에서 사용자 지정 규칙 로직을 구현하고 싶다면 Add를 사용하면 됩니다. Add는 리디렉트 메서드에서 HttpContext를 사용할 수 있게 하는 RewriteContext를 공개합니다. RewriteContext.Result 속성은 추가적인 파이프라인 처리가 수행되는 방법을 결정합니다. 값을 다음 표에 설명된 RuleResult 필드 중 하나로 설정합니다.

컨텍스트 결과 다시 작성 작업
RuleResult.ContinueRules (기본값) 규칙 적용을 계속합니다.
RuleResult.EndResponse 규칙 적용을 중지하고 응답을 보냅니다.
RuleResult.SkipRemainingRules 규칙 적용을 중지하고 컨텍스트를 다음 미들웨어로 보냅니다.
using Microsoft.AspNetCore.Rewrite;
using RewriteRules;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

using (StreamReader apacheModRewriteStreamReader =
    File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
    File.OpenText("IISUrlRewrite.xml"))
{
    var options = new RewriteOptions()
        .AddRedirectToHttpsPermanent()
        .AddRedirect("redirect-rule/(.*)", "redirected/$1")
        .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
            skipRemainingRules: true)
        .AddApacheModRewrite(apacheModRewriteStreamReader)
        .AddIISUrlRewrite(iisUrlRewriteStreamReader)
        .Add(MethodRules.RedirectXmlFileRequests)
        .Add(MethodRules.RewriteTextFileRequests)
        .Add(new RedirectImageRequests(".png", "/png-images"))
        .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

    app.UseRewriter(options);
}

app.UseStaticFiles();

app.Run(context => context.Response.WriteAsync(
    $"Rewritten or Redirected Url: " +
    $"{context.Request.Path + context.Request.QueryString}"));

app.Run();

샘플 앱은 .로 끝나는 .xml경로에 대한 요청을 리디렉션하는 메서드를 보여 줍니다. /file.xml에 대해 요청하는 경우:

  • 요청은 /xmlfiles/file.xml으로 리디렉션됩니다.
  • 상태 코드가 301 - Moved Permanently로 설정됩니다. 브라우저에서 새 요청을 /xmlfiles/file.xml수행하면 정적 파일 미들웨어는 wwwroot/xmlfiles 폴더에서 클라이언트에 파일을 제공합니다. 리디렉션의 경우 응답의 상태 코드를 명시적으로 설정합니다. 그렇지 않으면 200 - 정상 상태 코드가 반환되고 클라이언트에서 리디렉션이 수행되지 않습니다.

RewriteRules.cs:

public static void RedirectXmlFileRequests(RewriteContext context)
{
    var request = context.HttpContext.Request;

    // Because the client is redirecting back to the same app, stop 
    // processing if the request has already been redirected.
    if (request.Path.StartsWithSegments(new PathString("/xmlfiles")) ||
        request.Path.Value==null)
    {
        return;
    }

    if (request.Path.Value.EndsWith(".xml", StringComparison.OrdinalIgnoreCase))
    {
        var response = context.HttpContext.Response;
        response.StatusCode = (int) HttpStatusCode.MovedPermanently;
        context.Result = RuleResult.EndResponse;
        response.Headers[HeaderNames.Location] = 
            "/xmlfiles" + request.Path + request.QueryString;
    }
}

또한 이 방법은 요청을 다시 작성할 수도 있습니다. 샘플 앱에서는 wwwroot 폴더의 file.txt 텍스트 파일을 제공할 텍스트 파일 요청의 경로를 다시 작성하는 방법을 보여 줍니다. 정적 파일 미들웨어는 업데이트된 요청 경로에 따라 파일을 제공합니다.

using Microsoft.AspNetCore.Rewrite;
using RewriteRules;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

using (StreamReader apacheModRewriteStreamReader =
    File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
    File.OpenText("IISUrlRewrite.xml"))
{
    var options = new RewriteOptions()
        .AddRedirectToHttpsPermanent()
        .AddRedirect("redirect-rule/(.*)", "redirected/$1")
        .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
            skipRemainingRules: true)
        .AddApacheModRewrite(apacheModRewriteStreamReader)
        .AddIISUrlRewrite(iisUrlRewriteStreamReader)
        .Add(MethodRules.RedirectXmlFileRequests)
        .Add(MethodRules.RewriteTextFileRequests)
        .Add(new RedirectImageRequests(".png", "/png-images"))
        .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

    app.UseRewriter(options);
}

app.UseStaticFiles();

app.Run(context => context.Response.WriteAsync(
    $"Rewritten or Redirected Url: " +
    $"{context.Request.Path + context.Request.QueryString}"));

app.Run();

RewriteRules.cs:

public static void RewriteTextFileRequests(RewriteContext context)
{
    var request = context.HttpContext.Request;

    if (request.Path.Value != null &&
        request.Path.Value.EndsWith(".txt", StringComparison.OrdinalIgnoreCase))
    {
        context.Result = RuleResult.SkipRemainingRules;
        request.Path = "/file.txt";
    }
}

IRule 기반 규칙

Add를 사용하여 IRule 인터페이스를 구현하는 클래스에서 규칙 논리를 사용합니다. IRule은 메서드 기반 규칙 방식을 사용하는 것보다 더 높은 유연성을 제공합니다. 구현 클래스에는 ApplyRule 메서드에 대한 매개 변수를 전달할 수 있는 생성자가 포함될 수 있습니다.

using Microsoft.AspNetCore.Rewrite;
using RewriteRules;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

using (StreamReader apacheModRewriteStreamReader =
    File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
    File.OpenText("IISUrlRewrite.xml"))
{
    var options = new RewriteOptions()
        .AddRedirectToHttpsPermanent()
        .AddRedirect("redirect-rule/(.*)", "redirected/$1")
        .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
            skipRemainingRules: true)
        .AddApacheModRewrite(apacheModRewriteStreamReader)
        .AddIISUrlRewrite(iisUrlRewriteStreamReader)
        .Add(MethodRules.RedirectXmlFileRequests)
        .Add(MethodRules.RewriteTextFileRequests)
        .Add(new RedirectImageRequests(".png", "/png-images"))
        .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

    app.UseRewriter(options);
}

app.UseStaticFiles();

app.Run(context => context.Response.WriteAsync(
    $"Rewritten or Redirected Url: " +
    $"{context.Request.Path + context.Request.QueryString}"));

app.Run();

예제 응용 프로그램은 extensionnewPath 매개 변수 값들이 다양한 조건을 만족하는지 검사합니다. extension은 값을 포함해야 하며, 값은 .png, .jpg 또는 .gif여야 합니다. 만약 newPath가 유효하지 않으면 ArgumentException이 던져집니다. image.png에 대한 요청이 수행되면 해당 요청이 /png-images/image.png로 리디렉션됩니다. image.jpg에 대한 요청이 수행되면 해당 요청이 /jpg-images/image.jpg로 리디렉션됩니다. 상태 코드는 301 - Moved Permanently로 설정되고, context.Result는 규칙 처리를 중지하고 응답을 보내도록 설정됩니다.

public class RedirectImageRequests : IRule
{
    private readonly string _extension;
    private readonly PathString _newPath;

    public RedirectImageRequests(string extension, string newPath)
    {
        if (string.IsNullOrEmpty(extension))
        {
            throw new ArgumentException(nameof(extension));
        }

        if (!Regex.IsMatch(extension, @"^\.(png|jpg|gif)$"))
        {
            throw new ArgumentException("Invalid extension", nameof(extension));
        }

        if (!Regex.IsMatch(newPath, @"(/[A-Za-z0-9]+)+?"))
        {
            throw new ArgumentException("Invalid path", nameof(newPath));
        }

        _extension = extension;
        _newPath = new PathString(newPath);
    }

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

        // Because we're redirecting back to the same app, stop 
        // processing if the request has already been redirected
        if (request.Path.StartsWithSegments(new PathString(_newPath)) ||
            request.Path.Value == null)
        {
            return;
        }

        if (request.Path.Value.EndsWith(_extension, StringComparison.OrdinalIgnoreCase))
        {
            var response = context.HttpContext.Response;
            response.StatusCode = (int) HttpStatusCode.MovedPermanently;
            context.Result = RuleResult.EndResponse;
            response.Headers[HeaderNames.Location] = 
                _newPath + request.Path + request.QueryString;
        }
    }
}

시도:

  • PNG 요청: https://redirect6.azurewebsites.net/image.png
  • JPG 요청: https://redirect6.azurewebsites.net/image.jpg

정규식 예제

목표 정규식 문자열 및
일치 예제
대체 문자열 및
출력 예
경로를 쿼리 문자열로 재작성 ^path/(.*)/(.*)
/path/abc/123
path?var1=$1&var2=$2
/path?var1=abc&var2=123
후행 슬래시 제거 ^path2/(.*)/$
/path2/xyz/
$1
/path2/xyz
후행 슬래시 적용 ^path3/(.*[^/])$
/path3/xyz
$1/
/path3/xyz/
특정 요청 재작성 방지 ^(.*)(?<!\.axd)$ 또는
^(?!.*\.axd$)(.*)$
예: /path4/resource.htm
아니요: /path4/resource.axd
rewritten/$1
/rewritten/resource.htm
/resource.axd
URL 세그먼트 재정렬 path5/(.*)/(.*)/(.*)
path5/1/2/3
path5/$3/$2/$1
path5/3/2/1
URL 세그먼트 대체 ^path6/(.*)/segment2/(.*)
^path6/segment1/segment2/segment3
path6/$1/replaced/$2
/path6/segment1/replaced/segment3

이전 표에 나오는 링크는 Azure에 배포된 다음 코드를 사용합니다.

using Microsoft.AspNetCore.Rewrite;
using RewriteRules;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

using (StreamReader apacheModRewriteStreamReader =
    File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
    File.OpenText("IISUrlRewrite.xml"))
{
    var options = new RewriteOptions()
        .AddRedirectToHttpsPermanent()
        .AddRedirect("redirect-rule/(.*)", "redirected/$1")
        .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
            skipRemainingRules: true)

        // Rewrite path to QS.
        .AddRewrite(@"^path/(.*)/(.*)", "path?var1=$1&var2=$2",
            skipRemainingRules: true)
        // Skip trailing slash.
        .AddRewrite(@"^path2/(.*)/$", "path2/$1",
            skipRemainingRules: true)
         // Enforce trailing slash.
         .AddRewrite(@"^path3/(.*[^/])$", "path3/$1/",
            skipRemainingRules: true)
         // Avoid rewriting specific requests.
         .AddRewrite(@"^path4/(.*)(?<!\.axd)$", "rewritten/$1",
            skipRemainingRules: true)
         // Rearrange URL segments
         .AddRewrite(@"^path5/(.*)/(.*)/(.*)", "path5/$3/$2/$1",
            skipRemainingRules: true)
          // Replace a URL segment
          .AddRewrite(@"^path6/(.*)/segment2/(.*)", "path6/$1/replaced/$2",
            skipRemainingRules: true)

        .AddApacheModRewrite(apacheModRewriteStreamReader)
        .AddIISUrlRewrite(iisUrlRewriteStreamReader)
        .Add(MethodRules.RedirectXmlFileRequests)
        .Add(MethodRules.RewriteTextFileRequests)
        .Add(new RedirectImageRequests(".png", "/png-images"))
        .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

    app.UseRewriter(options);
}

app.UseStaticFiles();

app.Run(context => context.Response.WriteAsync(
    $"Rewritten or Redirected Url: " +
    $"{context.Request.Path + context.Request.QueryString}"));

app.Run();

위의 정규식 샘플 대부분에서 리터럴 path는 배포된 샘플을 위한, 테스트 가능한 고유한 재작성 규칙을 만드는 데 사용됩니다. 일반적으로 정규식에는 path가 포함되지 않습니다. 예제는 정규식 예제 표에서 확인할 수 있습니다.

본문에서는 ASP.NET Core 응용 프로그램에서 URL 재작성 미들웨어를 사용하는 방법에 관한 지침과 URL 재작성에 관해서 살펴봅니다.

URL 재작성은 하나 이상의 미리 정의된 규칙을 기반으로 하는 요청 URL을 수정하는 작업입니다. URL 재작성은 위치와 주소가 밀접하게 연결되지 않도록 리소스 위치와 해당 주소 간의 추상화를 만듭니다. URL 재작성이 중요한 몇 가지 시나리오는 다음과 같습니다.

  • 서버 리소스를 일시적 또는 영구적으로 이동하거나 대체하고, 해당 리소스에 대한 안정적인 로케이터를 유지 관리합니다.
  • 여러 앱 또는 한 앱의 여러 영역 간에 요청 처리를 분리합니다.
  • 들어오는 요청의 URL 세그먼트를 제거, 추가, 또는 다시 구성합니다.
  • SEO(검색 엔진 최적화)에 맞게 공용 URL을 최적화합니다.
  • 친숙한 공용 URL을 사용하여 방문자가 리소스를 요청함으로써 반환되는 콘텐츠를 예측할 수 있도록 합니다.
  • 안전하지 않은 요청을 보안 엔드포인트로 리디렉션합니다.
  • 외부 사이트에서 자산을 자체의 콘텐츠에 연결하여 다른 사이트에서 호스팅된 정적 자산을 사용하는 핫 링크를 방지합니다.

참고 항목

URL 재작성은 애플리케이션의 성능을 저하할 수 있습니다. 가능한 경우 규칙의 수와 복잡성을 제한합니다.

샘플 코드 보기 및 다운로드(다운로드 방법)

URL 리디렉션 및 URL 재작성

URL 리디렉션URL 재작성 간의 표현 차이는 미묘하지만 클라이언트에 리소스를 제공하는 데 더 중요한 의미가 있습니다. ASP.NET Core의 URL 재작성 미들웨어는 두 가지 모두에 대한 요구를 만족합니다.

URL 리디렉션은 클라이언트 쪽 작업과 관련되어 있습니다. 여기서 클라이언트는 원래 요청한 클라이언트와는 다른 주소로 리소스에 액세스하도록 지시받습니다. 이 경우 서버를 왕복해야 합니다. 클라이언트로 반환된 리디렉션 URL은 클라이언트가 리소스에 대한 새로운 요청을 만들 때 브라우저의 주소 표시줄에 나타나게 됩니다.

/resource/different-resource리디렉션되는 경우 서버는 임시 또는 영구 리디렉션을 나타내는 상태 코드와 함께 클라이언트가 /different-resource에서 리소스를 가져와야 한다고 응답합니다.

A WebAPI service endpoint has been temporarily changed from version 1 (v1) to version 2 (v2) on the server. A client makes a request to the service at the version 1 path /v1/api. The server sends back a 302 (Found) response with the new, temporary path for the service at version 2 /v2/api. The client makes a second request to the service at the redirect URL. The server responds with a 200 (OK) status code.

요청을 다른 URL로 리디렉션하는 경우 응답과 함께 상태 코드를 지정하여 영구 리디렉션 또는 임시 리디렉션인지의 여부를 나타낼 수 있습니다.

  • 301 - Moved Permanently 상태 코드는 리소스에 새 영구 URL이 있고, 리소스에 대한 이후의 모든 요청에서 새 URL을 사용해야 한다고 클라이언트에 지시하려는 경우에 사용됩니다. 301 상태 코드를 받으면 클라이언트에서 응답을 캐시하고 다시 사용할 수 있습니다.

  • 302 - 있음 상태 코드는 리디렉션이 일시적이거나 일반적으로 변경될 수 있는 경우에 사용됩니다. 302 상태 코드는 클라이언트에서 URL을 저장하지 않고 나중에 사용하지 못하도록 지시합니다.

상태 코드에 대한 자세한 내용은 RFC 9110: 상태 코드 정의를 참조하세요.

URL 재작성은 요청한 클라이언트와 다른 리소스 주소에서 리소스를 제공하는 서버 쪽 작업입니다. URL을 다시 작성하는 경우 서버를 왕복할 필요가 없습니다. 다시 작성된 URL은 클라이언트에 반환되지 않고 브라우저의 주소 표시줄에도 표시되지 않습니다.

/resource/different-resource다시 작성되면 서버에서 내부적으로/different-resource에 있는 리소스를 가져와서 반환합니다.

클라이언트는 다시 작성된 URL에서 리소스를 검색할 수 있지만, 요청을 만들고 응답을 받을 때 리소스가 다시 작성된 URL에 있음을 클라이언트에 알리지 않습니다.

A WebAPI service endpoint has been changed from version 1 (v1) to version 2 (v2) on the server. A client makes a request to the service at the version 1 path /v1/api. The request URL is rewritten to access the service at the version 2 path /v2/api. The service responds to the client with a 200 (OK) status code.

URL 재작성 예제 응용 프로그램

샘플 앱을 사용하면 URL 재작성 미들웨어의 기능을 살펴볼 수 있습니다. 이 앱은 리디렉션 및 재작성 규칙을 적용하고, 여러 시나리오에 대해 리디렉션되거나 다시 작성된 URL을 표시합니다.

URL 재작성 미들웨어를 사용해야 하는 경우

다음 방법을 사용할 수 없는 경우 URL 재작성 미들웨어를 사용합니다.

URL 재작성 미들웨어는 앱이 HTTP.sys 서버에서 호스팅될 때 사용합니다.

IIS, Apache 및 Nginx에서 서버 기반 URL 재작성 기술을 사용하는 주요 이유는 다음과 같습니다.

  • 미들웨어에서 이러한 모듈의 전체 기능을 지원하지 않습니다.

    서버 모듈의 일부 기능이 IIS 재작성 모듈의 IsFileIsDirectory 제약 조건과 같은 ASP.NET Core 프로젝트에서 작동하지 않습니다. 바로 이런 시나리오에서 대신 미들웨어를 사용합니다.

  • 미들웨어의 성능이 아마도 모듈의 성능과 일치하지 않습니다.

    벤치마킹은 성능을 가장 많이 저하시키는 방법 또는 저하된 성능을 무시할 수 있는 경우를 확인할 수 있는 유일한 방법입니다.

Package(패키지)

URL 다시 작성 미들웨어는 Microsoft.AspNetCore.Rewrite 패키지에서 제공하며, 이 패키지는 ASP.NET Core 앱에 포함됩니다.

확장 및 옵션

각각의 재작성 규칙에 대한 확장 메서드를 사용하여 RewriteOptions 클래스의 인스턴스를 만들어 URL 재작성 및 리디렉션 규칙을 설정합니다. 처리하고자 하는 순서대로 여러 규칙을 연결하면 됩니다. RewriteOptionsUseRewriter를 사용하여 요청 파이프라인에 추가될 때 URL 재작성 미들웨어에 전달됩니다.

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

www 이외 요청을 www로 리디렉션

이러한 옵션은 앱이 www 이외 요청을 www로 리디렉션하도록 허용합니다.

  • AddRedirectToWwwPermanent: 요청 www 이 아닌www 경우 하위 기본 요청을 영구적으로 리디렉션합니다. Status308PermanentRedirect 상태 코드를 사용하여 리디렉션합니다.

  • AddRedirectToWww: 들어오는 요청 www 이 아닌 경우 하위 기본 요청을www 리디렉션합니다. Status307TemporaryRedirect 상태 코드를 사용하여 리디렉션합니다. 오버로드를 사용하면 응답에 대한 상태 코드를 제공할 수 있습니다. 상태 코드 할당을 위해 StatusCodes 클래스의 필드를 사용합니다.

URL 리디렉션

요청을 리디렉션하려면 AddRedirect를 사용합니다. 첫 번째 매개 변수에는 들어오는 URL의 경로와 일치하는 부분을 찾기 위한 정규식이 포함되어 있습니다. 두 번째 매개 변수는 대체 문자열입니다. 필요한 경우 세 번째 매개 변수로 상태 코드를 지정할 수 있습니다. 상태 코드를 지정하지 않으면 상태 코드가 기본적으로 302 - 있음으로 설정되며, 이는 리소스가 일시적으로 이동하거나 대체되었음을 나타냅니다.

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

개발자 도구가 활성화된 브라우저에서 예제 응용 프로그램에 /redirect-rule/1234/5678 경로를 요청합니다. 그러면 정규식이 redirect-rule/(.*)의 요청 경로와 일치하므로 /redirected/1234/5678로 경로가 치환됩니다. 리디렉션 URL은 302 - 있음 상태 코드와 함께 클라이언트로 다시 전송됩니다. 브라우저는 리디렉션 URL에 대한 새로운 요청을 만들고 이 주소는 브라우저의 주소 표시줄에 출력됩니다. 샘플 앱의 규칙이 리디렉션 URL에서 일치하지 않으므로 다음과 같이 수행됩니다.

  • 두 번째 요청에서 앱의 200 - 정상 응답을 받습니다.
  • 응답 본문에 리디렉션 URL이 표시됩니다.

URL이 리디렉션되면 서버로의 왕복이 수행됩니다.

Warning

리디렉션 규칙을 설정할 때에는 주의하세요. 리디렉션 규칙은 리디렉션 이후를 포함하여 앱에 대한 모든 요청에서 평가됩니다. 따라서 무한 리디렉션 루프가 의도치 않게 자주 생성됩니다.

원래 요청: /redirect-rule/1234/5678

Browser window with developer tools tracking the requests and responses: Add redirect

표현식에서 괄호로 둘러쌓인 부분을 캡처 그룹(Capture Group)이라고 합니다. 그리고 표현식에서 마침표(.)는 모든 문자와 일치함을 뜻합니다. 마지막으로 별표(*)는 앞의 문자와 0번 이상 일치함을 나타냅니다. 따라서 URL의 마지막 두 세그먼트, 1234/5678은 캡쳐 그룹 (.*)에 의해 캡쳐됩니다. 즉 요청 URL에서 redirect-rule/ 이후에 제공하는 모든 값이 이 단일 캡처 그룹에 의해서 캡처됩니다.

대체 문자열에서, 캡처된 그룹은 캡처의 일련번호가 뒤에 붙는 달러 기호($)를 통해서 문자열에 삽입됩니다. 첫 번째 캡처 그룹 값은 $1로 얻을 수 있고, 두 번째 캡처 그룹 값은 $2로 얻을 수 있으며, 이는 정규식에 포함된 캡처 그룹에 대해 순차적으로 계속됩니다. 예제 응용 프로그램에서 리디렉션 규칙의 정규식에 캡처된 그룹은 단 하나뿐이므로 대체 문자열에 삽입되는 그룹도 $1 하나뿐입니다. 규칙이 적용되고 나면 URL은 /redirected/1234/5678로 변환됩니다.

보안 엔드포인트에 대한 URL 리디렉션

AddRedirectToHttps를 사용하여 HTTPS 프로토콜을 통해 HTTP 요청을 동일한 호스트 및 경로로 리디렉션할 수 있습니다. 상태 코드가 제공되지 않는 경우 미들웨어는 기본적으로 302 - 있음으로 설정됩니다. 포트가 제공되지 않는 경우 다음과 같이 수행됩니다.

  • 미들웨어가 기본적으로 null로 설정됩니다.
  • 체계가 https(HTTPS 프로토콜)로 변경되고 클라이언트에서 443 포트의 리소스에 액세스합니다.

다음 예제에서는 상태 코드를 설정하고 포트를 301 - Moved Permanently 5001로 변경하는 방법을 보여줍니다.

public void Configure(IApplicationBuilder app)
{
    var options = new RewriteOptions()
        .AddRedirectToHttps(301, 5001);

    app.UseRewriter(options);
}

AddRedirectToHttpsPermanent를 사용하여 443 포트의 HTTPS 프로토콜을 통해 안전하지 않은 요청을 동일한 호스트 및 경로로 리디렉션합니다. 미들웨어는 상태 코드를 301 - Moved Permanently로 설정합니다.

public void Configure(IApplicationBuilder app)
{
    var options = new RewriteOptions()
        .AddRedirectToHttpsPermanent();

    app.UseRewriter(options);
}

참고 항목

추가 리디렉션 규칙을 요구하지 않고 보안 엔드포인트로 리디렉션하는 경우 HTTPS 리디렉션 미들웨어를 사용하는 것이 좋습니다. 자세한 내용은 HTTPS 적용 항목을 참조하세요.

예제 응용 프로그램을 통해서 AddRedirectToHttps 또는 AddRedirectToHttpsPermanent의 사용 방법을 확인해 볼 수 있습니다. 먼저 RewriteOptions에 이 확장 메서드를 추가합니다. 모든 URL에서 앱에 대한 안전하지 않은 요청을 만듭니다. 자체 서명된 인증서를 신뢰할 수 없다는 브라우저 보안 경고는 무시하면 됩니다. 또는 인증서를 신뢰할 예외를 만듭니다.

AddRedirectToHttps(301, 5001)에 대한 원본 요청: http://localhost:5000/secure

Browser window with developer tools tracking the requests and responses: Add redirect to HTTPS

AddRedirectToHttpsPermanent에 대한 원본 요청: http://localhost:5000/secure

Browser window with developer tools tracking the requests and responses: Add redirect to HTTPS permanent

URL 재작성

URL을 재작성하는 규칙을 만들려면 AddRewrite를 사용합니다. 첫 번째 매개 변수에는 들어오는 URL의 경로에서 일치하는 정규식이 포함됩니다. 두 번째 매개 변수는 대체 문자열입니다. 세 번째 매개 변수, skipRemainingRules: {true|false}는 현재 규칙이 적용되는 경우에 추가 재작성 규칙을 건너뛸 것인지 여부를 미들웨어에 나타냅니다.

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

원래 요청: /rewrite-rule/1234/5678

Browser window with developer tools tracking the request and response: Add rewrite

식의 시작 부분에 있는 캐럿(^)은 URL 경로의 시작 부분에서 일치가 시작된다는 것을 의미합니다.

redirect-rule/(.*) 리디렉션 규칙이 있는 이전 예제에는 정규식의 시작 부분에 캐럿(^)이 없습니다. 따라서 일치하는 모든 문자가 경로의 redirect-rule/ 앞에 나올 수 있습니다.

Path Match
/redirect-rule/1234/5678
/my-cool-redirect-rule/1234/5678
/anotherredirect-rule/1234/5678

반면 ^rewrite-rule/(\d+)/(\d+) 재작성 규칙의 경우에는 오로지 rewrite-rule/로 시작하는 경로만 일치합니다. 다음 표에는 일치에서의 차이가 나와 있습니다.

Path Match
/rewrite-rule/1234/5678
/my-cool-rewrite-rule/1234/5678 없음
/anotherrewrite-rule/1234/5678 아니요

식의 ^rewrite-rule/ 부분을 따릅니다. 두 개의 캡처 그룹, (\d+)/(\d+)가 있습니다. 여기서 \d숫자 하나와 일치함을 뜻합니다. 그리고 더하기 기호(+)는 앞의 문자와 한 번 이상 일치함을 나타냅니다. 따라서 URL은 반드시 숫자 뒤에 슬래시와 다른 숫자가 연이어 나타나는 부분을 포함해야 합니다. 이 캡쳐 그룹들은 $1$2를 통해서 재작성 URL에 삽입됩니다. 재작성 규칙의 대체 문자열은 캡처된 그룹을 쿼리 문자열에 배치합니다. 즉, 요청 경로 /rewrite-rule/1234/5678/rewritten?var1=1234&var2=5678에서 리소스를 가져오도록 재작성됩니다. 원본 요청에 쿼리 문자열이 있으면 URL을 다시 작성할 때 유지됩니다.

리소스를 가져오기 위해 서버를 왕복하지 않습니다. 리소스가 있으면 이를 가져와서 200 - 정상 상태 코드와 함께 클라이언트에 반환합니다. 클라이언트는 리디렉션 되지 않으므로 브라우저 주소 표시줄의 URL은 변경되지 않습니다. 클라이언트는 서버에서 URL 재작성 작업이 발생했음을 검색할 수 없습니다.

참고 항목

일치 규칙은 컴퓨팅 측면에서 비용이 많이 들고 앱의 응답 속도가 증가되므로 가능한 경우 skipRemainingRules: true를 사용합니다. 최대한 빠른 응용 프로그램 응답을 위해서는:

  • 재작성 규칙을 가장 자주 일치하는 규칙에서 가장 드물게 일치하는 규칙으로의 순서로 정렬합니다.
  • 일치가 발생하고 추가적인 규칙 처리가 필요하지 않다면 나머지 규칙의 처리를 생략합니다.

Apache mod_rewrite

AddApacheModRewrite를 사용하면 Apache mod_rewrite 규칙을 적용할 수 있습니다. 규칙 파일이 응용 프로그램과 함께 배포되고 있는지 확인하시기 바랍니다. mod_rewrite 규칙에 대한 자세한 내용 및 예제는 Apache mod_rewrite를 참조하세요.

StreamReaderApacheModRewrite.txt 규칙 파일에서 규칙을 읽는 데 사용됩니다.

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

예제 응용 프로그램은 /apache-mod-rules-redirect/(.\*)에서 /redirected?id=$1로 요청을 리디렉션합니다. 응답 상태 코드는 302 - 있음입니다.

# Rewrite path with additional sub directory
RewriteRule ^/apache-mod-rules-redirect/(.*) /redirected?id=$1 [L,R=302]

원래 요청: /apache-mod-rules-redirect/1234

Browser window with developer tools tracking the requests and responses: Add Apache mod redirect

미들웨어는 다음과 같은 Apache mod_rewrite 서버 변수를 지원합니다:

  • CONN_REMOTE_ADDR
  • HTTP_ACCEPT
  • HTTP_CONNECTION
  • HTTP_COOKIE
  • HTTP_FORWARDED
  • HTTP_HOST
  • HTTP_REFERER
  • HTTP_USER_AGENT
  • HTTPS
  • IPV6
  • QUERY_STRING
  • REMOTE_ADDR
  • REMOTE_PORT
  • REQUEST_FILENAME
  • REQUEST_METHOD
  • REQUEST_SCHEME
  • REQUEST_URI
  • SCRIPT_FILENAME
  • SERVER_ADDR
  • SERVER_PORT
  • SERVER_PROTOCOL
  • TIME
  • TIME_DAY
  • TIME_HOUR
  • TIME_MIN
  • TIME_MON
  • TIME_SEC
  • TIME_WDAY
  • TIME_YEAR

IIS URL 재작성 모듈 규칙

IIS URL 재작성 모듈에 적용되는 것과 동일한 규칙 세트를 사용하려면 AddIISUrlRewrite를 사용합니다. 규칙 파일이 응용 프로그램과 함께 배포되고 있는지 확인하시기 바랍니다. Windows Server IIS에서 실행하는 경우 미들웨어에서 앱의 web.config 파일을 사용하도록 지시하지 않습니다. IIS를 사용하는 경우 IIS 재작성 모듈과 충돌하지 않도록 이러한 규칙을 앱의 web.config 파일 외부에 저장해야 합니다. IIS URL 재작성 모듈 규칙에 대한 자세한 내용 및 예제는 Url 재작성 모듈 2.0 사용URL 재작성 모듈 구성 참조를 참조하세요.

A StreamReader 는 규칙 파일에서 규칙을 읽는 IISUrlRewrite.xml 데 사용됩니다.

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

예제 응용 프로그램은 /iis-rules-rewrite/(.*)에서 /rewritten?id=$1로 요청을 재작성합니다. 응답이 200 - 정상 상태 코드와 함께 클라이언트에 전송됩니다.

<rewrite>
  <rules>
    <rule name="Rewrite segment to id querystring" stopProcessing="true">
      <match url="^iis-rules-rewrite/(.*)$" />
      <action type="Rewrite" url="rewritten?id={R:1}" appendQueryString="false"/>
    </rule>
  </rules>
</rewrite>

원래 요청: /iis-rules-rewrite/1234

Browser window with developer tools tracking the request and response: Add IIS URL rewrite

원하지 않는 방식으로 응용 프로그램에 영향을 주는 서버 수준 규칙이 구성된 활성 IIS 재작성 모듈이 존재할 경우 응용 프로그램에 대한 IIS 재작성 모듈을 비활성화할 수 있습니다. 보다 자세한 내용은 IIS 모듈 비활성화를 참고하시기 바랍니다.

지원되지 않는 기능

미들웨어는 다음과 같은 IIS URL 재작성 모듈 기능을 지원하지 않습니다:

  • 아웃바운드 규칙
  • 사용자 지정 서버 변수
  • 와일드카드
  • LogRewrittenUrl

지원되는 서버 변수

미들웨어 다음 IIS URL 재작성 모듈 서버 변수를 지원합니다.

  • CONTENT_LENGTH
  • CONTENT_TYPE
  • HTTP_ACCEPT
  • HTTP_CONNECTION
  • HTTP_COOKIE
  • HTTP_HOST
  • HTTP_REFERER
  • HTTP_URL
  • HTTP_USER_AGENT
  • HTTPS
  • LOCAL_ADDR
  • QUERY_STRING
  • REMOTE_ADDR
  • REMOTE_PORT
  • REQUEST_FILENAME
  • REQUEST_URI

참고 항목

PhysicalFileProvider를 이용해서 IFileProvider를 가져올 수도 있습니다. 이 방식이 재작성 규칙 파일의 위치에 대해 더 많은 유연성을 제공할 수 있습니다. 재작성 규칙 파일이 서버의 지정한 경로에 배포되는지 확인하시기 바랍니다.

PhysicalFileProvider fileProvider = new PhysicalFileProvider(Directory.GetCurrentDirectory());

메서드 기반 규칙

메서드를 이용해서 직접 규칙 로직을 구현하고 싶다면 Add를 사용하면 됩니다. Add는 메서드에서 사용할 HttpContext를 사용할 수 있게 하는 RewriteContext를 공개합니다. RewriteContext.Result는 추가 파이프라인 처리가 수행되는 방법을 결정합니다. 값을 다음 표에 설명된 RuleResult 필드 중 하나로 설정합니다.

컨텍스트 결과 다시 작성 작업
RuleResult.ContinueRules (기본값) 규칙 적용을 계속합니다.
RuleResult.EndResponse 규칙 적용을 중지하고 응답을 보냅니다.
RuleResult.SkipRemainingRules 규칙 적용을 중지하고 컨텍스트를 다음 미들웨어로 보냅니다.
public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

샘플 앱은 .로 끝나는 .xml경로에 대한 요청을 리디렉션하는 메서드를 보여 줍니다. /file.xml에 대한 요청이 수행되면 해당 요청이 /xmlfiles/file.xml로 리디렉션됩니다. 상태 코드가 301 - Moved Permanently로 설정됩니다. 브라우저에서 새 요청을 /xmlfiles/file.xml수행하면 정적 파일 미들웨어는 wwwroot/xmlfiles 폴더에서 클라이언트에 파일을 제공합니다. 리디렉션의 경우 응답의 상태 코드를 명시적으로 설정합니다. 그렇지 않으면 200 - 정상 상태 코드가 반환되고 클라이언트에서 리디렉션이 수행되지 않습니다.

RewriteRules.cs:

public static void RedirectXmlFileRequests(RewriteContext context)
{
    var request = context.HttpContext.Request;

    // Because the client is redirecting back to the same app, stop 
    // processing if the request has already been redirected.
    if (request.Path.StartsWithSegments(new PathString("/xmlfiles")))
    {
        return;
    }

    if (request.Path.Value.EndsWith(".xml", StringComparison.OrdinalIgnoreCase))
    {
        var response = context.HttpContext.Response;
        response.StatusCode = (int) HttpStatusCode.MovedPermanently;
        context.Result = RuleResult.EndResponse;
        response.Headers[HeaderNames.Location] = 
            "/xmlfiles" + request.Path + request.QueryString;
    }
}

또한 이 방법은 요청을 다시 작성할 수도 있습니다. 샘플 앱에서는 wwwroot 폴더의 file.txt 텍스트 파일을 제공할 텍스트 파일 요청의 경로를 다시 작성하는 방법을 보여 줍니다. 정적 파일 미들웨어는 업데이트된 요청 경로에 따라 파일을 제공합니다.

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

RewriteRules.cs:

public static void RewriteTextFileRequests(RewriteContext context)
{
    var request = context.HttpContext.Request;

    if (request.Path.Value.EndsWith(".txt", StringComparison.OrdinalIgnoreCase))
    {
        context.Result = RuleResult.SkipRemainingRules;
        request.Path = "/file.txt";
    }
}

IRule 기반 규칙

Add를 사용하여 IRule 인터페이스를 구현하는 클래스에서 규칙 논리를 사용합니다. IRule은 메서드 기반 규칙 방식을 사용하는 것보다 더 높은 유연성을 제공합니다. 구현 클래스에는 ApplyRule 메서드에 대한 매개 변수를 전달할 수 있는 생성자가 포함될 수 있습니다.

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

예제 응용 프로그램은 extensionnewPath 매개 변수 값들이 다양한 조건을 만족하는지 검사합니다. extension은 값을 포함하고 있어야 하고 그 값은 .png, .jpg 또는 .gif 중 하나이어야 합니다. 만약 newPath가 유효하지 않으면 ArgumentException이 던져집니다. image.png에 대한 요청이 수행되면 해당 요청이 /png-images/image.png로 리디렉션됩니다. image.jpg에 대한 요청이 수행되면 해당 요청이 /jpg-images/image.jpg로 리디렉션됩니다. 상태 코드는 301 - Moved Permanently로 설정되고, context.Result는 규칙 처리를 중지하고 응답을 보내도록 설정됩니다.

public class RedirectImageRequests : IRule
{
    private readonly string _extension;
    private readonly PathString _newPath;

    public RedirectImageRequests(string extension, string newPath)
    {
        if (string.IsNullOrEmpty(extension))
        {
            throw new ArgumentException(nameof(extension));
        }

        if (!Regex.IsMatch(extension, @"^\.(png|jpg|gif)$"))
        {
            throw new ArgumentException("Invalid extension", nameof(extension));
        }

        if (!Regex.IsMatch(newPath, @"(/[A-Za-z0-9]+)+?"))
        {
            throw new ArgumentException("Invalid path", nameof(newPath));
        }

        _extension = extension;
        _newPath = new PathString(newPath);
    }

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

        // Because we're redirecting back to the same app, stop 
        // processing if the request has already been redirected
        if (request.Path.StartsWithSegments(new PathString(_newPath)))
        {
            return;
        }

        if (request.Path.Value.EndsWith(_extension, StringComparison.OrdinalIgnoreCase))
        {
            var response = context.HttpContext.Response;
            response.StatusCode = (int) HttpStatusCode.MovedPermanently;
            context.Result = RuleResult.EndResponse;
            response.Headers[HeaderNames.Location] = 
                _newPath + request.Path + request.QueryString;
        }
    }
}

원래 요청: /image.png

Browser window with developer tools tracking the requests and responses for image.png

원래 요청: /image.jpg

Browser window with developer tools tracking the requests and responses for image.jpg

정규식 예제

목표 정규식 문자열 및
일치 예제
대체 문자열 및
출력 예
경로를 쿼리 문자열로 재작성 ^path/(.*)/(.*)
/path/abc/123
path?var1=$1&var2=$2
/path?var1=abc&var2=123
후행 슬래시 제거 (.*)/$
/path/
$1
/path
후행 슬래시 적용 (.*[^/])$
/path
$1/
/path/
특정 요청 재작성 방지 ^(.*)(?<!\.axd)$ 또는 ^(?!.*\.axd$)(.*)$
예: /resource.htm
아니요: /resource.axd
rewritten/$1
/rewritten/resource.htm
/resource.axd
URL 세그먼트 재정렬 path/(.*)/(.*)/(.*)
path/1/2/3
path/$3/$2/$1
path/3/2/1
URL 세그먼트 대체 ^(.*)/segment2/(.*)
/segment1/segment2/segment3
$1/replaced/$2
/segment1/replaced/segment3

본문에서는 ASP.NET Core 응용 프로그램에서 URL 재작성 미들웨어를 사용하는 방법에 관한 지침과 URL 재작성에 관해서 살펴봅니다.

URL 재작성은 하나 이상의 미리 정의된 규칙을 기반으로 하는 요청 URL을 수정하는 작업입니다. URL 재작성은 위치와 주소가 밀접하게 연결되지 않도록 리소스 위치와 해당 주소 간의 추상화를 만듭니다. URL 재작성이 중요한 몇 가지 시나리오는 다음과 같습니다.

  • 서버 리소스를 일시적 또는 영구적으로 이동하거나 대체하고, 해당 리소스에 대한 안정적인 로케이터를 유지 관리합니다.
  • 여러 앱 또는 한 앱의 여러 영역 간에 요청 처리를 분리합니다.
  • 들어오는 요청의 URL 세그먼트를 제거, 추가, 또는 다시 구성합니다.
  • SEO(검색 엔진 최적화)에 맞게 공용 URL을 최적화합니다.
  • 친숙한 공용 URL을 사용하여 방문자가 리소스를 요청함으로써 반환되는 콘텐츠를 예측할 수 있도록 합니다.
  • 안전하지 않은 요청을 보안 엔드포인트로 리디렉션합니다.
  • 외부 사이트에서 자산을 자체의 콘텐츠에 연결하여 다른 사이트에서 호스팅된 정적 자산을 사용하는 핫 링크를 방지합니다.

참고 항목

URL 재작성은 애플리케이션의 성능을 저하할 수 있습니다. 가능한 경우 규칙의 수와 복잡성을 제한합니다.

샘플 코드 보기 및 다운로드(다운로드 방법)

URL 리디렉션 및 URL 재작성

URL 리디렉션URL 재작성 간의 표현 차이는 미묘하지만 클라이언트에 리소스를 제공하는 데 더 중요한 의미가 있습니다. ASP.NET Core의 URL 재작성 미들웨어는 두 가지 모두에 대한 요구를 만족합니다.

URL 리디렉션은 클라이언트 쪽 작업과 관련되어 있습니다. 여기서 클라이언트는 원래 요청한 클라이언트와는 다른 주소로 리소스에 액세스하도록 지시받습니다. 이 경우 서버를 왕복해야 합니다. 클라이언트로 반환된 리디렉션 URL은 클라이언트가 리소스에 대한 새로운 요청을 만들 때 브라우저의 주소 표시줄에 나타나게 됩니다.

/resource/different-resource리디렉션되는 경우 서버는 임시 또는 영구 리디렉션을 나타내는 상태 코드와 함께 클라이언트가 /different-resource에서 리소스를 가져와야 한다고 응답합니다.

A WebAPI service endpoint has been temporarily changed from version 1 (v1) to version 2 (v2) on the server. A client makes a request to the service at the version 1 path /v1/api. The server sends back a 302 (Found) response with the new, temporary path for the service at version 2 /v2/api. The client makes a second request to the service at the redirect URL. The server responds with a 200 (OK) status code.

요청을 다른 URL로 리디렉션하는 경우 응답과 함께 상태 코드를 지정하여 영구 리디렉션 또는 임시 리디렉션인지의 여부를 나타낼 수 있습니다.

  • 301 - Moved Permanently 상태 코드는 리소스에 새 영구 URL이 있고, 리소스에 대한 이후의 모든 요청에서 새 URL을 사용해야 한다고 클라이언트에 지시하려는 경우에 사용됩니다. 301 상태 코드를 받으면 클라이언트에서 응답을 캐시하고 다시 사용할 수 있습니다.

  • 302 - 있음 상태 코드는 리디렉션이 일시적이거나 일반적으로 변경될 수 있는 경우에 사용됩니다. 302 상태 코드는 클라이언트에서 URL을 저장하지 않고 나중에 사용하지 못하도록 지시합니다.

상태 코드에 대한 자세한 내용은 RFC 9110: 상태 코드 정의를 참조하세요.

URL 재작성은 요청한 클라이언트와 다른 리소스 주소에서 리소스를 제공하는 서버 쪽 작업입니다. URL을 다시 작성하는 경우 서버를 왕복할 필요가 없습니다. 다시 작성된 URL은 클라이언트에 반환되지 않고 브라우저의 주소 표시줄에도 표시되지 않습니다.

/resource/different-resource다시 작성되면 서버에서 내부적으로/different-resource에 있는 리소스를 가져와서 반환합니다.

클라이언트는 다시 작성된 URL에서 리소스를 검색할 수 있지만, 요청을 만들고 응답을 받을 때 리소스가 다시 작성된 URL에 있음을 클라이언트에 알리지 않습니다.

A WebAPI service endpoint has been changed from version 1 (v1) to version 2 (v2) on the server. A client makes a request to the service at the version 1 path /v1/api. The request URL is rewritten to access the service at the version 2 path /v2/api. The service responds to the client with a 200 (OK) status code.

URL 재작성 예제 응용 프로그램

샘플 앱을 사용하면 URL 재작성 미들웨어의 기능을 살펴볼 수 있습니다. 이 앱은 리디렉션 및 재작성 규칙을 적용하고, 여러 시나리오에 대해 리디렉션되거나 다시 작성된 URL을 표시합니다.

URL 재작성 미들웨어를 사용해야 하는 경우

다음 방법을 사용할 수 없는 경우 URL 재작성 미들웨어를 사용합니다.

또한 앱이 HTTP.sys 서버(이전의 WebListener)에서 호스팅되는 경우 미들웨어를 사용합니다.

IIS, Apache 및 Nginx에서 서버 기반 URL 재작성 기술을 사용하는 주요 이유는 다음과 같습니다.

  • 미들웨어에서 이러한 모듈의 전체 기능을 지원하지 않습니다.

    서버 모듈의 일부 기능이 IIS 재작성 모듈의 IsFileIsDirectory 제약 조건과 같은 ASP.NET Core 프로젝트에서 작동하지 않습니다. 바로 이런 시나리오에서 대신 미들웨어를 사용합니다.

  • 미들웨어의 성능이 아마도 모듈의 성능과 일치하지 않습니다.

    벤치마킹은 성능을 가장 많이 저하시키는 방법 또는 저하된 성능을 무시할 수 있는 경우를 확인할 수 있는 유일한 방법입니다.

Package(패키지)

프로젝트에 미들웨어를 포함시키려면 Microsoft.AspNetCore.Rewrite 패키지가 포함된 프로젝트 파일의 Microsoft.AspNetCore.App 메타패키지에 패키지 참조를 추가합니다.

Microsoft.AspNetCore.App 메타패키지를 사용하지 않는 경우 Microsoft.AspNetCore.Rewrite 패키지에 프로젝트 참조를 추가합니다.

확장 및 옵션

각각의 재작성 규칙에 대한 확장 메서드를 사용하여 RewriteOptions 클래스의 인스턴스를 만들어 URL 재작성 및 리디렉션 규칙을 설정합니다. 처리하고자 하는 순서대로 여러 규칙을 연결하면 됩니다. RewriteOptionsUseRewriter를 사용하여 요청 파이프라인에 추가될 때 URL 재작성 미들웨어에 전달됩니다.

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

www 이외 요청을 www로 리디렉션

이러한 옵션은 앱이 www 이외 요청을 www로 리디렉션하도록 허용합니다.

  • AddRedirectToWwwPermanent: 요청 www 이 아닌www 경우 하위 기본 요청을 영구적으로 리디렉션합니다. Status308PermanentRedirect 상태 코드를 사용하여 리디렉션합니다.

  • AddRedirectToWww: 들어오는 요청 www 이 아닌 경우 하위 기본 요청을www 리디렉션합니다. Status307TemporaryRedirect 상태 코드를 사용하여 리디렉션합니다. 오버로드를 사용하면 응답에 대한 상태 코드를 제공할 수 있습니다. 상태 코드 할당을 위해 StatusCodes 클래스의 필드를 사용합니다.

URL 리디렉션

요청을 리디렉션하려면 AddRedirect를 사용합니다. 첫 번째 매개 변수에는 들어오는 URL의 경로와 일치하는 부분을 찾기 위한 정규식을 지정합니다. 두 번째 매개 변수는 대체 문자열입니다. 필요한 경우 세 번째 매개 변수로 상태 코드를 지정할 수 있습니다. 상태 코드를 지정하지 않으면 상태 코드가 기본적으로 302 - 있음으로 설정되며, 이는 리소스가 일시적으로 이동하거나 대체되었음을 나타냅니다.

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

개발자 도구가 활성화된 브라우저에서 예제 응용 프로그램에 /redirect-rule/1234/5678 경로를 요청합니다. 그러면 정규식이 redirect-rule/(.*)의 요청 경로와 일치하므로 /redirected/1234/5678로 경로가 치환됩니다. 리디렉션 URL은 302 - 있음 상태 코드와 함께 클라이언트로 다시 전송됩니다. 브라우저는 리디렉션 URL에 대한 새로운 요청을 만들고 이 주소는 브라우저의 주소 표시줄에 출력됩니다. 샘플 앱의 규칙이 리디렉션 URL에서 일치하지 않으므로 다음과 같이 수행됩니다.

  • 두 번째 요청에서 앱의 200 - 정상 응답을 받습니다.
  • 응답 본문에 리디렉션 URL이 표시됩니다.

URL이 리디렉션되면 서버로의 왕복이 수행됩니다.

Warning

리디렉션 규칙을 설정할 때에는 주의하세요. 리디렉션 규칙은 리디렉션 이후를 포함하여 앱에 대한 모든 요청에서 평가됩니다. 따라서 무한 리디렉션 루프가 의도치 않게 자주 생성됩니다.

원래 요청: /redirect-rule/1234/5678

Add redirect: Browser window with developer tools tracking the requests and responses

표현식에서 괄호로 둘러쌓인 부분을 캡처 그룹(Capture Group)이라고 합니다. 그리고 표현식에서 마침표(.)는 모든 문자와 일치함을 뜻합니다. 마지막으로 별표(*)는 앞의 문자와 0번 이상 일치함을 나타냅니다. 따라서 URL의 마지막 두 세그먼트, 1234/5678은 캡쳐 그룹 (.*)에 의해 캡쳐됩니다. 즉 요청 URL에서 redirect-rule/ 이후에 제공하는 모든 값이 이 단일 캡처 그룹에 의해서 캡처됩니다.

대체 문자열에서, 캡처된 그룹은 캡처의 일련번호가 뒤에 붙는 달러 기호($)를 통해서 문자열에 삽입됩니다. 첫 번째 캡처 그룹 값은 $1로 얻을 수 있고, 두 번째 캡처 그룹 값은 $2로 얻을 수 있으며, 이는 정규식에 포함된 캡처 그룹에 대해 순차적으로 계속됩니다. 예제 응용 프로그램에서 리디렉션 규칙의 정규식에 캡처된 그룹은 단 하나뿐이므로 대체 문자열에 삽입되는 그룹도 $1 하나뿐입니다. 규칙이 적용되고 나면 URL은 /redirected/1234/5678로 변환됩니다.

보안 엔드포인트에 대한 URL 리디렉션

AddRedirectToHttps를 사용하여 HTTPS 프로토콜을 통해 HTTP 요청을 동일한 호스트 및 경로로 리디렉션할 수 있습니다. 상태 코드가 제공되지 않는 경우 미들웨어는 기본적으로 302 - 있음으로 설정됩니다. 포트가 제공되지 않는 경우 다음과 같이 수행됩니다.

  • 미들웨어가 기본적으로 null로 설정됩니다.
  • 체계가 https(HTTPS 프로토콜)로 변경되고 클라이언트에서 443 포트의 리소스에 액세스합니다.

다음 예제에서는 상태 코드를 설정하고 포트를 301 - Moved Permanently 5001로 변경하는 방법을 보여줍니다.

public void Configure(IApplicationBuilder app)
{
    var options = new RewriteOptions()
        .AddRedirectToHttps(301, 5001);

    app.UseRewriter(options);
}

AddRedirectToHttpsPermanent를 사용하여 443 포트의 HTTPS 프로토콜을 통해 안전하지 않은 요청을 동일한 호스트 및 경로로 리디렉션합니다. 미들웨어는 상태 코드를 301 - Moved Permanently로 설정합니다.

public void Configure(IApplicationBuilder app)
{
    var options = new RewriteOptions()
        .AddRedirectToHttpsPermanent();

    app.UseRewriter(options);
}

참고 항목

추가 리디렉션 규칙을 요구하지 않고 보안 엔드포인트로 리디렉션하는 경우 HTTPS 리디렉션 미들웨어를 사용하는 것이 좋습니다. 자세한 내용은 HTTPS 적용 항목을 참조하세요.

예제 응용 프로그램을 통해서 AddRedirectToHttps 또는 AddRedirectToHttpsPermanent의 사용 방법을 확인해 볼 수 있습니다. 먼저 RewriteOptions에 이 확장 메서드를 추가합니다. 모든 URL에서 앱에 대한 안전하지 않은 요청을 만듭니다. 자체 서명된 인증서를 신뢰할 수 없다는 브라우저 보안 경고는 무시하면 됩니다. 또는 인증서를 신뢰할 예외를 만듭니다.

AddRedirectToHttps(301, 5001)에 대한 원본 요청: http://localhost:5000/secure

Add redirect to HTTPS: Browser window with developer tools tracking the requests and responses

AddRedirectToHttpsPermanent에 대한 원본 요청: http://localhost:5000/secure

Add redirect to HTTPS permanent: Browser window with developer tools tracking the requests and responses

URL 재작성

URL을 재작성하는 규칙을 만들려면 AddRewrite를 사용합니다. 첫 번째 매개 변수에는 들어오는 URL의 경로에서 일치하는 정규식이 포함됩니다. 두 번째 매개 변수는 대체 문자열입니다. 세 번째 매개 변수, skipRemainingRules: {true|false}는 현재 규칙이 적용되는 경우에 추가 재작성 규칙을 건너뛸 것인지 여부를 미들웨어에 나타냅니다.

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

원래 요청: /rewrite-rule/1234/5678

Add rewrite: Browser window with developer tools tracking the request and response

식의 시작 부분에 있는 캐럿(^)은 URL 경로의 시작 부분에서 일치가 시작된다는 것을 의미합니다.

redirect-rule/(.*) 리디렉션 규칙이 있는 이전 예제에는 정규식의 시작 부분에 캐럿(^)이 없습니다. 따라서 일치하는 모든 문자가 경로의 redirect-rule/ 앞에 나올 수 있습니다.

Path Match
/redirect-rule/1234/5678
/my-cool-redirect-rule/1234/5678
/anotherredirect-rule/1234/5678

반면 ^rewrite-rule/(\d+)/(\d+) 재작성 규칙의 경우에는 오로지 rewrite-rule/로 시작하는 경로만 일치합니다. 다음 표에는 일치에서의 차이가 나와 있습니다.

Path Match
/rewrite-rule/1234/5678
/my-cool-rewrite-rule/1234/5678 없음
/anotherrewrite-rule/1234/5678 아니요

식의 ^rewrite-rule/ 부분을 따릅니다. 두 개의 캡처 그룹, (\d+)/(\d+)가 있습니다. 여기서 \d숫자 하나와 일치함을 뜻합니다. 그리고 더하기 기호(+)는 앞의 문자와 한 번 이상 일치함을 나타냅니다. 따라서 URL은 반드시 숫자 뒤에 슬래시와 다른 숫자가 연이어 나타나는 부분을 포함해야 합니다. 이 캡쳐 그룹들은 $1$2를 통해서 재작성 URL에 삽입됩니다. 재작성 규칙의 대체 문자열은 캡처된 그룹을 쿼리 문자열에 배치합니다. 즉, 요청 경로 /rewrite-rule/1234/5678/rewritten?var1=1234&var2=5678에서 리소스를 가져오도록 재작성됩니다. 원본 요청에 쿼리 문자열이 있으면 URL을 다시 작성할 때 유지됩니다.

리소스를 가져오기 위해 서버를 왕복하지 않습니다. 리소스가 있으면 이를 가져와서 200 - 정상 상태 코드와 함께 클라이언트에 반환합니다. 클라이언트는 리디렉션 되지 않으므로 브라우저 주소 표시줄의 URL은 변경되지 않습니다. 클라이언트는 서버에서 URL 재작성 작업이 발생했음을 검색할 수 없습니다.

참고 항목

일치 규칙은 컴퓨팅 측면에서 비용이 많이 들고 앱의 응답 속도가 증가되므로 가능한 경우 skipRemainingRules: true를 사용합니다. 최대한 빠른 응용 프로그램 응답을 위해서는:

  • 재작성 규칙을 가장 자주 일치하는 규칙에서 가장 드물게 일치하는 규칙으로의 순서로 정렬합니다.
  • 일치가 발생하고 추가적인 규칙 처리가 필요하지 않다면 나머지 규칙의 처리를 생략합니다.

Apache mod_rewrite

AddApacheModRewrite를 사용하면 Apache mod_rewrite 규칙을 적용할 수 있습니다. 규칙 파일이 응용 프로그램과 함께 배포되고 있는지 확인하시기 바랍니다. mod_rewrite 규칙에 대한 자세한 내용 및 예제는 Apache mod_rewrite를 참조하세요.

StreamReaderApacheModRewrite.txt 규칙 파일에서 규칙을 읽는 데 사용됩니다.

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

예제 응용 프로그램은 /apache-mod-rules-redirect/(.\*)에서 /redirected?id=$1로 요청을 리디렉션합니다. 응답 상태 코드는 302 - 있음입니다.

# Rewrite path with additional sub directory
RewriteRule ^/apache-mod-rules-redirect/(.*) /redirected?id=$1 [L,R=302]

원래 요청: /apache-mod-rules-redirect/1234

Add Apache mod redirect: Browser window with developer tools tracking the requests and responses

미들웨어는 다음과 같은 Apache mod_rewrite 서버 변수를 지원합니다:

  • CONN_REMOTE_ADDR
  • HTTP_ACCEPT
  • HTTP_CONNECTION
  • HTTP_COOKIE
  • HTTP_FORWARDED
  • HTTP_HOST
  • HTTP_REFERER
  • HTTP_USER_AGENT
  • HTTPS
  • IPV6
  • QUERY_STRING
  • REMOTE_ADDR
  • REMOTE_PORT
  • REQUEST_FILENAME
  • REQUEST_METHOD
  • REQUEST_SCHEME
  • REQUEST_URI
  • SCRIPT_FILENAME
  • SERVER_ADDR
  • SERVER_PORT
  • SERVER_PROTOCOL
  • TIME
  • TIME_DAY
  • TIME_HOUR
  • TIME_MIN
  • TIME_MON
  • TIME_SEC
  • TIME_WDAY
  • TIME_YEAR

IIS URL 재작성 모듈 규칙

IIS URL 재작성 모듈에 적용되는 것과 동일한 규칙 세트를 사용하려면 AddIISUrlRewrite를 사용합니다. 규칙 파일이 응용 프로그램과 함께 배포되고 있는지 확인하시기 바랍니다. Windows Server IIS에서 실행하는 경우 미들웨어에서 앱의 web.config 파일을 사용하도록 지시하지 않습니다. IIS를 사용하는 경우 IIS 재작성 모듈과 충돌하지 않도록 이러한 규칙을 앱의 web.config 파일 외부에 저장해야 합니다. IIS URL 재작성 모듈 규칙에 대한 자세한 내용 및 예제는 Url 재작성 모듈 2.0 사용URL 재작성 모듈 구성 참조를 참조하세요.

A StreamReader 는 규칙 파일에서 규칙을 읽는 IISUrlRewrite.xml 데 사용됩니다.

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

예제 응용 프로그램은 /iis-rules-rewrite/(.*)에서 /rewritten?id=$1로 요청을 재작성합니다. 응답이 200 - 정상 상태 코드와 함께 클라이언트에 전송됩니다.

<rewrite>
  <rules>
    <rule name="Rewrite segment to id querystring" stopProcessing="true">
      <match url="^iis-rules-rewrite/(.*)$" />
      <action type="Rewrite" url="rewritten?id={R:1}" appendQueryString="false"/>
    </rule>
  </rules>
</rewrite>

원래 요청: /iis-rules-rewrite/1234

Add IIS URL rewrite: Browser window with developer tools tracking the request and response

원하지 않는 방식으로 응용 프로그램에 영향을 주는 서버 수준 규칙이 구성된 활성 IIS 재작성 모듈이 존재할 경우 응용 프로그램에 대한 IIS 재작성 모듈을 비활성화할 수 있습니다. 보다 자세한 내용은 IIS 모듈 비활성화를 참고하시기 바랍니다.

지원되지 않는 기능

ASP.NET Core 2.x와 함께 출시된 미들웨어는 다음과 같은 IIS URL 재작성 모듈 기능을 지원하지 않습니다:

  • 아웃바운드 규칙
  • 사용자 지정 서버 변수
  • 와일드카드
  • LogRewrittenUrl

지원되는 서버 변수

미들웨어 다음 IIS URL 재작성 모듈 서버 변수를 지원합니다.

  • CONTENT_LENGTH
  • CONTENT_TYPE
  • HTTP_ACCEPT
  • HTTP_CONNECTION
  • HTTP_COOKIE
  • HTTP_HOST
  • HTTP_REFERER
  • HTTP_URL
  • HTTP_USER_AGENT
  • HTTPS
  • LOCAL_ADDR
  • QUERY_STRING
  • REMOTE_ADDR
  • REMOTE_PORT
  • REQUEST_FILENAME
  • REQUEST_URI

참고 항목

PhysicalFileProvider를 이용해서 IFileProvider를 가져올 수도 있습니다. 이 방식이 재작성 규칙 파일의 위치에 대해 더 많은 유연성을 제공할 수 있습니다. 재작성 규칙 파일이 서버의 지정한 경로에 배포되는지 확인하시기 바랍니다.

PhysicalFileProvider fileProvider = new PhysicalFileProvider(Directory.GetCurrentDirectory());

메서드 기반 규칙

메서드를 이용해서 직접 규칙 로직을 구현하고 싶다면 Add를 사용하면 됩니다. Add는 메서드에서 사용할 HttpContext를 사용할 수 있게 하는 RewriteContext를 공개합니다. RewriteContext.Result는 추가 파이프라인 처리가 수행되는 방법을 결정합니다. 값을 다음 표에 설명된 RuleResult 필드 중 하나로 설정합니다.

컨텍스트 결과 다시 작성 작업
RuleResult.ContinueRules (기본값) 규칙 적용을 계속합니다.
RuleResult.EndResponse 규칙 적용을 중지하고 응답을 보냅니다.
RuleResult.SkipRemainingRules 규칙 적용을 중지하고 컨텍스트를 다음 미들웨어로 보냅니다.
public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

샘플 앱은 .로 끝나는 .xml경로에 대한 요청을 리디렉션하는 메서드를 보여 줍니다. /file.xml에 대한 요청이 수행되면 해당 요청이 /xmlfiles/file.xml로 리디렉션됩니다. 상태 코드가 301 - Moved Permanently로 설정됩니다. 브라우저에서 새 요청을 /xmlfiles/file.xml수행하면 정적 파일 미들웨어는 wwwroot/xmlfiles 폴더에서 클라이언트에 파일을 제공합니다. 리디렉션의 경우 응답의 상태 코드를 명시적으로 설정합니다. 그렇지 않으면 200 - 정상 상태 코드가 반환되고 클라이언트에서 리디렉션이 수행되지 않습니다.

RewriteRules.cs:

public static void RedirectXmlFileRequests(RewriteContext context)
{
    var request = context.HttpContext.Request;

    // Because the client is redirecting back to the same app, stop 
    // processing if the request has already been redirected.
    if (request.Path.StartsWithSegments(new PathString("/xmlfiles")))
    {
        return;
    }

    if (request.Path.Value.EndsWith(".xml", StringComparison.OrdinalIgnoreCase))
    {
        var response = context.HttpContext.Response;
        response.StatusCode = (int) HttpStatusCode.MovedPermanently;
        context.Result = RuleResult.EndResponse;
        response.Headers[HeaderNames.Location] = 
            "/xmlfiles" + request.Path + request.QueryString;
    }
}

또한 이 방법은 요청을 다시 작성할 수도 있습니다. 샘플 앱에서는 wwwroot 폴더의 file.txt 텍스트 파일을 제공할 텍스트 파일 요청의 경로를 다시 작성하는 방법을 보여 줍니다. 정적 파일 미들웨어는 업데이트된 요청 경로에 따라 파일을 제공합니다.

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

RewriteRules.cs:

public static void RewriteTextFileRequests(RewriteContext context)
{
    var request = context.HttpContext.Request;

    if (request.Path.Value.EndsWith(".txt", StringComparison.OrdinalIgnoreCase))
    {
        context.Result = RuleResult.SkipRemainingRules;
        request.Path = "/file.txt";
    }
}

IRule 기반 규칙

Add를 사용하여 IRule 인터페이스를 구현하는 클래스에서 규칙 논리를 사용합니다. IRule은 메서드 기반 규칙 방식을 사용하는 것보다 더 높은 유연성을 제공합니다. 구현 클래스에는 ApplyRule 메서드에 대한 매개 변수를 전달할 수 있는 생성자가 포함될 수 있습니다.

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

예제 응용 프로그램은 extensionnewPath 매개 변수 값들이 다양한 조건을 만족하는지 검사합니다. extension은 값을 포함하고 있어야 하고 그 값은 .png, .jpg 또는 .gif 중 하나이어야 합니다. 만약 newPath가 유효하지 않으면 ArgumentException이 던져집니다. image.png에 대한 요청이 수행되면 해당 요청이 /png-images/image.png로 리디렉션됩니다. image.jpg에 대한 요청이 수행되면 해당 요청이 /jpg-images/image.jpg로 리디렉션됩니다. 상태 코드는 301 - Moved Permanently로 설정되고, context.Result는 규칙 처리를 중지하고 응답을 보내도록 설정됩니다.

public class RedirectImageRequests : IRule
{
    private readonly string _extension;
    private readonly PathString _newPath;

    public RedirectImageRequests(string extension, string newPath)
    {
        if (string.IsNullOrEmpty(extension))
        {
            throw new ArgumentException(nameof(extension));
        }

        if (!Regex.IsMatch(extension, @"^\.(png|jpg|gif)$"))
        {
            throw new ArgumentException("Invalid extension", nameof(extension));
        }

        if (!Regex.IsMatch(newPath, @"(/[A-Za-z0-9]+)+?"))
        {
            throw new ArgumentException("Invalid path", nameof(newPath));
        }

        _extension = extension;
        _newPath = new PathString(newPath);
    }

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

        // Because we're redirecting back to the same app, stop 
        // processing if the request has already been redirected
        if (request.Path.StartsWithSegments(new PathString(_newPath)))
        {
            return;
        }

        if (request.Path.Value.EndsWith(_extension, StringComparison.OrdinalIgnoreCase))
        {
            var response = context.HttpContext.Response;
            response.StatusCode = (int) HttpStatusCode.MovedPermanently;
            context.Result = RuleResult.EndResponse;
            response.Headers[HeaderNames.Location] = 
                _newPath + request.Path + request.QueryString;
        }
    }
}

원래 요청: /image.png

For image.png: Browser window with developer tools tracking the requests and responses

원래 요청: /image.jpg

For image.jpg: Browser window with developer tools tracking the requests and responses

정규식 예제

목표 정규식 문자열 및
일치 예제
대체 문자열 및
출력 예
경로를 쿼리 문자열로 재작성 ^path/(.*)/(.*)
/path/abc/123
path?var1=$1&var2=$2
/path?var1=abc&var2=123
후행 슬래시 제거 (.*)/$
/path/
$1
/path
후행 슬래시 적용 (.*[^/])$
/path
$1/
/path/
특정 요청 재작성 방지 ^(.*)(?<!\.axd)$ 또는 ^(?!.*\.axd$)(.*)$
예: /resource.htm
아니요: /resource.axd
rewritten/$1
/rewritten/resource.htm
/resource.axd
URL 세그먼트 재정렬 path/(.*)/(.*)/(.*)
path/1/2/3
path/$3/$2/$1
path/3/2/1
URL 세그먼트 대체 ^(.*)/segment2/(.*)
/segment1/segment2/segment3
$1/replaced/$2
/segment1/replaced/segment3

추가 리소스