Luis Alvergue

Controls engineer by training, transportation engineer by trade

Tip on running Flask applications on Apache/2.4.46 (FreeBSD) using CGI

5 minutes
June 9, 2021

After receiving several Internal Server Error from Apache and making changes to httpd-custom.conf when trying to run a Flask app using CGI, I realized that setting environment variables in your .profile does not guarantee your .cgi or .py scripts in the cgi-bin directory will have access to them. Knowing the behavior of crontab and Python scripts, I should have figured this out earlier. For example, if script.py looks something like

import sys
sys.path.append('Your virtual env/site packages')

import os
import mysql.connector
from sqlalchemy import create_engine
import pandas as pd
from flask import Flask, render_template
from wsgiref.handlers import CGIHandler

app = Flask(__name__)

sqlpass = os.getenv('SQLCRD')
sqlusr = os.getenv('SQLUSR')
sqlsrv = os.getenv('SQLSRV')
sqldb = os.getenv('SQLDB')
engine = create_engine('mysql+mysqlconnector://' +
sqlusr + ':' + sqlpass + '@' + sqlsrv + '/' + sqldb, echo=False)

df = pd.read_sql('SOME SQL QUERY', engine)

@app.route("/hello")
def home():
    return df.to_json()

and you call it from cgi_script.cgi, where the cgi script follows Flask’s documentation

#!/pathtoyourpythonvirtualenv/python
from wsgiref.handlers import CGIHandler
from yourapplication import app

CGIHandler().run(app)

in the cgi-bin directory. Then, when you call it from

www.yoursitename.com/cgi-bin/cgi_script.cgi/hello

in your browser, it will not work. The reason is that scripts in cgi-bin run in a mostly empty environment, so SQLUSR and SQLCRD are not available, even though they have been defined in .profile. There are several ways to resolve this, I used the following approach. Add from wsgiref.handlers import CGIHandler to the top of script.py and

    if __name__ == '__main__':
        CGIHandler().run(app)

to the bottom of script.py. Then call script.py from a ‘wrapper’ CGI script (cgi_script.sh) as shown below

#!/usr/local/bin/bash
export SQLUSR=valueofvariable
export SQLCRD=valueofvariable
source ~/yourproject/env/bin/activate
cd ~/whereyoukeepthepythonscript
python script.py

by pointing your browser to

www.yoursitename.com/cgi-bin/cgi_script.cgi/hello

Alternatively, source your .profile inside of cgi_script.sh. Sourcing your .profile seems more secure but I’m not sure if it actually is.