Mr. Editor-in-chief Mr. Editor-in-chief 4 min read 682 words 405 views

Deploy Flask on Windows Server (Azure VM) 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.
shell
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
shell
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

shell
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

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

flaskblog.wsgi

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

flaskblog.conf

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

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

conf
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
conf
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
conf
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)
conf
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\
conf
<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

Copied to clipboard

Share this post

Related Posts