.Net core X-Forwarded-Proto header doesn't pass to Nginx properly

asp.net core forward request
asp.net core load balancing
x-forwarded-for c#
nginx x-forwarded-proto
nginx x-forwarded-for
useforwardedheaders github
net core proxy settings
get x-forwarded-for .net core

Sorry for the edit history but this issue was really unclear to me and it was difficult to locate the exact problem.

I have a .Net-Core web application that runs behind a Nginx and the X-Forwarded-Proto always passes http instead of https.

Startup.cs

 public void ConfigureServices(IServiceCollection services)
        {
            services.Configure<ForwardedHeadersOptions>(options =>
            {
                options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
            });

            services.AddMvc();

        }

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            //first middlewear
            app.UseForwardedHeaders();
             //and the rest
         }

Nginx conf

server {
    listen        80;
    server_name   example.com;
    location / {
        proxy_pass         http://localhost:5001/;
        proxy_http_version 1.1;
        proxy_set_header   Upgrade $http_upgrade;
        proxy_set_header   Connection keep-alive;
        proxy_set_header   Host $host;
        proxy_cache_bypass $http_upgrade;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;
    }
}

Nginx.conf

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    #cloudflare real ip
    #https://support.cloudflare.com/hc/en-us/articles/200170786-Restoring-original-visitor-IPs-Logging-visitor-IP-addresses-with-mod-cloudflare-#12345681
    set_real_ip_from 173.245.48.0/20;
    real_ip_header    X-Forwarded-For;
    real_ip_recursive on;

    log_format  main  '"$scheme" $remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

access.log record

"http" 185.108.83.156 - - [03/Oct/2019:19:59:33 +0300] "GET /auth/signin/Facebook?returnUrl=%2F HTTP/1.1" 302 0 "https://example.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36" "156"

as you can see the $scheme that I log is always HTTP.

A solution that solves the issue is to enforce Scheme to be HTTPS like so:

app.Use((context, next) =>
        {
            context.Request.Scheme = "https";
            return next();
        });

from https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-2.2#forwarded-headers-middleware-options

But with this solution I don't pass the headers and loses some information.

So does anyone have any solution for this case?


Your Startup.cs file is fine but you didn't configure ssl for Kesterl.

You need to configure a X.509 ssl certificate for your .net core application

From https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel?view=aspnetcore-2.2#when-to-use-kestrel-with-a-reverse-proxy-1

Only the reverse proxy server requires an X.509 certificate, and that server can communicate with the app's servers on the internal network using plain HTTP

So in your case you have 7 main steps to do: 1) Get a SSL certificate from CloudFlare ORIGIN CA 2) Install the ssl certificate in your server 3) Create a PFX file so your Kesterl Server can read it. 4) Configure Kesterl https end points. 5) Modify your Nginx server to listen to port 443 and then redirect to Kestrel listening ports 6) Modify service files accordingly your configuration 7) Change Cloudflare to run Strict mode

Step 1 - Get ssl certificate from cloudflare: Go to your dashboard at Cloudflare and choose SSL/TLS --> Origin Server -->Create Certificate. Follow the instructions and you will have 2 files at the end of the process: example.com.key example.com.pem

Step 2 - Place the files in your server at etc/ssl folder

Step 3 - Create PFX file so Kesterl can use the certificate: run this at your server:

openssl pkcs12 -export -out example.pfx -inkey example.com.key -in example.com.pem

This will generate the example.pfx, place it in the same folder.

Step 4 - Configure Kesterl https end points. From step 3 you should have got a password that you need to use at this step. At your appsetting.json place this lines:

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "Kestrel": {
    "Endpoints": {
      "HTTPS": {
        "Url": "https://localhost:5001",
        "Certificate": {
          "Path": "/etc/ssl/example.pfx",
          "Password": "**********"
        }
      }
    }
  }

Step 5 - Configure your Nginx to listen on port 443 like so:

server {
    #listen        80;
    listen                 *:443 ssl;
    ssl_certificate        /etc/ssl/example.pem;
    ssl_certificate_key    /etc/ssl/example.key;
    server_name            example.com
    location / {
        proxy_pass         https://localhost:5001/;
        proxy_http_version 1.1;
        proxy_set_header   Upgrade $http_upgrade;
        proxy_set_header   Connection keep-alive;
        proxy_set_header   Host $host;
        proxy_cache_bypass $http_upgrade;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;
    }
}

Step 6 - configure your service file like so:

  [Unit]
    Description=example

    [Service]
    WorkingDirectory=/var/www/example
    ExecStart=/usr/bin/dotnet /var/www/example/exampleSite.dll
    Restart=always
    # Restart service after 10 seconds if the dotnet service crashes:
    RestartSec=10
    KillSignal=SIGINT
    SyslogIdentifier=example
    User=www-data
    Environment=ASPNETCORE_ENVIRONMENT=Staging
    Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false
    Environment=ASPNETCORE_HTTPS_PORT=5001
    Environment=ASPNETCORE_URLS=https://localhost:5001

    [Install]
    WantedBy=multi-user.target

Step 7 - Change Cloudflare at SSL/TLS to use Strict mode

Restart your application + Nginx and your server should now work like this:

Request --> Cloudflare(HTTPS) --> Nginx(HTTPS)-->Example.com

