How to install and configure Python with Apache

How to install and configure Python with Apache

How to install and configure Python with Apache

Python and Apache don’t naturally speak the same language out of the box, and I remember being confused early on about why my Flask app wouldn’t just “run” when I dropped it into the DocumentRoot. The missing piece is WSGI — the interface that lets Apache hand off requests to Python code. In this guide, I’ll walk through installing and configuring Python with Apache using mod_wsgi, the most common and reliable approach.

Understanding How Python Works with Apache

Python web frameworks like Flask and Django follow the WSGI (Web Server Gateway Interface) specification, a standard that defines how a web server communicates with a Python application. Apache doesn’t understand WSGI natively, so it needs a bridge: mod_wsgi.

mod_wsgi embeds a Python interpreter inside Apache (or runs it in separate daemon processes) and handles translating HTTP requests into calls your Python application understands, then converts the application’s response back into an HTTP response.

There are two operating modes:

Prerequisites

Step 1: Install Python and Development Tools

Debian/Ubuntu:

sudo apt update
sudo apt install python3 python3-pip python3-venv python3-dev apache2 apache2-dev libapache2-mod-wsgi-py3

CentOS/RHEL:

sudo dnf install python3 python3-pip python3-devel httpd httpd-devel
sudo dnf install python3-mod_wsgi

Step 2: Enable mod_wsgi

Debian/Ubuntu:

sudo a2enmod wsgi
sudo systemctl restart apache2

Verify it loaded:

apache2ctl -M | grep wsgi

CentOS/RHEL:

Ensure this line exists in your Apache configuration:

LoadModule wsgi_module modules/mod_wsgi.so

Then restart:

sudo systemctl restart httpd

Step 3: Set Up a Python Virtual Environment

Isolating dependencies in a virtual environment is essential for production stability:

sudo mkdir -p /var/www/myflaskapp
cd /var/www/myflaskapp
python3 -m venv venv
source venv/bin/activate
pip install flask

Step 4: Create a Simple Flask Application

nano /var/www/myflaskapp/app.py
from flask import Flask

app = Flask(__name__)

@app.route("/")
def home():
    return "Hello from Flask running on Apache with mod_wsgi!"

if __name__ == "__main__":
    app.run()

Step 5: Create the WSGI Entry Point

nano /var/www/myflaskapp/myflaskapp.wsgi
#!/usr/bin/python3
import sys
import logging

logging.basicConfig(stream=sys.stderr)

sys.path.insert(0, "/var/www/myflaskapp/")

from app import app as application

The variable must be named application — this is the convention mod_wsgi looks for.

Step 6: Configure the Apache Virtual Host

<VirtualHost *:80>
    ServerName myflaskapp.com

    WSGIDaemonProcess myflaskapp python-home=/var/www/myflaskapp/venv python-path=/var/www/myflaskapp
    WSGIProcessGroup myflaskapp
    WSGIScriptAlias / /var/www/myflaskapp/myflaskapp.wsgi

    <Directory /var/www/myflaskapp>
        Require all granted
    </Directory>

    ErrorLog ${APACHE_LOG_DIR}/myflaskapp_error.log
    CustomLog ${APACHE_LOG_DIR}/myflaskapp_access.log combined
</VirtualHost>

WSGIDaemonProcess with python-home pointing to the virtual environment ensures Apache uses your isolated Python packages rather than the system-wide installation.

Enable the site and restart:

sudo a2ensite myflaskapp.conf
sudo apachectl configtest
sudo systemctl restart apache2

Visit http://myflaskapp.com — you should see the Flask response.

Configuring Django with Apache

Django follows a similar pattern but with a slightly different WSGI entry point structure, since Django generates wsgi.py automatically.

Step 1: Set Up the Django Project

cd /var/www
python3 -m venv django_env
source django_env/bin/activate
pip install django
django-admin startproject myproject

Step 2: Configure the Apache Virtual Host

<VirtualHost *:80>
    ServerName mydjangoapp.com

    WSGIDaemonProcess myproject python-home=/var/www/django_env python-path=/var/www/myproject
    WSGIProcessGroup myproject
    WSGIScriptAlias / /var/www/myproject/myproject/wsgi.py

    <Directory /var/www/myproject/myproject>
        <Files wsgi.py>
            Require all granted
        </Files>
    </Directory>

    Alias /static/ /var/www/myproject/static/
    <Directory /var/www/myproject/static>
        Require all granted
    </Directory>

    ErrorLog ${APACHE_LOG_DIR}/mydjangoapp_error.log
    CustomLog ${APACHE_LOG_DIR}/mydjangoapp_access.log combined
</VirtualHost>

Don’t forget to run collectstatic before going live:

python manage.py collectstatic

Real-World Use Cases

Common Mistakes to Avoid

  1. Forgetting python-home in WSGIDaemonProcess – Without it, Apache uses the system Python, missing your virtual environment’s packages, leading to ModuleNotFoundError.
  2. Naming the WSGI callable incorrectly – It must be named application, not app, in the .wsgi file (even though your Flask variable can be named app inside app.py, the imported alias in the wsgi file must be application).
  3. Running the dev server in production – app.run() or Django’s runserver are not production-grade; always use mod_wsgi (or Gunicorn behind a proxy) instead.
  4. Mixing Python 2 and Python 3 mod_wsgi packages – Ensure you install libapache2-mod-wsgi-py3 specifically, not the Python 2 version.
  5. Incorrect file permissions – The Apache user needs read access to your application directory and virtual environment.

Security Best Practices

Performance Optimization Tips

WSGIDaemonProcess myflaskapp python-home=/var/www/myflaskapp/venv processes=4 threads=2

Troubleshooting

500 Internal Server Error with no clear message:

ModuleNotFoundError for installed packages:

Static files return 404 in Django:

Changes to code not reflecting:

touch /var/www/myflaskapp/myflaskapp.wsgi

Frequently Asked Questions

Do I need mod_wsgi, or can I use Gunicorn instead? Both work. mod_wsgi embeds Python directly into Apache’s process management. Gunicorn runs as a separate process, with Apache acting as a reverse proxy (similar to the Rails/Puma pattern). Gunicorn behind a proxy is increasingly common for containerized deployments; mod_wsgi remains popular for traditional VPS setups already standardized on Apache.

Can I run multiple Python apps on the same Apache server? Yes, using separate WSGIDaemonProcess groups and virtual hosts, each with its own virtual environment and process isolation.

Why does my app work with python app.py but not through Apache? The built-in Flask/Django development server behaves differently from mod_wsgi’s process model — typically the issue is either the python-home path, file permissions, or a missing environment variable that was set locally but not in Apache’s environment.

Is mod_wsgi actively maintained? Yes, mod_wsgi continues to be maintained and is a stable, production-proven choice for serving Python WSGI applications through Apache.

Summary and Key Takeaways

References

Exit mobile version