There are few things more frustrating in backend development than deploying code that works perfectly on localhost, only to be greeted by a generic "Incomplete response received from application" error in production.
If you are hosting a Python application on A2 Hosting (or any cPanel environment using Phusion Passenger), this error is rarely a bug in your application code. It is almost always a mismatch between how your framework (Django/Flask) expects to start and how the Passenger application server attempts to launch it.
This guide provides the definitive technical solution for configuring passenger_wsgi.py, handling the WSGI interface correctly, and eliminating 404/500 errors on shared Python hosting environments.
The Root Cause: How Passenger Interacts with Python
To fix the error, you must understand the architecture. When you run python manage.py runserver or flask run locally, you are utilizing a lightweight, single-threaded development server.
In production on A2 Hosting, Phusion Passenger acts as the bridge between the Apache/Nginx web server and your Python code. Passenger does not know about your management commands. Instead, it looks for a specific file named passenger_wsgi.py in your application root.
Within this file, Passenger strictly looks for a callable object named application (specifically following the WSGI standard defined in PEP 3333).
The "Incomplete response" error occurs when:
- Entry Point Mismatch: Passenger cannot find the
passenger_wsgi.pyfile. - Namespace Error: The file exists, but does not expose an object named
application. - Path Resolution Failure: Python cannot locate your project modules because they aren't in
sys.path. - Process Blocking: You invoked
app.run(), which launches a blocking server instance instead of exposing a WSGI object.
Prerequisite: The Virtual Environment
Before modifying the WSGI entry point, ensure your virtual environment is correctly mapped in the A2 "Setup Python App" interface.
- Navigate to cPanel > Setup Python App.
- Ensure the Application Root matches your uploaded file path.
- Copy the Command for entering virtual environment. It usually looks like
source /home/username/virtualenv/app_name/3.X/bin/activate. - Install your dependencies (Flask, Django, Gunicorn, etc.) inside this environment via SSH.
If your dependencies are missing, passenger_wsgi.py will fail silently before it can output an error to the browser.
Solution 1: Configuring Flask
Flask wraps the WSGI application object in the Flask class instance. A common mistake is attempting to call app.run() inside the production configuration.
Assume your file structure looks like this:
/home/username/public_html/myapp/
├── app.py # Your main Flask application
├── passenger_wsgi.py # The entry point
├── requirements.txt
└── venv/ # Managed by A2
The Correct passenger_wsgi.py for Flask
Create or edit passenger_wsgi.py with the following code. This script explicitly imports your Flask instance and renames it to application so Passenger can hook into it.
import sys
import os
# 1. INTERPRETER PATH
# Explicitly tell the system where the current directory is.
# This prevents import errors if Passenger starts the process
# from a different relative path.
sys.path.append(os.getcwd())
# 2. IMPORT THE FLASK INSTANCE
# Assuming your main file is named 'app.py' and
# the Flask object is initialized as 'app = Flask(__name__)'
from app import app as application
# 3. VERIFICATION (Optional but recommended)
# This block only runs if you execute this script directly,
# serving as a sanity check.
if __name__ == "__main__":
# strictly for local testing, never executed by Passenger
application.run()
Why This Works
We use sys.path.append(os.getcwd()) to ensure the Python interpreter perceives the current directory as a package source. When we do from app import app as application, we map the Flask instance to the variable application. Passenger automatically detects this variable and begins passing HTTP requests to it.
Solution 2: Configuring Django
Django is slightly more complex because it separates the WSGI configuration into its own file (wsgi.py) nested within the configuration folder.
Assume your file structure looks like this:
/home/username/public_html/myproject/
├── manage.py
├── myproject/ # Configuration module
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── passenger_wsgi.py # The entry point
└── requirements.txt
The Correct passenger_wsgi.py for Django
You must direct Passenger to load Django's default WSGI handler.
import sys
import os
# 1. SYSTEM PATH CONFIGURATION
# Add the project directory to sys.path so Python can find 'myproject'
# 'os.getcwd()' usually resolves to /home/username/public_html/myproject
sys.path.append(os.getcwd())
# 2. ENVIRONMENT SETTINGS
# Point to your Django project's settings module.
# Replace 'myproject.settings' with your actual folder name.
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings")
# 3. IMPORT WSGI HANDLER
# Django's built-in get_wsgi_application initializes the framework.
from django.core.wsgi import get_wsgi_application
# 4. ASSIGN TO 'application'
# Passenger looks for this specific variable name.
application = get_wsgi_application()
Addressing Static Files
Unlike runserver, Passenger will not serve static files (CSS/JS) automatically. You will likely see a styled 404 page or a raw HTML page without layout.
To fix this efficiently in a shared environment without setting up complex Nginx aliases, use Whitenoise.
pip install whitenoise- Add it to your
middlewareinsettings.py:
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
"whitenoise.middleware.WhiteNoiseMiddleware", # Add this immediately after SecurityMiddleware
'django.contrib.sessions.middleware.SessionMiddleware',
# ... rest of middleware
]
Troubleshooting and Reloading
Changes to Python code in Phusion Passenger environments are not picked up automatically. The application server caches the compiled bytecode.
Forcing a Restart
To force A2 Hosting to reload your changes, you must touch a specific file. Run this command in your terminal after every deployment or code change:
touch tmp/restart.txt
If the tmp directory does not exist, create it:
mkdir -p tmp
touch tmp/restart.txt
viewing the Error Logs
If you still see "Incomplete response," standard HTML errors won't tell you enough. You must view the actual stderr stream.
On A2/cPanel, the error log is typically located at: /home/username/virtualenv/app_name/3.X/var/log/passenger.log
Or, simply check the Apache error log via the console:
cat error_log
# or
tail -f error_log
Summary
The "Incomplete response" error is a structural issue, not a coding bug. By adhering to the following checklist, you ensure stability:
- Use the
sys.path.append(os.getcwd())trick to fix import resolution. - Expose the WSGI object specifically as
application. - Never use
app.run()in the global scope ofpassenger_wsgi.py. - Always
touch tmp/restart.txtto apply changes.
Configuring the WSGI entry point correctly turns a fragile deployment into a robust production environment, capable of handling traffic without the erratic behavior associated with development servers.