Forwarded Headers Middleware Updates in .NET Core 3.0 preview 6, .net Core has a default set for the forwarded headers. It defaults to 127.0.0.1, for IIS integration. After tracking down the source code, you can  The forwarded headers are named X-Forwarded-For and X-Forwarded-Proto. Not all network appliances add the X-Forwarded-For and X-Forwarded-Proto headers without additional configuration. Consult your appliance manufacturer's guidance if proxied requests don't contain these headers when they reach the app.


Generally, the Headers of HTTP doesn't accept non-ASCII characters. Urls must be properly encoded when generated, the Redirect methods don't do it for you.

A quick fix could be to use the WebUtility.UrlEncode

using System.Net;
// ...
var encodedLocationName = WebUtility.UrlEncode(locationName);
return Redirect("~/locations/" + encodedLocationName);

.net Core X Forwarded Proto not working, Asp.Net Core, reverse proxy and X-Forwarded-* headers with XForwardedHost + XForwardedProto (which is all that is needed for a Now the middleware does not update the HttpContext based on the X-ForwardedFor-*  Like shown above, a forwarded header will help the application to determine which is the original IP that sent the request. It’s up to the proxy server to set up the headers correctly. We have a couple of headers: X-Forwarded-Host – the original host name; X-Forwarded-Proto – the original scheme (http or https) X-Forwarded-For – the original IP


This looks to be an issue with the way your NGinx file and Cloudflare is setup. From the config, you setup is

HTTPS->Cloudflare->HTTP->Your Server

This is why Scheme is always "http" in your nginx log. As Cloudflare is passing the request to your server over HTTP. You should have TLS setup between Cloudflare and you, which would enable port 443 on your nginx config, which would set the scheme as HTTPS. Forcing dotnet to beleive it was handled over HTTPS is not valid as Cloudflare is sending your clients data unencrypted over the public internet.

https://support.cloudflare.com/hc/en-us/articles/115000479507-Managing-Cloudflare-Origin-CA-certificates

Cloudflare's documentation lists how to set this up with NGinx.

Asp.Net Core, reverse proxy and X-Forwarded-* headers – soapfault , https://docs.microsoft.com/en-us/aspnet/core/publishing/ Headers.TryGetValue(​XForwardedProto, out StringValues proto)) { context.Request. If you call UseForwardedHeaders with no arguments, it does nothing and throws no exception. NET Core to work with proxy servers and load balancers topic. ASP.NET Core has the flexibility to add HTTP response headers anywhere in the middleware pipeline. Dino Esposito explains what you need to know to handle the headers in ASP.NET Core.


You're only running nginx on port 80 and without ssl, so, the $scheme variable will always be http. See http://nginx.org/r/$scheme.

If you care about https, it's probably a good idea to secure the connection between Cloudflare and your backend with HTTPS, too; this way, your $scheme variable will be populated correctly. Otherwise, you could also hardcode https in place of $scheme, but then it defeats the purpose of having a conditional test and a redirect on your real and final .Net backend.

Docs for UseForwardedHeaders, working with reverse proxies and , My application is built on Dot net core 1.1 not 3.0 which is latest. Real Client IP address using X-Forwarded-For header issue #15370 https://docs.microsoft.​com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer? My new book, ASP.NET Core in Action, Second Edition is available now, and supports .NET Core 3.1! May 19, 2016 in ASP.NET Core Middleware ~ 6 min read. How to add default security headers in ASP.NET Core using custom middleware


Please set appropriate character set in your nginx file.

Here is the documentation http://nginx.org/en/docs/http/ngx_http_charset_module.html#charset_types

Real Client IP address using X-Forwarded-For header issue · Issue , Basically, what happens under a proxy is that our app doesn't directly X-​Forwarded-Host – the original host name; X-Forwarded-Proto – the  By Rick Anderson and Kirk Larkin. This article shows how to enable CORS in an ASP.NET Core app. Browser security prevents a web page from making requests to a different domain than the one that served the web page.


Properly configure forwarded headers in ASP.NET Core – Mindgaze , X-Forwarded-Host - HttpContext.Request.Host. The minimal ASP.NET Core MVC application does not have ForwardedHeaders enabled by default, but if you're  First, you must enable the X-Forwarded-For header by editing your server (s). In the settings dialog you will see the “Use client IP header” option. Enable it and then specify the header name “X-Forwarded-For” as shown in the screen capture below: Okay, now that you’ve completed that step, you are ready to extract it within your code.


The X-Forwarded-* headers are set by proxies in asp .net core , NET Core web application to the cloud you are likely putting it behind a load balancer. There are generally 3 headers which are added to the request header to tell your application about how it was X-Forwarded-Proto: The scheme from the original client and proxies. What this does is updates the. Making ASP.NET Core understand SSL Acceleration. The "properly interpret" means that application needs to detect the presence of the header and if the value indicates that original request was over HTTPS it should be treated as such. In case of ASP.NET Core the perfect behavior would be for HttpContext.Request.IsHttps to return true.


Configuring ASP.NET Core Behind a Load Balancer, UseRouting(); // Invoke the UseForwardedHeaders middleware and configure it // to forward the X-Forwarded-For and X-Forwarded-Proto headers. // NOTE: This  But: unfortunately it doesn't work when the client is behind our corporate proxy. In this case, Connection.RemoteIpAddress everytime returns 127.0.0.1 . Doing some more investigation we found out that the X-Forwarded-For request header was correctly set for clients that come over the proxy: X-Forwarded-For: <client-ip>, 127.0.0.1, <proxy-ip:port> .