Nginx Proxy on Azure Windows Server
If you have registered with Microsoft's cloud service Azure, you will be provided with several services that can be used free of charge for 12 months according to purpose, but the most practical service, virtual machines, can be used separately for each OS on Linux and Windows.
Takanobu FuseAdministrator

19 min read

last year

Cloud / Server

The usage conditions are Standard B1s (1 vcpu, 1 GiB memory), disk: Premium SSD 64GB, dynamic IPv4 address. When creating a virtual machine from the free service page, the conditions that can be used in these free tiers are selected by default, but there are restrictions on OS selection. If you create it directly from the virtual machine page as a prerequisite for free, you will have more options for OS, etc., but the default disk size (32GB is allocated by default, you can change it to 64GB for free later) is Exceeding the free quota of 64GB (Windows 10, Windows Server 2019 or later, cannot be changed to a smaller disk size), static IP is assigned by default (can be changed dynamically at the time of creation), etc. You should be careful.

Creating a virtual machine is like putting pieces of a puzzle together: CPU, memory, OS, disk, IP address (dynamic or static).

You can choose the latest OS for Linux in the free frame, and it is practical for a wide range of uses, but if you choose Windows in the free frame, you can only choose the OS before Windows Server 2016. Even if it is free, I don't know what to use it for, but I thought that it would be possible to use it without any problems if it was just a proxy server load, so I decided to build a proxy server with Nginx. It's also a good opportunity to remember remote desktop operations, PowerShell mastery, and forgotten command prompt operations.

Azure Nginx Proxy

Building Process

  1. Creating a VM (Virtual Machine) (OS: Windows Server 2016)

  2. Connect to VM from Ubuntu terminal (Remmina)

  3. Install Nginx, Certbot, OpenSSL (PowerShell, command prompt operation)

  4. Registration to Task Scheduler

1. Creating a VM (Virtual Machine) (OS: Windows Server 2016)

After registering with Azure, select Windows Virtual Machine from the free services page. Enter the resource group, virtual machine name, Windows username and password, etc. Select Windows (Windows Server 2016 Datacenter) for the image (the image is optional). Size is default (B1s, 1CPU, 1GB memory). Open (permit) port RDP(3389) for remote desktop, port HTTP(80) and HTTPS(443) for web server. No OS license required. Click the Confirm and Create button to create the virtual machine. After creation, check the overview of the virtual machine (link destination button will be displayed).

Registering any DNS name from the overview page of the virtual machine saves you the trouble of directly entering the IP address.

Screenshot From 2022 12 14 16 53 53

2. Connect to VM from Ubuntu terminal (Remmina)

The recommended way to connect to the created Windows Server virtual machine is via Remote Desktop (RDP), so use Remmina, the standard Ubuntu remote desktop app.

Note) Before connecting, please specify the keyboard layout (Japanese) to be used at the connection destination in the settings on the Remmina side.

Screenshot From 2022 12 05 16 11 20

First, download the RDP file containing the connection destination information (IP address or DNS name) to be imported by Remmina from the connection page of the virtual machine.

Screenshot From 2022 11 30 16 13 54

Next, import the RDP file downloaded above with Remmina.

After importing, the connection destination list will be displayed. Double-click here to display the command prompt screen of the virtual machine Windows Server.

Screenshot From 2022 11 30 16 15 48

Below is the command prompt screen on the virtual machine.

Screenshot From 2022 11 30 16 16 34

Start PowerShell by typing powershell in the command prompt.

Screenshot From 2022 11 30 17 50 45

WIndows Server 2016 PowerShell Reference

Windows Command Reference

3. Install Nginx, Certbot and OpenSSL (PowerShell, command prompt operation)

3-1. Firewall rule setting by PowerShell

Due to the firewall of the OS, access to the web server from the outside is denied by default. Therefore, use New-NetFirewallRule to allow access to port number 80 and 443.

PS > New-NetFirewallRule -DisplayName 'HTTP-Inbound' -Direction Inbound -Action Allow -Protocol TCP -LocalPort @('80', '443')

Name                  : {xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}
DisplayName           : HTTP-Inbound
Description           :
DisplayGroup          :
Group                 :
Enabled               : True
Profile               : Any
Platform              : {}
Direction             : Inbound
Action                : Allow
EdgeTraversalPolicy   : Block
LooseSourceMapping    : False
LocalOnlyMapping      : False
Owner                 :
PrimaryStatus         : OK
Status                : The rule was parsed successfully from the store. (65536)
EnforcementStatus     : NotApplicable
PolicyStoreSource     : PersistentStore
PolicyStoreSourceType : Local

Note) In case of excluding the above rule Remove-NetFirewallRule

PS > Remove-NetFirewallRule -DisplayName 'HTTP-Inbound'

3-2. Download and Install Nginx

Nginx for Windows


Download with Invoke-WebRequest. The download destination folder is arbitrary.

Deployment with Expand-Archive. Run the startup command "start nginx" in the expanded folder to start it (the configuration file is not set at this point).

PS > Invoke-WebRequest -Uri -Outfile ./
PS > Expand-Archive
PS > cd nginx/nginx-1.23.2
PS > start nginx

