Deploy .NET Core web site wit SignalR behind Nginx reverse proxy

Check list to verify that your application is ready for deployment behind NGINX reverse proxy.

Here is the list of points to check when you hosting web site behind reverse proxy:

  1. You use everywhere Url.Content("~/...") in your Razor pages
  2. You enabled using of forwarded headers
  3. If you’re deploying to sub-folder, you need to specify base path in the application
  4. You need to configure NGINX

Relative URLs in razor pages and Sub-folders

Usage of the class UrlHelper will utilize the base path which may be configured for your website. If your website is located under a sub-folder, than any link which starts with /... is referencing its root. For example, if you need to have your website be accessible using this URL http://common.com/app/... then all links to images, css, scripts should be relative to /app/ folder. If you use just /images browser will understand that you want to load a resource from the root (http://common.com/images/) instead of the sub-folder (http://common.com/app/images/). So that you need explicitly to tell browser where to look for resources. Using of the Url helper and specifying links with ~ solves this.

If you’re deploying th web site under a sub-folder, you may want to specify this in Startup.cs file. Use this middleware for that purpose.

app.UsePathBase("/folder");

Utilization of forwarded HTTP headers

In order to work behind reverse proxy, your web site should know a bit about outside internet to correctly build references and request correct resources. The web server (NGINX in our case) sends special HTTP headers to say to the web site about this host and other connection parameters. Your application in its turn should react and do some stuff when it gets those headers.

Starting from .NET Core 3.0 it’s possible to enable usage of HTTP forwarded header just specifying environment variable ASPNETCORE_FORWARDEDHEADERS_ENABLED to true. The default hosting configuration of ASP.NET will configure Kestler to use them. In older version you had to add a middleware which enables usage of those headers. More about that is written here.

NGINX and SignalR

The NGINX configuration for the website which has SignalR enabled should include configuration for hub endpoint. If it’s missing then you can have such error message in DevTools:

Error during WebSocket handshake: Unexpected response code: 204

Additional configuration should specify to use header Connection with the value “upgrade”. And the sample configuration may look as follows (assuming, that the web site is deployed to a sub-folder /app):

worker_processes 4;
 
events { worker_connections 1024; }
 
http {
    sendfile on;
 
    upstream app_servers {
        server http://x.x.x.x:5050;
    }
 
    server {
        listen 80;
 
        location /app {
            proxy_pass         http://app_servers;
            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-Host $server_name;
            proxy_set_header   Upgrade $http_upgrade;
            proxy_set_header   Connection keep-alive;
            proxy_cache_bypass $http_upgrade;
        }
        
        location /app/notifications {
            proxy_pass         http://app_servers/notifications;
            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-Host $server_name;
            proxy_set_header   Upgrade $http_upgrade;
            proxy_set_header   Connection "upgrade";
            proxy_cache_bypass $http_upgrade;
        }
    }
}

SignalR is using the endpoint /notifications. If you need to use TCP port inside the configuration you can do this via $server_port. But it will be equal to the one which NGINX listens to (80 in the example). If you need another port, for example 8888, you can modify setting for Host header and use something like this:

proxy_set_header   Host $host:8888;

See Also

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Related Post

%d bloggers like this: