Host ASP.NET Core on Nginx in Linux

Host ASP.NET Core on Nginx in Linux

Nginx is the king of Web Servers and can be used to host ASP.NET Core apps on a Linux system. Nginx acts as a reverse proxy server to redirect traffic to an ASP.NET Core web app running on Kestrel server. In this tutorial we are going to host ASP.NET Core app on Ngnix in Ubuntu Linux from start and so kindly read this tutorial on one go to understand the full process.

This tutorial is a part of the .NET Hosting Series which contains a total of 5 tutorials, which are:

  1. How to Host ASP.NET Core App in a Windows Service
  2. Host ASP.NET Core app on IIS
  3. Host ASP.NET Core on Apache in Windows
  4. Host ASP.NET Core on Apache in Linux
  5. Host ASP.NET Core on Nginx in Linux

Install .NET on Ubuntu

We will install .NET 8.0 SDK on our Ubuntu pc. First update the system by running the below command.

sudo apt-get update

Next, run the install command for .NET 8.0 sdk.

sudo apt-get install -y dotnet-sdk-8.0

Install DotNet Ubuntu

After installation is completed, run the dotnet --info command to to check the DOT NET version installed.

dotnet --info

With the installation of .NET completed we can start creating .NET app from CLI command.

Create ASP.NET Core App

We create an ASP.NET Core app through .NET command-line interface (CLI). Open terminal and run the below given .NET CLI command to create a MVC based app by the name of NginxDotNet.

dotnet new mvc --name NginxDotNet

The ASP.NET Core MVC app is created successfully.

dotnet new mvc cli command

Go inside the app folder with cd NginxDotNet then open Program class with Nano.

nano Program.cs

nano open program.cs

In the Program.cs file, you will find the hsts and redirection middlewares are already added.

app.UseHsts();
app.UseHttpsRedirection(); 

Nginx Web Server will be a Reverse Proxy Server when hosting ASP.NET Core so we need the requests to be forwarded by Nginx to the app hosted by Kestrel. We will use the Forwarded Headers Middleware from the Microsoft.AspNetCore.HttpOverrides package to do this forwarding job. The middleware updates the Request.Scheme, using the X-Forwarded-Proto header, so that redirect URIs and other security policies work correctly. This package is installed in the app by default.

Clean Architecture is a popular software development architecture which can be used in .NET apps – Implement Clean Architecuture in .NET

On the top of the Program.cs class include the namespace.

using Microsoft.AspNetCore.HttpOverrides;

And after var app = builder.Build() code, enter the Forwarded Headers Middleware code as given below.

app.UseForwardedHeaders(new ForwardedHeadersOptions
{
    ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});

The Program class should now looks as:

using Microsoft.AspNetCore.HttpOverrides;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllersWithViews();

var app = builder.Build();

