Troubleshooting
Gunicorn & app servers

Fix "Target WSGI Script Cannot Be Loaded as Python Module" in Apache

Fix the Apache mod_wsgi error Target WSGI script cannot be loaded as Python module for Django: match mod_wsgi to your Python version, set python-home and python-path, fix permissions on the project and virtualenv, and read the real exception below it.

Problem statement

The site shows Apache's 500 Internal Server Error page, and the Apache error log contains:

[wsgi:error] mod_wsgi (pid=1234): Failed to exec Python script file '/srv/app/config/wsgi.py'.
[wsgi:error] mod_wsgi (pid=1234): Exception occurred processing WSGI script '/srv/app/config/wsgi.py'.
[wsgi:error] Target WSGI script '/srv/app/config/wsgi.py' cannot be loaded as Python module.
[wsgi:error] Traceback (most recent call last):
[wsgi:error]   ...
[wsgi:error] ModuleNotFoundError: No module named 'django'

mod_wsgi tried to import your wsgi.py and an exception was raised. The "cannot be loaded" line is a wrapper; the real cause is the traceback that follows it. Most often mod_wsgi runs a different Python than your virtualenv, cannot find your project on sys.path, or cannot read the files.

Why mod_wsgi failsApache startsmod_wsgi daemonImports wsgi.pywith its PythonExceptionpath, version, permsCannot be loaded500 errorThe traceback under this line names the real problem.
  1. Read the last line of the traceback in the Apache error log; that is the actual error.
  2. No module named 'django': set python-home to your virtualenv in WSGIDaemonProcess.
  3. No module named 'config': set python-path to the project root.
  4. Version mismatch: mod_wsgi must be compiled for the same Python as the virtualenv; install it with pip install mod_wsgi into the venv.
  5. Permission denied: the Apache user needs execute permission on every parent directory and read on the files.

Step-by-step solution

1. Read the full traceback

sudo tail -n 60 /var/log/apache2/error.log      # Debian/Ubuntu
sudo tail -n 60 /var/log/httpd/error_log        # RHEL family

Note the final exception. The fixes below map to the common ones.

2. Point mod_wsgi at the virtualenv and project

Use daemon mode with explicit paths:

<VirtualHost *:443>
    ServerName example.com
    WSGIDaemonProcess example python-home=/srv/app/.venv python-path=/srv/app \
        user=deploy group=www-data processes=2 threads=15
    WSGIProcessGroup example
    WSGIApplicationGroup %{GLOBAL}
    WSGIScriptAlias / /srv/app/config/wsgi.py

    <Directory /srv/app/config>
        <Files wsgi.py>
            Require all granted
        </Files>
    </Directory>
</VirtualHost>

python-home is the virtualenv root (the directory containing bin/ and lib/), not bin/python. python-path adds the project root so import config works. WSGIApplicationGroup %{GLOBAL} avoids problems with C extensions such as psycopg and NumPy that do not work in sub-interpreters.

last line of the tracebackExceptionCause and fixNo module named 'django'system Python used; setpython-home to venvNo module named 'config'project not on path; set python-pathImportError, undefined symbolmod_wsgi built forother Python; reinstallPermissionErrorApache user cannottraverse; fix dir permsThe wrapper message is the same; the exception below it differsdjango-deployment.com
Fix one exception at a time and reload Apache between changes.

3. Match mod_wsgi and Python versions

The distribution package libapache2-mod-wsgi-py3 is compiled against the system Python, for example 3.12 on Ubuntu 24.04. A virtualenv made with Python 3.13 will not work with it. Check:

apache2ctl -M | grep wsgi
ls /srv/app/.venv/lib/

To use a different Python, install mod_wsgi into the virtualenv and load that module instead of the system one:

/srv/app/.venv/bin/pip install mod_wsgi
/srv/app/.venv/bin/mod_wsgi-express module-config

Put the printed LoadModule and WSGIPythonHome lines in an Apache config file and disable the distribution module (sudo a2dismod wsgi).

4. Fix permissions

Apache's daemon process runs as the user= you set or as www-data. It needs x on every directory from / down to the project, and r on the files:

namei -l /srv/app/config/wsgi.py
sudo chmod o+x /srv /srv/app

Projects under /home/youruser/ often fail because home directories are 0750. Moving the project to /srv avoids it.

5. Check wsgi.py itself

Errors in settings (missing environment variables, syntax errors) surface here too. Test the import as the Apache user with the same environment:

sudo -u deploy /srv/app/.venv/bin/python -c "import sys; sys.path.insert(0, '/srv/app'); import config.wsgi"

Environment variables from your shell are not visible to Apache, and SetEnv values do not reach os.environ under mod_wsgi either. Load them from a file instead, for example with django-environ reading /srv/app/.env at the top of your settings.

daemon mode settingsWSGIDaemonProcess examplepython-home=/srv/app/.venvpython-path=/srv/appuser=deploy group=www-dataWSGIApplicationGroup%{GLOBAL}1231Virtualenv root, not bin/python.2Project root so config.wsgi imports.3Main interpreter; needed by many C extensions.Daemon mode with explicit paths isolates each Django sitedjango-deployment.com
Embedded mode without these settings is the most common source of the error.

Explanation

mod_wsgi embeds a Python interpreter inside Apache. Which Python, and which version, is fixed when mod_wsgi is compiled. In daemon mode, it starts separate processes that import your wsgi.py using that interpreter, with the virtualenv and paths you configure. Any exception during import is logged with the "cannot be loaded as Python module" wrapper, followed by the original traceback. Because the interpreter and environment are Apache's, not your shell's, imports that work in a terminal can fail there.

Edge cases / notes

  • SELinux. On RHEL and Rocky Linux, files need the httpd_sys_content_t context and the venv may need httpd_sys_script_exec_t; check ausearch -m avc.
  • Multiple sites. Use a separate WSGIDaemonProcess name per site so their Python environments never mix.
  • Reloads. Touching wsgi.py reloads a daemon-mode application without restarting Apache.

Next steps

FAQ

What does "cannot be loaded as Python module" mean?

mod_wsgi tried to import your WSGI file and an exception occurred. The traceback below it shows the real error.

Why does it work with runserver but not Apache?

Apache uses its own Python interpreter, user, and environment, which can differ from your shell's virtualenv and variables.

Should python-home point to bin/python?

No. It should point to the virtualenv directory itself, such as /srv/app/.venv.

Can I use a newer Python than the system one?

Yes, by installing mod_wsgi with pip into a virtualenv of that Python and loading that module in Apache.

Keep going