A long time ago I was building PHP websites, and more recently I learned some Python and really liked Flask as a minimalist framework for small projects. Until recently, I used hosting with cPanel, where setting up a Python application is mostly automated. On Loopia hosting, this is not the case. Their knowledge base covers setting up PHP websites, WordPress, Joomla, or their SiteBuilder, but not Python. I needed several hours of experimentation to get everything working. That's why I'm writing this guide—for others and for myself in the future.
Advantages of Loopia Hosting
Hosting with cPanel is generally easier and faster, but you don’t learn much about how things work in the background. I needed to implement Google authentication using the OAuth2 protocol. For that, I required the OpenSSL library, which on a cPanel system is tied to the cPanel version and cannot be upgraded. The available library version was very old, and my application couldn’t run. Loopia provides a more recent version, which allows setting up my application. The only supported mode is via a CGI server, meaning the application runs only for the duration of a request. If you need something to run periodically without a request, Loopia provides Cron. It doesn’t support standard Cron syntax, but you can schedule tasks to run at fixed intervals.
Setting Up a Python Application
Here I’ll describe what worked logically for me. Prerequisites:
- Familiarize yourself with the options Loopia provides, as I won’t go into too much detail.
- Set up an SSH account.
- Set up an FTP account.
- After purchasing a domain or pointing it to the "ns1.loopia.se" and "ns2.loopia.se" servers, propagation may take up to 48 hours, so be patient before proceeding.
-
Open a terminal and connect to your account via SSH. You’ll be in your $HOME folder, where you need
to create a folder for your application. My domain is "ujagaga.in.rs", so I created:
mkdir ujagaga.in.rs cd ujagaga.in.rs
-
To get a minimal Python Flask "Hello World" application, you can use my automatic setup script
https://github.com/ujagaga/cgi_falsk_deploy:
curl https://raw.githubusercontent.com/ujagaga/cgi_flask_deploy/refs/heads/main/cgi_flask_deploy.sh -o cgi_flask_deploy.sh chmod +x cgi_flask_deploy.sh ./cgi_flask_deploy.sh
Note: the application runs using the cgi-bin/cgi_serve.py script, so Flask's "url_for" will reflect that in URL generation. The easiest fix is to use strings, e.g., instead of:url_for('home')use "/".
If you want to explore the process in detail, read on. -
mkdir public_html cd public_html mkdir cgi-bin
Apache runs in the background and executes scripts placed in "cgi-bin" or its subfolders. -
Use FTP or SCP to upload your application files. I will assume your executable script is "index.py" and your application is in:
~/<my_domain.com>/public_html/cgi-bin
You also need to make your Python script executable:cd ~/<my_domain.com>/public_html/cgi-bin chmod +x index.py
-
Create a ".htaccess" file in "public_html" to give Apache extra instructions. The contents might look like:
SetEnv HOME # Look for cgi-bin/index.py first, then index.html DirectoryIndex cgi-bin/index.py index.html # Allow Python scripts to execute Options +ExecCGI AddHandler cgi-script .py
For testing, create an "index.py" file with:#!/usr/bin/env python3 print ("Content-type:text/html\r\n\r\n") print ('') print ('') print ('You should now see "Hello World from Python" on your domain. Optionally, add a "public_html/index.html" file for maintenance messages when the main script is missing. Make sure the top of your script contains "#!/usr/bin/env python3" so the server knows how to interpret it.Hello ') print ('') print ('') print ('Hello World from Python
') print ('') print ('')
Python Virtual Environment
A simple "Hello World" Python script works with built-in libraries, but a more complex application requires additional libraries that Apache cannot find unless its path is specified. Therefore, I created a virtual environment:
cd ~/<domain.in.rs> python3 -m venv venv source venv/bin/activate pip install flask ...Installing some libraries requires a Rust compiler, so it's good to install that in $HOME as well. Once the libraries are installed in the virtual environment, you need to adjust ".htaccess" to forward the path. First, get the absolute path:
echo $PWDThis gives something like "/www/webvol39/cm/ukz6s6j0jrlyx9o/<domain.in.rs>", referred to below as "venv_abs_path" for ".htaccess":
SetEnv HOME DirectoryIndex cgi-bin/index.py index.html Options +ExecCGI AddHandler cgi-script .py # Set paths to Python environment SetEnv PATH /<venv_abs_path>/venv/bin:$PATH SetEnv PYTHONPATH /<venv_abs_path>/venv/lib/python3.11/site-packagesNow the server should be able to locate the libraries for your Python application.
Handling URL Prefixes
My application is in a subfolder:
~/<domain.in.rs>/public_html/cgi-bin/<project_name>/index.pyTo ensure Apache points to the correct file, ".htaccess" must be adjusted:
SetEnv HOME
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ cgi-bin/<project_name>/index.py/$1 [QSA,L]
DirectoryIndex cgi-bin/gate_server/index.py index.html
Options +ExecCGI
AddHandler cgi-script .py
SetEnv PATH /<venv_abs_path>/venv/bin:$PATH
SetEnv PYTHONPATH /<venv_abs_path>/venv/lib/python3.11/site-packages
The next issue is that Flask must be served through the CGI server. It’s not built-in; it’s invoked from Flask.
You need to create a "cgi_serve.py" script in the same folder as "index.py":
#!/usr/bin/env python3 from wsgiref.handlers import CGIHandler from index import application CGIHandler().run(application)This assumes your Flask app in "index.py" uses
application = Flask(__name__). Then modify ".htaccess" to point to "cgi_serve.py" instead of "index.py":
SetEnv HOME
RewriteEngine On
RewriteBase /
# Redirect external requests to remove /cgi-bin/cgi_serve.py from the visible URL
RewriteCond %{THE_REQUEST} \s/cgi-bin/cgi_serve.py/([^\s?]*) [NC]
RewriteRule ^ cgi-bin/cgi_serve.py/%1 [L,R=301]
# Internally rewrite clean URLs to the actual script
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ cgi-bin/cgi_serve.py/$1 [QSA,L]
DirectoryIndex cgi-bin/gate_server/cgi_serve.py index.html
Options +ExecCGI
AddHandler cgi-script .py
SetEnv PATH /<venv_abs_path>/venv/bin:$PATH
SetEnv PYTHONPATH /<venv_abs_path>/venv/lib/python3.11/site-packages