app.UseForwardedHeaders(new ForwardedHeadersOptions
{
    ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

Save the file by pressing Ctrl+s, thex exit nano by Ctrl+x.

Run build command to check for any errors – dotnet build, it should succeed.

dotnet build cli command

Publish the app by the CLI publish command.

dotnet publish --configuration Release

The app will be published to the “bin” folder – bin/Release/net8.0/publish.

dotnet cli publish command

Let’s run the app through CLI command. Go inside the publish folder by the command.

cd bin/Release/net8.0/publish

Then type ls command to see all the files inside this folder. There is NginxDotNet.dll file of the app.

app dll file

We can execute this dll file through the given CLI command.

dotnet NginxDotNet.dll

running dotnet app cli command

The app is running at http://localhost:5000. Open this url on the browser to view the app.

Opening Nginx hosted App on browser

Press Ctrl+C to stop kestrel and with it the app will go down. With Linux Service we are going to run the app automatically when our pc starts.

We will host the app from the /var/www/ directory of Ubuntu. With mkdir create a new folder called “nginxdotnet”.

sudo mkdir /var/www/nginxdotnet

The folder nginxdotnet is created and now copy the app’s published files to it. Run the “cp” command given below from inside the app’s “publish” folder.

sudo cp -r . /var/www/nginxdotnet

That’s it, the apps published files are now copied to /var/www/nginxdotnet folder.

Create Service file

With Linux Service we can run the app whenever our system reboots. This will automate the process. So create a System file called kestrel-nginxdotnet.service inside /etc/systemd/system/ folder. We will use nano to create this file.

sudo nano /etc/systemd/system/kestrel-nginxdotnet.service

Paste the following code on nano.

[Unit]
Description=Nginx DOT NET App

[Service]
WorkingDirectory=/var/www/nginxdotnet
ExecStart=/usr/bin/dotnet /var/www/nginxdotnet/NginxDotNet.dll
Restart=always
# Restart service after 10 seconds if the dotnet service crashes:
RestartSec=10
KillSignal=SIGINT
SyslogIdentifier=nginxdotnet
User=www-data
Environment=ASPNETCORE_ENVIRONMENT=Production 

[Install]
WantedBy=multi-user.target

Save it and exit nano.

Explanation: We provided the /usr/bin/dotnet folder for .NET distributions and telling it to execute the app’s dll file i.e. /var/www/apachedotnet/ApacheDotNet.dll. This will run the app on Kestrel.

The user that manages the service is specified by the User option which is “www-data” in our case. We also have set RestartSec=10 to specify the time in seconds to wait for terminating the app in case the app does not start. A SIGKILL signal is issued by Linux to terminate the app.

Now enable the service:

sudo systemctl enable kestrel-nginxdotnet.service

Next start this service.

sudo systemctl status kestrel-nginxdotnet.service

Now we can check it’s status to confirm it is running.

sudo systemctl status kestrel-apachedotnet.service

DotNet app Linux Service

The app is running and available at the url – http://localhost:5000. Now we don’t have to manually run the app’s dll file like before as the service will start the app on Kestrel whenever our Linux system starts.

We can now proceed to the installation of Nginx Web Server and map this url to it.

Other important commands, to stop the service use sudo systemctl stop kestrel-nginxdotnet.service and to disable the service use sudo systemctl disable kestrel-nginxdotnet.service.
journalctl command

The journalctl command is used to see the logs to debug if anything is not working. The command is – sudo journalctl -fu kestrel-nginxdotnet.service.

journalctl logs

Install Nginx in Linux

We now install Nginx on Ubuntu. First update the system by the below command.

sudo apt update

Next, install Nginx by the below command.

sudo apt install nginx

Install Nginx Ubuntu

We should start Nginx.

sudo service nginx start

Let’s check the status now with the below command.

sudo service nginx status

Nginx Status

Some other Nginx command which you should know.

To stop Nginx.

sudo service nginx stop

To restart Nginx.

sudo service nginx restart

To reload Nginx.

sudo nginx -s reload

To validate the syntax.

sudo nginx -t

Configure Nginx for .NET app

In Ubuntu, Nginx virtual host configuration files are stored in /etc/nginx/sites-available location. We can create a new configuration for our .NET App and configure Nginx as a reverse proxy for it. So create a new configuration by the given command.

sudo nano /etc/nginx/sites-available/nginxdotnet

Enter the given configuration to it and save.

server {
   listen        80;
   server_name   localhost;
   location / {
       proxy_pass         http://127.0.0.1:5000/;
       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;
    }
}

Explanation: Here we provided the port number 80 of localhost to be mapped to the kestrel address for the app which is http://127.0.0.1:5000/. The server_name value is given localhost but for production you should give it the domain name like yogihosting.com.

Next, create the symlink:

sudo ln -s /etc/nginx/sites-available/nginxdotnet /etc/nginx/sites-enabled/nginxdotnet

Validate the syntax with the below command:

sudo nginx -t

You will get the message:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok 
nginx: configuration file /etc/nginx/nginx.conf test is successful 

Reload Nginx.

sudo nginx -s reload

Now open the url – http://localhost on the browser which should open the .NET app. I have shown this in the below image.

ASP.NET Core Nginx Hosting

Host ASP.NET Core App with SSL on Nginx

Let’s now use Self-Signed SSL with our ASP.NET Core app. Here we will do 3 things:

  1. Generate self-signed SSL from openSSL.
  2. Add SSL and perform redirection from http to https.
  3. Use a url to host our website.

Use the following openSSL commands to generate myssl.crt and myssl.key files for the SSL. Note that when command name is asked enter nginxdotnet.com for it as we will host this app from this domain.

openssl genrsa -des3 -out myssl.key 2048
openssl rsa -in myssl.key -out myssl.key.insecure
mv myssl.key myssl.key.secure
mv myssl.key.insecure myssl.key
openssl req -new -key myssl.key -out myssl.csr
openssl x509 -req -days 365 -in myssl.csr -signkey myssl.key -out myssl.crt

Generate SSL Openssl

Next copy them to certs and private folders.

sudo cp myssl.crt /etc/ssl/certs
sudo cp myssl.key /etc/ssl/private

Open app’s configuration file.

sudo nano /etc/nginx/sites-available/nginxdotnet

Add the new configuration which is given below.

limit_req_zone $binary_remote_addr zone=one:10m rate=5r/s;

server {
       listen 80;
       listen [::]:80;
       server_name nginxdotnet.com;
       return                301 https://$host$request_uri;
}

server {
        listen                    443 ssl http2;
        listen                    [::]:443 ssl http2;
        server_name               nginxdotnet.com;
        ssl_certificate           /etc/ssl/certs/myssl.crt;
        ssl_certificate_key       /etc/ssl/private/myssl.key;
        ssl_session_timeout       1d;
        ssl_protocols             TLSv1.2 TLSv1.3;
        ssl_prefer_server_ciphers off;
        ssl_ciphers               ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
        ssl_session_cache         shared:SSL:10m;
        ssl_session_tickets       off;
        ssl_stapling              off;

        add_header X-Frame-Options DENY;
        add_header X-Content-Type-Options nosniff;

        location / {
            proxy_pass http://127.0.0.1:5000/;
            limit_req  zone=one burst=10 nodelay;
        }
}

We now have 2 server block 80 and 443 for listening these port, 443 for SSL traffic. In the port 80 server block we provided server_name nginxdotnet.com since nginxdotnet.com will be our domain name. We are redirecting the requests from http to https from this code line – return 301 https://$host$request_uri.

On the 443 server block we provided the SSL files location:

ssl_certificate           /etc/ssl/certs/myssl.crt;
ssl_certificate_key       /etc/ssl/private/myssl.key;

On the location block we provided the app’s url on kestrel.

proxy_pass http://127.0.0.1:5000/;

Check the syntax, which should be ok.

sudo nginx -t

Reload Nginx.

sudo nginx -s reload

Now final thing is to add the entry of the domain in etc/hosts file. Run the below command to open the file in nano.

sudo nano /etc/hosts

At the end of this file add the below entry.

127.0.0.1  nginxdotnet.com

Save and exit.

Now open the url – nginxdotnet.com on your browser. First you will see a security warning, click Advanced button and then click Accept the risk and Continue button.

Congrats, the .NET app opens in Nginx. Notice it is redirected to the https version which is https://nginxdotnet.com.

Nginx SSL .NET app

View the certificate on the browser which will show you all the details.

SSL Info Browser

In the above configuration of Nginx we have perfomed redirection from Nginx itself and so we do not need the redirection middleware in the app i.e. remove app.UseHttpsRedirection(); code line in Program.cs. Also note that only one port which is 5000 is opened on kestral.

Performing redirection from http to https from Redirection Middleware

We can also perform redirection from the app and not on Nginx. In this case we will have to open another port for https traffic in appsettings.json. We must also need to have app.UseHttpsRedirection(); code line in Program.cs.

appsetting.json
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "Kestrel": {
    "Endpoints": {
      "MyHttpEndpoint": {
        "Url": "http://localhost:5000"
      },
      "MyHttpsEndpoint": {
        "Url": "http://localhost:6000"
      }
    }
  }
}

Inside the “Endpoints” we added MyHttpEndpoint and MyHttpsEndpoint for http and https traffic.

Nginx Configuration

Open it in nano.

sudo nano /etc/nginx/sites-available/nginxdotnet

The updated configuration.

limit_req_zone $binary_remote_addr zone=one:10m rate=5r/s;

server {
    listen                    80;
    server_name               nginxdotnet.com;
    location / {
        proxy_pass http://127.0.0.1:5000/;
        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;
    }
}
server {
        listen                    443 ssl http2;
        listen                    [::]:443 ssl http2;
        server_name               nginxdotnet.com;
        ssl_certificate           /etc/ssl/certs/myssl.crt;
        ssl_certificate_key       /etc/ssl/private/myssl.key;
        ssl_session_timeout       1d;
        ssl_protocols             TLSv1.2 TLSv1.3;
        ssl_prefer_server_ciphers off;
        ssl_ciphers               ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
        ssl_session_cache         shared:SSL:10m;
        ssl_session_tickets       off;
        ssl_stapling              off;

        add_header X-Frame-Options DENY;
        add_header X-Content-Type-Options nosniff;

        location / {
            proxy_pass http://127.0.0.1:5000/;
            limit_req  zone=one burst=10 nodelay;
        }
}

Notice we have removed redirection, now reload Nginx.

sudo nginx -s reload

And done. You app is hosted successfully.

Conclusion

We successully install .NET, Nginx Web Server and hosted .NET on Nginx as a reverse proxy. The whole process was set in Ubuntu Linux. We also created an SSL with openSSL and used it on Nginx for our app. I hope you enjoyed learning from this tutorial. If you face any difficuly then use the comments section to message me.

SHARE THIS ARTICLE

  • linkedin
  • reddit
yogihosting

ABOUT THE AUTHOR

I hope you enjoyed reading this tutorial. If it helped you then consider buying a cup of coffee for me. This will help me in writing more such good tutorials for the readers. Thank you. Buy Me A Coffee donate

Leave a Reply

Your email address will not be published. Required fields are marked *