question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Documentation : behind a reverse proxy

See original GitHub issue

Here is a code snippet you could include in the documentation reltive to RootUrl https://github.com/domaindrivendev/Swashbuckle#rooturl

I think I am facing a common problem (2 chained reverse proxies) and many users can benefit from this…

    public static string ComputeHostAsSeenByOriginalClient(HttpRequestMessage req)
    {
        if (req.Headers.Contains("X-Forwarded-Host"))
        {
            //we are behind a reverse proxy, use the host that was used by the client
            var xForwardedHost = req.Headers.GetValues("X-Forwarded-Host").First();
            //when multiple apache httpd are chained, each proxy append to the header 
            //with a comma (see //https://httpd.apache.org/docs/2.4/mod/mod_proxy.html#x-headers).
            //so we need to take only the first host because it is the host that was 
            //requested by the original client.
            //note that other reverse proxies may behave differently but 
            //we are not taking care of them...
            var firstForwardedHost = xForwardedHost.Split(',')[0];

            //now that we have the host, we also need to determine the protocol used by the 
            //original client.
            //if present, we are using the de facto standard header X-Forwarded-Proto, assuming 
            //that only the first reverse proxy in the chain added it.
            //otherwise, we fallback to http
            //note that this is extremely brittle, either because the first proxy 
            //can "forget" to set the header or because another proxy can rewrite it...
            var xForwardedProto = req.Headers.Contains("X-Forwarded-Proto")
                ? req.Headers.GetValues("X-Forwarded-Proto").First()
                : "http";
            return xForwardedProto + "://" + firstForwardedHost;
        }
        else
        {
            //no reverse proxy mean we can directly use the RequestUri
            return req.RequestUri.Scheme + "://" + req.RequestUri.Authority;
        }
    }

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Comments:8 (1 by maintainers)

github_iconTop GitHub Comments

2reactions
nsdev0commented, May 23, 2019

@ottomatic RequestUri.Authority already contains port number. А non-correct address will be generated because of it, like http://localhost:19292:19292/swagger/docs/v1

1reaction
daffodilisticcommented, Oct 13, 2022

Hi, @ottomatic thanks for the code snippet! It didn’t really work with my configuration, however (ASP.NET MVC app on IIS behind a Caddy reverse proxy). Basically the changes were to use the host portion of the URI instead of authority, and specifying default ports if X-Forwarded-Port is not defined. Here’s the modified code:

static string ComputeHostAsSeenByOriginalClient(HttpRequestMessage req)
{
    var host = req.RequestUri.Host;
    var scheme = req.RequestUri.Scheme;
    var port = req.RequestUri.Port;

    if (req.Headers.Contains("X-Forwarded-Host"))
    {
        // we are behind a reverse proxy, use the host that was used by the client
        var xForwardedHost = req.Headers.GetValues("X-Forwarded-Host").First();

        /*
         When multiple apache httpd are chained, each proxy append to the header
          with a comma (see //https://httpd.apache.org/docs/2.4/mod/mod_proxy.html#x-headers).
          so we need to take only the first host because it is the host that was
          requested by the original client.
          note that other reverse proxies may behave differently but
         we are not taking care of them...
         */
        var firstForwardedHost = xForwardedHost.Split(',')[0];

        host = firstForwardedHost;
    }

    if (req.Headers.Contains("X-Forwarded-Proto"))
    {
        /*
         now that we have the host, we also need to determine the protocol used by the
         original client.
         if present, we are using the de facto standard header X-Forwarded-Proto
         otherwise, we fallback to http
         note that this is extremely brittle, either because the first proxy
         can "forget" to set the header or because another proxy can rewrite it...
        */
        var xForwardedProto = req.Headers.GetValues("X-Forwarded-Proto").First();
        if (xForwardedProto.IndexOf(",") != -1)
        {
            // >hen multiple apache, X-Forwarded-Proto is also multiple ...
            xForwardedProto = xForwardedProto.Split(',')[0];
        }

        scheme = xForwardedProto;
    }

    if (req.Headers.Contains("X-Forwarded-Port"))
    {
        var xForwardedPort = req.Headers.GetValues("X-Forwarded-Port").First();
        if (xForwardedPort.IndexOf(",") != -1)
        {
            // When multiple apache, X-Forwarded-Proto is also multiple ...
            xForwardedPort = xForwardedPort.Split(',')[0];
        }

        int.TryParse(xForwardedPort, out port);
    }
    else
    {
        // If port is missing, set port to defaults
        if (("http".Equals(scheme, StringComparison.InvariantCultureIgnoreCase))) port = 80;
        if (("https".Equals(scheme, StringComparison.InvariantCultureIgnoreCase))) port = 443;
    }

    // If we have standard scheme + port, leave out the port in the resulting Url.
    if (("http".Equals(scheme, StringComparison.InvariantCultureIgnoreCase) && port == 80)
        || ("https".Equals(scheme, StringComparison.InvariantCultureIgnoreCase) && port == 443))
    {
        return scheme + "://" + host;
    }

    return scheme + "://" + host + ":" + port.ToString();
}
Read more comments on GitHub >

github_iconTop Results From Across the Web

Using Behind a Reverse Proxy — waitress 2.1.2 ...
If you're using Waitress behind a reverse proxy, you'll almost always want your reverse proxy to pass along the Host header sent by...
Read more >
NGINX Reverse Proxy | NGINX Documentation
Configure NGINX as a reverse proxy for HTTP and other protocols, with support for modifying request headers and fine-tuned buffering of responses.
Read more >
Using reverse proxies — Dataiku DSS 12 documentation
HTTPS deployment behind a nginx reverse proxy¶. DSS can also be accessed using secure HTTPS connections, provided you have a valid certificate for...
Read more >
What is a reverse proxy? | Proxy servers explained
A reverse proxy protects web servers from attacks and can provide performance and reliability benefits. Learn more about forward and reverse proxies.
Read more >
Running behind reverse proxy — Flower 2.0.0 documentation
To run Flower behind a reverse proxy, remember to set the correct Host header to the request to make sure Flower can generate...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found