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.
Page Contents
This tutorial is a part of the .NET Hosting Series which contains a total of 5 tutorials, which are:
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
After installation is completed, run the dotnet --info
command to to check the DOT NET version installed.
With the installation of .NET completed we can start creating .NET app from CLI command.
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.
Go inside the app folder with cd NginxDotNet then open Program class with Nano.
nano 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.
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.
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.
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.
We can execute this dll file through the given CLI command.
dotnet NginxDotNet.dll
The app is running at http://localhost:5000. Open this url on the browser to view the app.
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.
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
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.
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.
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
We should start Nginx.
sudo service nginx start
Let’s check the status now with the below command.
sudo service nginx status
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
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.
Let’s now use Self-Signed SSL with our ASP.NET Core app. Here we will do 3 things:
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
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.
View the certificate on the browser which will show you all the details.
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.
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.
{
"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.
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.
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.