Linux

[Linux] Nginx 리버스 프록시 뒤의 ASP.NET Core: UseForwardedHeaders 클라이언트 IP 유실 해결

전자기린 2026. 11. 11. 12:14

 

Nginx 리버스 프록시 뒤에서 ASP.NET Core를 운영하면 클라이언트 IP가 127.0.0.1이나 프록시 IP로 찍히고, UseHttpsRedirection이 무한 리다이렉트를 만들기도 합니다. UseForwardedHeaders 설정과 Nginx 헤더 구성을 짝으로 맞추는 절차입니다.

 

절차

 

  1. 증상 구분
  2. Nginx 헤더 전달 설정
  3. UseForwardedHeaders 등록과 순서
  4. 신뢰 프록시 범위 지정
  5. HTTPS 리다이렉트 루프 해결
  6. 확인 방법
  7. 보안 주의점

 

1. 증상 구분

 

  1. HttpContext.Connection.RemoteIpAddress가 127.0.0.1 또는 내부 대역입니다.
  2. 인증·레이트 리밋이 모든 사용자를 한 IP로 취급합니다.
  3. Request.Scheme이 http로 보여 https 리다이렉트가 반복됩니다.
  4. app.UseHttpsRedirection()만 켜면 ERR_TOO_MANY_REDIRECTS가 납니다.

 

원인은 프록시가 클라이언트 정보를 헤더로 넘기고, 애플리케이션이 그 헤더를 신뢰하도록 설정되지 않은 것입니다.

 

2. Nginx 헤더 전달 설정

 

server {
    listen 443 ssl;
    server_name api.example.com;

    location / {
        proxy_pass http://10.0.0.11:8080;
        proxy_http_version 1.1;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Port $server_port;

        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

 

  1. $proxy_add_x_forwarded_for는 기존 헤더에 클라이언트 IP를 덧붙입니다. 값이 클라이언트, 프록시1, 프록시2 형태의 목록이 됩니다.
  2. X-Forwarded-Proto를 넘기지 않으면 애플리케이션은 항상 http로 인식합니다.
  3. $remote_addr는 Nginx가 본 직전 홉의 주소입니다. 프록시가 여러 겹이면 real_ip 모듈로 앞단 주소를 먼저 정리합니다.

 

3. UseForwardedHeaders 등록과 순서

 

using Microsoft.AspNetCore.HttpOverrides;

var builder = WebApplication.CreateBuilder(args);

builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders = ForwardedHeaders.XForwardedFor
                             | ForwardedHeaders.XForwardedProto;
    options.ForwardLimit = 1;
});

var app = builder.Build();

// 반드시 다른 미들웨어보다 먼저
app.UseForwardedHeaders();

app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();

app.MapGet("/ip", (HttpContext context) => new
{
    ip = context.Connection.RemoteIpAddress?.ToString(),
    scheme = context.Request.Scheme,
    forwardedFor = context.Request.Headers["X-Forwarded-For"].ToString()
});

app.Run();

 

  1. UseForwardedHeaders는 UseHttpsRedirection, 인증, 라우팅보다 앞에 둡니다. 뒤에 두면 이미 http로 판정된 뒤라 효과가 없습니다.
  2. ForwardedHeaders를 지정하지 않으면 아무 헤더도 처리하지 않습니다.
  3. ForwardLimit은 몇 홉까지 신뢰할지 정합니다. 프록시가 1대면 1입니다. null로 두면 X-Forwarded-For의 가장 왼쪽 값을 쓰므로, 클라이언트가 임의로 헤더를 보내면 위조가 가능합니다.

 

4. 신뢰 프록시 범위 지정

 

기본 설정은 루프백만 신뢰합니다. 컨테이너·사설망 프록시는 명시적으로 추가해야 합니다. .NET 10 이후에는 KnownNetworks가 폐기되고 KnownIPNetworks(System.Net.IPNetwork)를 씁니다.

 

using System.Net;

builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders = ForwardedHeaders.XForwardedFor
                             | ForwardedHeaders.XForwardedProto;

    options.KnownIPNetworks.Clear();
    options.KnownProxies.Clear();

    // Nginx가 도는 사설 대역
    options.KnownIPNetworks.Add(new IPNetwork(IPAddress.Parse("10.0.0.0"), 8));
    options.KnownProxies.Add(IPAddress.Parse("10.0.0.5"));
});

 

  1. KnownIPNetworks는 CIDR 대역, KnownProxies는 단일 IP입니다.
  2. 신뢰 범위를 넓히면 그만큼 위조 위험이 커집니다. 프록시 대역만 남깁니다.
  3. 사설망이 고정되지 않은 환경(오토스케일링)에서는 프록시 주소 대신 서비스 메시나 인그레스 컨트롤러가 넣는 헤더를 기준으로 정책을 잡습니다.

 

5. HTTPS 리다이렉트 루프 해결

 

루프는 애플리케이션이 요청을 http로 인식해 https로 보내고, 프록시가 다시 http로 넘기는 순환에서 생깁니다. 순서대로 확인합니다.

 

  1. X-Forwarded-Proto가 Nginx에서 실제로 전달되는지 확인합니다.
  2. ForwardedHeaders에 XForwardedProto가 포함됐는지 확인합니다.
  3. UseForwardedHeaders()가 UseHttpsRedirection()보다 앞에 있는지 확인합니다.

 

프록시에서 TLS를 종료하고 내부는 http로 통신하는 구성이라면, 리다이렉트를 애플리케이션이 아니라 Nginx에서 처리하는 편이 단순합니다.

 

server {
    listen 80;
    server_name api.example.com;
    return 301 https://$host$request_uri;
}

 

6. 확인 방법

 

curl -i https://api.example.com/ip
curl -H "X-Forwarded-For: 1.2.3.4" https://api.example.com/ip

 

  1. 첫 요청에서 실제 공인 IP와 https가 보이면 정상입니다.
  2. 두 번째 요청처럼 헤더를 직접 넣어 위조가 통과되는지 확인합니다. 신뢰 범위가 넓으면 그대로 반영됩니다.
  3. 로그에는 RemoteIpAddress와 X-Forwarded-For 원본을 함께 남겨 두면 추적이 쉽습니다.

 

7. 보안 주의점

 

  1. X-Forwarded-For는 클라이언트가 임의로 보낼 수 있는 헤더입니다. 프록시를 반드시 거치게 하고, 애플리케이션 포트를 외부에 열지 않습니다.
  2. 인증·레이트 리밋의 기준 IP를 이 헤더로만 판단하면 우회됩니다. 신뢰 프록시에서 온 요청인지 먼저 검증합니다.
  3. IPv6를 함께 쓴다면 KnownIPNetworks에 IPv6 대역도 추가합니다.
  4. 리다이렉트 URL을 Host 헤더로 만들 때는 허용 호스트 목록을 함께 검증합니다. 호스트 헤더 주입으로 외부 도메인으로 유도될 수 있습니다.

 

정리

 

Nginx에서 X-Forwarded-For와 X-Forwarded-Proto를 넘기고, 애플리케이션은 UseForwardedHeaders를 다른 미들웨어보다 먼저 등록합니다. 신뢰 프록시 대역을 KnownIPNetworks로 좁히고, TLS 종료 지점을 한 곳으로 정하면 IP 유실과 리다이렉트 루프가 함께 해결됩니다.