Confirmation of running process(tasklist)

PS > tasklist /fi "imagename eq nginx.exe"

Image Name           PID Session Name     Session#    Mem Usage
=============== ======== ============== ========== ============
nginx.exe            652 Console                 0      2 780 K
nginx.exe           1332 Console                 0      3 112 K

If you can confirm the running, stop it temporary.

PS > ./nginx.exe -s stop

Nginx command by command prompt

Replace "nginx" with "./nginx.exe" when running with PowerShell.

Commands Description
nginx -s stop fast shutdown
nginx -s quit graceful shutdown
nginx -s reload changing configuration, starting new worker processes with a new configuration, graceful shutdown of old worker processes
nginx -s reopen re-opening log files

3-3. Download and Install Certbot

For SSL/TLS authentication, Let's Encrypt authentication will be performed with Certbot.

Certbot Windows Install Instructions

Download to any directory with PowerShell

PS > invoke-webrequest -uri -outfile ./certbot-beta-installer-win_amd64.exe

Back to Windows console, then run the above installer

PS > exit

C:\ > certbot-beta-installer-win_amd64.exe

The following tasks are done using the command prompt, but it should be possible to replace these with PowerShell.

Help command for confirmation of running

C:\ > "program files/certbot/bin/certbot" --help
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  certbot [SUBCOMMAND] [options] [-d DOMAIN] [-d DOMAIN] ...

Certbot can obtain and install HTTPS/TLS/SSL certificates.  By default,
it will attempt to use a webserver both for obtaining and installing the
certificate. The most common SUBCOMMANDS and flags are:

obtain, install, and renew certificates:
    (default) run   Obtain & install a certificate in your current webserver
    certonly        Obtain or renew a certificate, but do not install it
    renew           Renew all previously obtained certificates that are near
    enhance         Add security enhancements to your existing configuration
   -d DOMAINS       Comma-separated list of domains to obtain a certificate for

Authentication procedure in standalone mode

Note) E-mail address and domain must be entered. It is necessary to open port: 80 to obtain authentication, so stop the web server before executing.

Authentication files are saved in the following directory.

C:\ > "program files/certbot/bin/certbot" certonly --standalone
Successfully received certificate.
Certificate is saved at: C:\Certbot\live\\fullchain.pem
Key is saved at:         C:\Certbot\live\\privkey.pem

Check the directory structure of nginx installed on Windows

C:\nginx\nginx-1.23.2 > dir
 Volume in drive C is Windows
 Volume Serial Number is 58AD-4A66

 Directory of C:\nginx\nginx-1.23.2

12/03/2022  05:07 AM    < DIR>          .
12/03/2022  05:07 AM    < DIR>          ..
12/03/2022  12:22 AM    < DIR>          conf
12/01/2022  08:15 AM    < DIR>          contrib
12/01/2022  08:15 AM    < DIR>          docs
12/03/2022  05:23 AM    < DIR>          html
12/03/2022  02:48 AM    < DIR>          logs
10/19/2022  01:38 PM         3,799,552 nginx.exe
12/01/2022  08:15 AM    < DIR>          temp
               2 File(s)      3,799,556 bytes
               8 Dir(s)  23,975,211,008 bytes free

Create conf.d folder (mkdir) to store new configuration files

C:\nginx\nginx-1.23.2 > dir conf
 Volume in drive C is Windows
 Volume Serial Number is 58AD-4A66

 Directory of C:\nginx\nginx-1.23.2\conf

12/03/2022  12:22 AM    < DIR>          .
12/03/2022  12:22 AM    < DIR>          ..
12/03/2022  02:34 AM    < DIR>          conf.d
10/19/2022  01:39 PM             1,103 fastcgi.conf
10/19/2022  01:39 PM             1,032 fastcgi_params
10/19/2022  01:39 PM             2,946 koi-utf
10/19/2022  01:39 PM             2,326 koi-win
10/19/2022  01:39 PM             5,448 mime.types
12/03/2022  02:15 PM               731 nginx.conf
10/19/2022  01:39 PM               653 scgi_params
10/19/2022  01:39 PM               681 uwsgi_params
10/19/2022  01:39 PM             3,736 win-utf
               9 File(s)         18,656 bytes
               3 Dir(s)  23,979,286,528 bytes free

