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