Mr. Editor-in-chief Mr. Editor-in-chief June 23, 2022 Updated April 24, 2026

Deploy Flask on Windows Server (Azure Virtual Machine) using Apache & Wsgi

1. Pre-Preparation

  • Open ports 80, 443, 5000 (Windows Logo > Windows Defender Firewall with Advanced Security > Inbound Rules > New Rules ...
  • Install Microsoft C++ Build Tools, Please refer to https://github.com/bycloudai/InstallVSBuildToolsWindows for detail. Windows 11 SDK is pretty large yet is still needed.
  • Install Microsoft Visual C++ Redistributable (Maybe not needed if the above step has already finished doing so)

2. Install & Test Run Apache

  • Dowload Apache from Apache 2.4 VS17 Windows Binaries and Modules, Unzip the file you will find a ReadMe.txt, on it will be the instructions about how to install it.
  • After copying files over to C:/Apache24, open a CMD terminal as Administrator.
cd C:\Apache24\bin
httpd.exe
# Test your installation open address http://localhost at local and open address http://[ip address] at remote. You can shut down Apache by pressing Ctrl+C (It may take a few seconds).
httpd.exe -k install
httpd.exe -k start
httpd.exe -k stop
httpd.exe -k restart
  • Restart the computer and test the server local and remote again. Make sure it's http://[ip address] not https://[ip_address].
    E: (OS 10048)Only one usage of each socket address (protocol/network address/port) is normally permitted. : AH00072: make_sock: could not bind to address [::]:80
    (OS 10048)Only one usage of each socket address (protocol/network address/port) is normally permitted. :
    AH00072: make_sock: could not bind to address 0.0.0.0:80
    AH00451: no listening sockets available, shutting down
    AH00015: Unable to open logs
netstat -aon | findstr :80
TCP    0.0.0.0:80             0.0.0.0:0              LISTENING       2660
TCP    [::]:80                [::]:0                 LISTENING       2660

taskkill /PID 2660 /F
SUCCESS: The process with PID 2660 has been terminated.

3. Install Python, MySQL, etc

python -m venv venv
venv\Scripts\activate
pip install -r requirements.txt
# Test flask test server local and remote on port 5000
pip install mod_wsgi
mod_wsgi-express module-config
LoadFile "C:/Users/Developer/AppData/Local/Programs/Python/Python312/python312.dll"
LoadModule wsgi_module "C:/flask_blog/venv/Lib/site-packages/mod_wsgi/server/mod_wsgi.cp312-win_amd64.pyd"
WSGIPythonHome "C:/flask_blog/venv"

4. Copy the flask application into server and here is the project structure

C:\flask_blog\
    +-- flaskblog
    |   +-- __init__.py          
    +-- deploy
    |   +-- flaskblog.wsgi
    +-- logs
    +-- venv
    +-- requirements.txt
    +-- run.py
    +-- flaskblog.conf

flaskblog.wsgi

import sys
sys.path.insert(0, 'C:\\flask_blog')
from flaskblog import create_app 
application = create_app()

flaskblog.conf

<VirtualHost *:80>

ServerName 104.208.86.219

WSGIScriptAlias / C:/flask_blog/deploy/flaskblog.wsgi

<Directory C:/flask_blog>

Require all granted

</Directory>

Alias /static C:/flask_blog/flaskblog/static

ErrorLog C:/flask_blog/logs/error.log

CustomLog C:/flask_blog/logs/access.log combined

</VirtualHost>

5. Configure Apache httpd.conf

......
LoadFile "C:/Users/Developer/AppData/Local/Programs/Python/Python312/python312.dll"
LoadModule wsgi_module "C:/flask_blog/venv/Lib/site-packages/mod_wsgi/server/mod_wsgi.cp312-win_amd64.pyd"
WSGIPythonHome "C:/flask_blog/venv"
Include C:/flask_blog/flaskblog.conf

6. Restart Apache server and test open the webpage on a remote browser

E: In the error.log: ModuleNotFoundError: No module named '_socket'\r
S: Add WSGIPythonPath in httpd.conf file under WSGIPythonHome

WSGIPythonHome "C:/Users/12506/AppData/Local/Programs/Python/Python311"
WSGIPythonPath "C:/Users/12506/AppData/Local/Programs/Python/Python311\DLLs" 

E: httpd.exe: Syntax error on line 538 of C:/Apache24/conf/httpd.conf: Cannot load C:/flask_blog/venv/Lib/site-packages/mod_wsgi/server/mod_wsgi.cp312-win_amd64.pyd into server: The specified module could not be found.
S: Get rid of virtual environment

7. Enable HTTPS after adding a domain name

  • Get a free SSL certificate from ZeroSSL
Troubleshooting - DNS (CNAME) Verification  
Please check if you have added the correct Name record:  
For example, your Name record in ZeroSSL is _D5856C5EEC87E5505EF3F53E95B414EB.domain.com, please enter only_D5856C5EEC87E5505EF3F53E95B414EB without the domain part as most DNS providers filter this part out automatically. 
  • Download the certificate files and paste it into C:\Apache24\conf directory
  • Edit C:\Apache24\conf\httpd.conf by un-commenting these 3 lines
LoadModule socache_shmcb_module modules/mod_socache_shmcb.so
LoadModule ssl_module modules/mod_ssl.so
Include conf/extra/httpd-ssl.conf
  • Comment out the original "Include" (We will stop using flaskblog.conf config setups from now on)
LoadFile "C:/Users/Developer/AppData/Local/Programs/Python/Python312/python312.dll"
LoadModule wsgi_module "C:/Users/Developer/AppData/Local/Programs/Python/Python312/Lib/site-packages/mod_wsgi/server/mod_wsgi.cp312-win_amd64.pyd"
WSGIPythonHome "C:/Users/Developer/AppData/Local/Programs/Python/Python312"
# Include C:/flask_blog/flaskblog.conf
  • Edit C:\Apache24\conf\extra\httpd-ssl.con\
<VirtualHost *:80>
    ServerName www.peng.codes
    Redirect / https://www.peng.codes/
</VirtualHost>

<VirtualHost _default_:443>
    ServerName www.peng.codes:443
    WSGIScriptAlias / C:/flask_blog/deploy/flaskblog.wsgi

    <Directory C:/flask_blog>
    Require all granted
    </Directory>

    Alias /static C:/flask_blog/flaskblog/static

    ErrorLog C:/flask_blog/logs/error.log
    CustomLog C:/flask_blog/logs/access.log combined

    SSLEngine on
    SSLCertificateFile "${SRVROOT}/conf/certificate.crt"
    SSLCertificateKeyFile "${SRVROOT}/conf/private.key"
    SSLCertificateChainFile "${SRVROOT}/conf/ca_bundle.crt"

    #  Since I don't know what below lines mean, I will keep them for safety.
    <FilesMatch "\.(cgi|shtml|phtml|php)$">
    SSLOptions +StdEnvVars
    </FilesMatch>
    <Directory "${SRVROOT}/cgi-bin">
    SSLOptions +StdEnvVars
    </Directory>
    BrowserMatch "MSIE [2-5]" \
    nokeepalive ssl-unclean-shutdown \
    downgrade-1.0 force-response-1.0
    CustomLog "${SRVROOT}/logs/ssl_request.log" \
    "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
</VirtualHost>
  • Restart the apache service and it should work instantly.

8. References

  • https://dev.to/willmvs/flask-deployment-on-windows-139b