Edit the parent configuration file to read the conf.d/*conf files.

C:\ > notepad conf/nginx.conf

pid        logs/;

events {
    worker_connections  1024;

http {
    include       mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    include conf.d/*.conf;


Create a new configuration file as a reverse proxy (such as specifying the authentication file created by Certbot)

C:\ > notepad conf/conf.d/default.conf

server {
    server_tokens off;
    # access_log  /var/log/nginx/access.log;
    # error_log   /var/log/nginx/error.log error;

    location / {
        proxy_pass https://xx.xx.xx.xx:8080;
        proxy_set_header 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-Proto $scheme;


    listen 443 ssl;
    ssl_certificate C:\Certbot\live\\fullchain.pem;
    ssl_certificate_key C:\Certbot\live\\privkey.pem;
    include C:\Certbot\options-ssl-nginx.conf;
    #ssl_dhparam C:\Certbot\ssl-dhparams.pem; 

server {
    if ($host = {
        return 301 https://$host$request_uri;


    listen 80;
    return 404; 


Create a new SSL configuration file specified in the nginx configuration file above

C:\ > notepad \Certbot\options-ssl-nginx.conf

# This file contains important security parameters. If you modify this file
# manually, Certbot will be unable to automatically provide future security
# updates. Instead, Certbot will print and log an error message with a path to
# the up-to-date file that you will need to refer to when manually updating
# this file.

ssl_session_cache shared:le_nginx_SSL:10m;
ssl_session_timeout 1440m;
ssl_session_tickets off;

ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;


3-4. Download and Install OpenSSL

Apply diffie-hellman key exchange encryption rules to Nginx for more secure SSL connections.

Create a pem file containing the parameters required for public key creation on the server and client sides with OpenSSL.

Binaries are distributed on several sites according to the OpenSSL Wiki.

This time, download the installer from the following site.

Win64 OpenSSL v3.0.7 Light

PS C:\> Invoke-WebRequest -Uri -Outfile ./Win64OpenSSL_Light-3_0_7.exe


PS C:\> .\Win64OpenSSL_Light-3_0_7.exe

Check binary openssl.exe on Windows console

C:\> dir "\program files\OpenSSl-Win64\bin"
 Volume in drive C is Windows
 Volume Serial Number is 58AD-4A66

 Directory of C:\program files\OpenSSl-Win64\bin

12/04/2022  02:53 AM    < DIR>          .
12/04/2022  02:53 AM    < DIR>          ..
11/01/2022  10:14 AM             8,299
11/01/2022  10:14 AM            71,168 capi.dll
11/01/2022  10:14 AM            46,592 dasync.dll
11/01/2022  10:14 AM           157,184 legacy.dll
11/01/2022  10:14 AM         5,140,992 libcrypto-3-x64.dll
11/01/2022  10:14 AM           776,192 libssl-3-x64.dll
11/01/2022  10:14 AM            83,456 loader_attic.dll
11/01/2022  10:14 AM           717,824 openssl.exe
11/01/2022  10:14 AM            49,152 ossltest.dll
11/01/2022  10:14 AM            58,368 padlock.dll
12/04/2022  02:53 AM    < DIR>          PEM
11/01/2022  10:14 AM             7,638
11/01/2022  10:14 AM            38,400 p_test.dll
11/01/2022  10:14 AM             6,946
              13 File(s)      7,162,211 bytes
               3 Dir(s)  24,056,950,784 bytes free

Create dhparam.pem

C:\ > "\program files\OpenSSl-Win64\bin\openssl" dhparam -out \Certbot\dhparam.pem 4096

Specify this file in the ssl section of the nginx configuration file (conf/conf.d/default.conf) (already described, uncommented the line: delete #).

Start nginx (execute in nginx.exe directory)

C:\ > start nginx

4. Registration to Task Scheduler

4-1. Certbot Renew Schedule

It seems that the Certbot update command (certbot renew) already has been registered in the task scheduler at the time of installation.

PS > Get-ScheduledTask

TaskPath                                       TaskName                          State
--------                                       --------                          -----
\                                              Certbot Renew Task                Ready
\Microsoft\Windows\.NET Framework\             .NET Framework NGEN v4.0.30319    Ready
\Microsoft\Windows\.NET Framework\             .NET Framework NGEN v4.0.30319 64 Ready


Check the execution schedule of Certbot (certification update command) (display 0 if there is no problem)

PS > Get-ScheduledTaskInfo -TaskName "Certbot Renew Task"

LastRunTime        : 12/19/2022 12:33:33 PM
LastTaskResult     : 0
NextRunTime        : 12/19/2022 11:16:16 PM
NumberOfMissedRuns : 0
TaskName           : Certbot Renew Task
TaskPath           :
PSComputerName     :

4-2. Auto Start Nginx

There are two ways to do this: using the task scheduler and using the dedicated app NSSM.

This time, use the task scheduler to run the Nginx startup batch file at system startup.

Create an Nginx startup batch file in the directory where nginx.exe is stored

REM Start Nginx
tasklist /FI "IMAGENAME eq nginx.exe" 2>NUL | find /I /N "nginx.exe">NUL
   REM Nginx is NOT running, so start it
   cd \nginx
   start nginx.exe
   ECHO Nginx started.
) else (
   ECHO Nginx is already running.

Task scheduler registration using the schtasks command

PS > schtasks /create /tn nginx-proxy /tr c:\nginx\start.bat /sc onstart /ru System

Check task schedule (display 0 if there is no problem)

PS C:\> Get-ScheduledTaskInfo -TaskName "nginx-proxy"

LastRunTime        : 12/7/2022 1:31:31 PM
LastTaskResult     : 0
NextRunTime        :
NumberOfMissedRuns : 0
TaskName           : nginx-proxy
TaskPath           :
PSComputerName     :

schtasks create

Additions and supplements to the contents of this blog will be followed in the following forum articles.