When developing web apps and api’s with ASP.NET Core, it is useful to replicate the kind of setup used to deploy your application to production. While the built-in Kestrel web server is adequate for local development, you need a full-fledged web server, such as IIS, Apache or Nginx, to perform functions such as load balancing and SSL termination. Therefore, it is worthwhile to configure an ASP.NET Core project to run locally using Nginx as a reverse proxy for secure communication over HTTPS. Of course, the best way to do this is by running both the web app and reverse proxy in Docker containers.
You can download the code for this blog post here: https://github.com/tonysneed/Demo.AspNetCore-Nginx-Ssl
Dockerize Web API
Then open the project folder in a code editor of your choice. My favorite is Visual Studio Code, which allows you to easily open a project folder from the command line:
Open Startup.cs and edit the
Configure method to remove
app.UseHttpsRedirection() and add support for using forwarded headers.
Next, containerize the Web API project by adding a file named Api.Dockerfile.
VS Code has a nice Docker extension that lets you add various kinds of Dockerfiles, including for ASP.NET Core. I modified the default format to use the Alpine Linux distro, which is small and lightweight, and to add an
ASPNETCORE_URLS environment variable for serving the Web API on port 5000. Run the following commands to build and run the Docker image.
Use Google Chrome to browse to
http://localhost:5000/weatherforecast, and you’ll see some pretty JSON. You can then remove both the container and image.
Dockerize Nginx Server
Next add an Nginx folder to the solution folder, and place a file there named Nginx.Dockerfile.
You’ll need to create a nginx.conf file that will be copied to the container.
proxy_pass specifies a host name of
web-api, which matches the
upstream directive with a
server value of
api:5000, which will be defined later as a service in a docker-compose file.
If you run both the Nginx and Web API containers at the name time, the reverse proxy will return a 502 Bad Gateway, because it will not see the Web API server. Both containers need to be placed in the same network. This can be accomplished using Docker networking directives, or you can simply use docker-compose, which is what we’ll do here. Add a docker-compose.yml file to the solution folder.
The build directives are there to facilitate building each docker image, which you can perform using the following command:
To run both containers in a default bridge network, run the following command:
docker-compose up -d.
View the running containers with
docker ps. Notice that the Web API is not exposed to the host, but the reverse proxy is. Browse to:
http://localhost/weatherforecast. To stop the containers run:
Enable SSL Termination
One of the benefits of using Nginx as a reverse proxy is that you can configure it to use SSL for secure communication with clients, with requests forwarded to the web app over plain HTTP. The first step in this process is to create a public / private key pair for localhost. We can accomplish this task using OpenSSL, which can be installed on both macOS and Windows. Start by adding a localhost.conf file to the Nginx folder.
Run the following command to create localhost.crt and localhost.key files, inserting your own strong password.
In order to trust the localhost certificate on your local machine, you’ll want to run the following command to create a localhost.pfx file, providing the same strong password when prompted.
To trust the localhost certificate on macOS, open Keychain Access, select System in the Keychains pane, and drag localhost.pfx from the Finder into the certificate list pane. Then double-click the localhost certificate and under the trust section select Always Trust.
To create and trust a self-signed certificate on Windows, follow these instructions.
Now that you have created a public / private key pair, you need to update Nginx.Dockerfile to copy these files to the container.
Next, update nginx.conf to load the certificate key pair. Configure a server to listen on port 443 over ssl and forward requests to the upstream web-api server. Also configure a server to listen on port 80 and redirect requests to port 443.
Lastly, edit the docker-compose.yml file to expose both ports 80 and 443.