This post is mirrored on personal blog and my Medium Account.
An example of what a final running result could look like is here, and was built based on my JSON Patch filtering blog post.
Please be respectful with the example site, just give it a test to see how it works. Spam and other nonsense will be quickly dealt with.
Background: A Use Case More Complex than the Tutorial
So I just spent a few days banging my head against my desk 😡, trying to get my .NET 5.0 application with a React SPA to live under a separate URL via a reverse proxy. While the official Microsoft tutorial for hosting .NET on Linux is very detailed, it's only for a single site running at port 80
, where the root of the site is assumed to be the .NET app itself. They also leave all ports as their default values in their tutorial.
On my own server, I have a unique setup, with proxies to multiple site URLs to various different ports running various applications using NGINX.
I am familiar with building proxies to node servers, but for the first time I was trying out running a .NET server with a react SPA - and uh, ran into some troubles. 😭
I am going to be explicitly clear with all file naming, URLs, paths, and ports, because I was scratching my head for too long based on all the oversimplified examples I read online! Hopefully, this can save you from my struggles. 😄
Here's the bare minimum of steps you need to proxy a .NET 5.0 app with React SPA on a Linux machine with NGINX.
Assumed Environment (Important: Please Read!)
For this tutorial, we're going to assume we already have a running website called mysite.com
.
We'll assume we want to reverse proxy to our .NET app on the URL /my-first-dotnet-app/
.
In other words, if somebody visits mysite.com/my-first-dotnet-app/
, we should see our React SPA that we built in .NET, and NOT what would otherwise be the homepage or 404 site of mysite.com
.
We'll assume our project's source code exists in a folder called MyFirstDotnetApp/
. (You could imagine the GitHub repository could be called that, so when it's cloned all the code goes in such a named folder)
Finally, we will also assume this MyFirstDotnetApp/
folder exists on the Linux server in the path /var/www/
, as the official Microsoft documents recommend (and as is the default for website source code on Linux machines).
Sound good? Let's go! 🚀
Step 1 - Extend the NGINX Configuration for mysite.com to Include a Reverse Proxy
Your extended configuration can be as simple as:
location /my-first-dotnet-app/ {
proxy_pass http://localhost:1234/;
}
After making this change, don't forget to restart NGINX with:
sudo service nginx restart
Microsoft recommends adding other NGINX directives, but my super basic example works just fine with this basic configuration.
You may have also noticed that I've chosen to proxy pass to http://localhost:1234/
. The default port(s) for a .NET app, in both production and development mode are 5000
for HTTP and 5001
for HTTPS. In my case, I already had something running at port 5000
, so I went with a completely different port. I also only need an HTTP port, since we assume mysite.com
is already set up with HTTPS.
Step 2 - Configure the Default Kestrel Port for the .NET Application
As mentioned above, we are using port number 1234
to run our application. This requires a change in the configuration of our .NET application.
Hop into your appsettings.json
file in your .NET project and add this node:
"Kestrel": {
"Endpoints": {
"Http": {
"Url": "http://localhost:1234"
}
}
}
This will override the default of port 5000
and tell Kestrel to run at port 1234
. Kestrel will see this when we fire off the dotnet
command to start the project from a service file, which we are about to create in the next step.
Step 3 - Remove HTTPS Redirect from the .NET App
I mentioned for this example we assume that mysite.com
already has https setup (and NGINX is handling the HTTPS redirect, so we don't need .NET for that). Hop into Startup.cs
and delete the following line:
app.UseHttpsRedirection();
Step 4 - Setup React for the Correct Path with the package.json Homepage Directive
This one is the biggest gotcha. You can do everything else correct and still get frustrating 404s and the dreaded white screen.
Hop into your package.json
of your React SPA (under ClientApp
), and add the following:
"homepage": "https://mysite.com/my-first-dotnet-app",
This tells React to build the site assuming that it is hosted at /my-first-dotnet-app/
, which is exactly what we are doing 😄. Because React builds a static index.html
with all file paths (.js
and .css
for example) relative to index.html
, this step is a must, even with the reverse proxy in NGINX.
Step 5 - Create a Service File to Run the .NET Project
When we run a build with:
dotnet publish --configuration Release
.NET will put our published .dll
file and React artifacts in the following folder:
MyFirstDotnetApp/bin/Release/net5.0/publish/
The .dll
file itself will also have the same name as our project, i.e. for this example MyFirstDotnetApp.dll
.
This is an important path that we need to use in our service file. Let's construct it now, based on Microsoft's recommended service file:
Description=My First Dotnet App
[Service]
WorkingDirectory=/var/www/MyFirstDotnetApp/bin/Release/net5.0/publish/
ExecStart=/usr/bin/dotnet /var/www/MyFirstDotnetApp/bin/Release/net5.0/publish/MyFirstDotnetApp.dll
Restart=always
# Restart service after 10 seconds if the dotnet service crashes:
RestartSec=10
KillSignal=SIGINT
SyslogIdentifier=my-first-dotnet-app
User=root
Environment=ASPNETCORE_ENVIRONMENT=Production
Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false
[Install]
WantedBy=multi-user.target
Save this file as
/etc/systemd/system/my-first-dotnet-app.service
We can then enable this service with:
sudo systemctl enable my-first-dotnet-app.service
and start it with:
sudo systemctl start my-first-dotnet-app.service
We should be all set.
Go ahead and navigate to mysite.com/my-first-dotnet-app/
. You should see your react SPA, working with any other backend controller endpoints you may have programmed there!
As a review, our five steps were:
- Extend
mysite.com
's NGINX configuration file to include a reverse proxy to thelocalhost
port of our choosing (1234
) - Override default Kestrel port to the port of our choosing (
1234
) - Remove HTTPS redirection from the .NET app
- Add correct homepage path,
mysite.com/my-first-dotnet-app/
topackage.json
of React SPA for proper SPA asset locating - Create and run a Kestrel service file for the .NET app
Note that with this setup, you can leave all fetch
calls in your React SPA relative as well. (i.e. NOT including the base URL). No need for environment based URL swaps etc.!
Questions, Comments, Something Didn't Work?
Let me know in the comments!
Cheers! 🍺
-Chris
Top comments (2)
Hi Chris,
for proper working and some advanced scenarios you should add the following to your .Net app behind proxy:
Also if you have HSTS and/or CORS settings applied you should remove them as it will become responsibility of your front-end proxy server to handle these:
Hi Slavius, thanks for the additions here!