How do I set up my Django Website using Apache and WSGI

I've joined the unhappy ranks of people who have tried to set up a Django website served by Apache (on Amazon-linux EC2).

I have successfully configured Apache and compiled mod_wsgi against Python3.4 using a virtualenv (at /home/ec2-user/web-dev) largely following instructions at and the mod_wsgi docs).

However, I get an Internal Server Error when loading the Django test app.

Looking at the logs, it seems that multiple versions of Python are somehow being called during the processing (see the change from /home/ec2-user/web-dev/lib64/python3.4 to /usr/lib64/python3.7). Also it seems that mod_wsgi cannot be imported (whilst 'import mod_wsgi' works fine from a python prompt in the ec2 terminal).

[Thu Jan 19 15:19:27.329376 2023] [wsgi:error] [pid 9955] [remote] mod_wsgi (pid=9955): Failed to exec Python script file '/var/www/test/test_site/test_site/'.
[Thu Jan 19 15:19:27.329445 2023] [wsgi:error] [pid 9955] [remote] mod_wsgi (pid=9955): Exception occurred processing WSGI script '/var/www/test/test_site/test_site/'.
[Thu Jan 19 15:19:27.330049 2023] [wsgi:error] [pid 9955] [remote] Traceback (most recent call last):
[Thu Jan 19 15:19:27.330088 2023] [wsgi:error] [pid 9955] [remote]   File "/var/www/test/test_site/test_site/", line 25, in <module>
[Thu Jan 19 15:19:27.330093 2023] [wsgi:error] [pid 9955] [remote]     application = get_wsgi_application()
[Thu Jan 19 15:19:27.330108 2023] [wsgi:error] [pid 9955] [remote]   File "/home/ec2-user/web-dev/lib64/python3.4/site-packages/django/core/", line 12, in get_wsgi_application
[Thu Jan 19 15:19:27.330112 2023] [wsgi:error] [pid 9955] [remote]     django.setup(set_prefix=False)
[Thu Jan 19 15:19:27.330117 2023] [wsgi:error] [pid 9955] [remote]   File "/home/ec2-user/web-dev/lib64/python3.4/site-packages/django/", line 24, in setup
[Thu Jan 19 15:19:27.330121 2023] [wsgi:error] [pid 9955] [remote]     apps.populate(settings.INSTALLED_APPS)
[Thu Jan 19 15:19:27.330126 2023] [wsgi:error] [pid 9955] [remote]   File "/home/ec2-user/web-dev/lib64/python3.4/site-packages/django/apps/", line 89, in populate
[Thu Jan 19 15:19:27.330129 2023] [wsgi:error] [pid 9955] [remote]     app_config = AppConfig.create(entry)
[Thu Jan 19 15:19:27.330134 2023] [wsgi:error] [pid 9955] [remote]   File "/home/ec2-user/web-dev/lib64/python3.4/site-packages/django/apps/", line 123, in create
[Thu Jan 19 15:19:27.330137 2023] [wsgi:error] [pid 9955] [remote]     import_module(entry)
[Thu Jan 19 15:19:27.330142 2023] [wsgi:error] [pid 9955] [remote]   File "/usr/lib64/python3.7/importlib/", line 127, in import_module
[Thu Jan 19 15:19:27.330146 2023] [wsgi:error] [pid 9955] [remote]     return _bootstrap._gcd_import(name[level:], package, level)
[Thu Jan 19 15:19:27.330151 2023] [wsgi:error] [pid 9955] [remote]   File "<frozen importlib._bootstrap>", line 1006, in _gcd_import
[Thu Jan 19 15:19:27.330156 2023] [wsgi:error] [pid 9955] [remote]   File "<frozen importlib._bootstrap>", line 983, in _find_and_load
[Thu Jan 19 15:19:27.330161 2023] [wsgi:error] [pid 9955] [remote]   File "<frozen importlib._bootstrap>", line 962, in _find_and_load_unlocked
[Thu Jan 19 15:19:27.330176 2023] [wsgi:error] [pid 9955] [remote] ModuleNotFoundError: No module named 'mod_wsgi.server'; 'mod_wsgi' is not a package

To debug this, I wanted to see which version of python was being called. Overriding to remove the call to Django's get_wsgi_application prevents the error. The URL shows the results of the application function below:

# In test_site/test_site/
# ...

python_home = '/home/ec2-user/web-dev'

activator = python_home + '/bin/'
with open(activator) as f:
    exec(, {'__file__': activator})

import os
import sys

from django.core.wsgi import get_wsgi_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'test_site.settings')

#application = get_wsgi_application()

def application(environ, start_response):
    status = '200 OK'
    msg = f"sys.path: {sys.path}    |   sys.prefix: {sys.prefix}     |   sys.executable: {sys.executable}   |  Python version {sys.version}"
    output = str.encode(msg)
    response_headers = [('Content-type', 'text/plain'),
                        ('Content-Length', str(len(output)))]
    start_response(status, response_headers)

    return [output]

The function has a confusing output in the browser:

sys.path: ['/home/ec2-user/web-dev/lib64/python3.4/site-packages', '/home/ec2-user/web-dev/lib/python3.4/site-packages', '/var/www/test/test_site/test_site/', '/usr/lib64/', '/usr/lib64/python3.7', '/usr/lib64/python3.7/lib-dynload', '/var/www/test/test_site', '/var/www/test/test_site', '/var/www/test/test_site']    |   sys.prefix: /home/ec2-user/web-dev     |   sys.executable: /home/ec2-user/web-dev/bin/python   |  Python version 3.7.15 (default, Oct 31 2022, 22:44:31) 
[GCC 7.3.1 20180712 (Red Hat 7.3.1-15)]

There are more folders in the PYTHONPATH than I expected, and also Python 3.7 appears to be running instead of 3.4. Interestingly this shows that mod_wsgi is working.

For reference, my httpd.conf file is updated to include:

WSGIScriptAlias / /var/www/test/test_site/test_site/
WSGIPythonPath /var/www/test:/home/ec2-user/web-dev/lib/python3.4/site-packages:/home/ec2-user/web-dev/lib64/python3.4/site-packages

<Directory /var/www/test/test_site/test_site>
Order deny,allow
Allow from all

What can I do to get this Django app to load? This is only step one - before I try my own website, and adding a database too - it was only supposed to take a few hours. It has been a few days!

Back to Top