Building Web Applications with Python Flask and Apache
Deploy production-ready Python Flask applications with Apache, mod_wsgi, and modern best practices.
Table of Contents
- Introduction
- What is Flask?
- Prerequisites
- Understanding the Architecture
- Python and Pip Installation
- Virtual Environments Best Practices
- Installing Flask
- Creating Your Flask Application
- Apache and mod_wsgi Configuration
- Production Deployment
- Environment Variables and Configuration
- Database Integration
- Security Best Practices
- Performance Optimization
- Logging and Monitoring
- Troubleshooting
- What’s Changed Since Python 2.7
Introduction
Flask is a lightweight, powerful Python web framework that makes it easy to build web applications. Combined with Apache and mod_wsgi, Flask provides a robust platform for production-ready web services, APIs, and full-stack web applications.
What you’ll learn:
- Modern Python 3 and Flask setup
- Virtual environment management
- Apache integration with mod_wsgi
- Production deployment strategies
- Security hardening
- Performance optimization
By the end of this guide, you’ll have:
- Flask application running on Apache
- Proper virtual environment isolation
- Production-ready configuration
- Security best practices implemented
- Understanding of common deployment patterns
What is Flask?
Flask is a micro web framework for Python that provides the essentials for web development without forcing specific tools or libraries.
Flask vs Other Frameworks
Flask (Micro-framework):
- Minimal core, extensible with plugins
- Flexibility in architecture choices
- Lightweight and fast
- Great for APIs and small to medium projects
Django (Full-stack):
- Batteries included (ORM, admin, auth)
- Opinionated structure
- Better for large, complex applications
- Built-in admin interface
FastAPI (Modern):
- Async/await support
- Automatic API documentation
- Type hints and validation
- Great performance for APIs
When to Use Flask
Flask is ideal for:
- RESTful APIs
- Microservices
- Small to medium web applications
- Projects requiring flexibility
- Learning web development
- Prototyping and MVPs
Consider alternatives for:
- Large enterprise applications (Django)
- Real-time applications (Node.js, FastAPI)
- Heavy async workloads (FastAPI)
Prerequisites
Required Setup
Before starting, ensure you have:
- LAMP Stack Installed (see my LAMP setup guide)
- Apache 2.4+
- MySQL 8.0+ or PostgreSQL
- SSL certificates configured
- Server Requirements:
- Ubuntu 22.04+ (or 20.04)
- 1GB+ RAM (2GB+ recommended)
- Root or sudo access
- Domain name (optional but recommended)
- Knowledge Prerequisites:
- Basic Python programming
- Understanding of web concepts (HTTP, REST)
- Basic Linux command line
- HTML/CSS basics (for templates)
Verify Existing Setup
# Check Apache is running
sudo systemctl status apache2
# Check Python is available
python3 --version
# Check if you have a domain configured
ls -l /etc/apache2/sites-available/
Understanding the Architecture
How Flask Works with Apache
[User Browser]
↓
[Apache Web Server] (Port 80/443)
↓
[mod_wsgi] (Python gateway)
↓
[Flask Application] (Python code)
↓
[Database] (MySQL/PostgreSQL)
Component roles:
- Apache: Handles HTTP requests, SSL, static files
- mod_wsgi: Bridges Apache and Python
- Flask: Application logic and routing
- Virtual Environment: Isolated Python dependencies
- WSGI file: Entry point for mod_wsgi
Request Flow
- User requests
https://example.com/api/users - Apache receives the HTTPS request
- Apache determines it’s a dynamic request (not static)
- mod_wsgi activates Python interpreter
- WSGI file loads Flask application
- Flask routes request to appropriate view function
- Flask processes request, queries database if needed
- Flask returns response (HTML, JSON, etc.)
- mod_wsgi passes response to Apache
- Apache sends response to user
Python and Pip Installation
Modern Ubuntu comes with Python 3 pre-installed, but we’ll ensure proper setup.
Verify Python Installation
# Check available Python versions
type python3 python
# Check Python 3 version (should be 3.10+ on Ubuntu 22.04)
python3 --version
Expected output:
Python 3.10.12
Install Python Development Tools
sudo apt update
sudo apt install -y \
python3 \
python3-pip \
python3-dev \
python3-venv \
build-essential \
libssl-dev \
libffi-dev \
libpq-dev \
git
Packages explained:
python3-pip- Python package installerpython3-dev- Header files for compiling Python extensionspython3-venv- Virtual environment supportbuild-essential- Compilers and build toolslibssl-dev- SSL library (for cryptography packages)libffi-dev- Foreign function interface librarylibpq-dev- PostgreSQL client library (if using PostgreSQL)
Install Apache mod_wsgi
# For Python 3
sudo apt install libapache2-mod-wsgi-py3 -y
# Enable mod_wsgi
sudo a2enmod wsgi
# Restart Apache
sudo systemctl restart apache2
Verify Installation
# Check pip version
pip3 --version
# Verify mod_wsgi is loaded
apache2ctl -M | grep wsgi
Should show:
wsgi_module (shared)
Virtual Environments Best Practices
Virtual environments isolate project dependencies, preventing conflicts and ensuring reproducibility.
Why Virtual Environments?
Without virtual environments:
System Python
├── Package A (v1.0) ← Project 1 needs this
├── Package A (v2.0) ← Project 2 needs this (CONFLICT!)
└── Package B (v3.0)
With virtual environments:
System Python
│
Project 1 (venv)
├── Package A (v1.0) ✓
└── Package B (v2.0)
│
Project 2 (venv)
├── Package A (v2.0) ✓
└── Package C (v1.0)
Creating a Virtual Environment
# Navigate to your project directory
cd /var/www/example.com
# Create a virtual environment
python3 -m venv venv
# Alternative name
python3 -m venv flask_env
Directory structure:
/var/www/example.com/
├── venv/
│ ├── bin/
│ │ ├── activate
│ │ ├── pip
│ │ └── python
│ ├── lib/
│ └── pyvenv.cfg
└── (your flask app files)
Activating and Deactivating
# Activate virtual environment
source venv/bin/activate
# Your prompt should change to show (venv)
(venv) user@server:/var/www/example.com$
# Now pip installs only affect this environment
pip install flask
# Check where packages are installed
pip show flask
# Should show: Location: /var/www/example.com/venv/lib/python3.10/site-packages
# Deactivate when done
deactivate
Virtual Environment Best Practices
DO:
- ✅ Create one venv per project
- ✅ Add
venv/to.gitignore - ✅ Use
requirements.txtfor dependencies - ✅ Activate venv before installing packages
- ✅ Set proper ownership for production
DON’T:
- ❌ Use
sudo pip install(installs globally) - ❌ Commit virtual environment to Git
- ❌ Share venvs between projects
- ❌ Install packages outside venv
Requirements Management
Create requirements.txt:
# Activate your venv first
source venv/bin/activate
# Install your packages
pip install flask flask-sqlalchemy flask-login
# Save installed packages
pip freeze > requirements.txt
Install from requirements.txt:
# On new server or fresh environment
source venv/bin/activate
pip install -r requirements.txt
Installing Flask
Basic Flask Installation
# Activate virtual environment
cd /var/www/example.com
source venv/bin/activate
# Install Flask
pip install Flask
# Verify installation
python -c "import flask; print(flask.__version__)"
Common Flask Extensions
# Database ORM
pip install Flask-SQLAlchemy
# User authentication
pip install Flask-Login
# Form handling
pip install Flask-WTF
# Email support
pip install Flask-Mail
# RESTful API tools
pip install Flask-RESTful
# CORS support (for APIs)
pip install Flask-CORS
# Environment variable management
pip install python-dotenv
# Database migrations
pip install Flask-Migrate
# Input validation
pip install marshmallow
Verify Flask Installation
# Start Python interpreter
python
# Import Flask
>>> import flask
>>> print(flask.__version__)
3.0.0
>>> exit()
Creating Your Flask Application
Basic Flask Application Structure
# Create directory structure
mkdir -p /var/www/example.com/flask_app
cd /var/www/example.com/flask_app
# Create necessary directories
mkdir -p app/static/{css,js,images}
mkdir -p app/templates
mkdir instance
mkdir tests
Recommended structure:
flask_app/
├── venv/ # Virtual environment
├── app/
│ ├── __init__.py # Application factory
│ ├── models.py # Database models
│ ├── routes.py # URL routes
│ ├── forms.py # WTForms
│ ├── static/
│ │ ├── css/
│ │ ├── js/
│ │ └── images/
│ └── templates/
│ ├── base.html
│ ├── index.html
│ └── ...
├── instance/
│ └── config.py # Instance-specific config
├── tests/
│ └── test_*.py # Unit tests
├── config.py # Configuration classes
├── requirements.txt # Dependencies
├── .env # Environment variables (not in git!)
├── .gitignore
└── wsgi.py # WSGI entry point
Simple Flask Application
Create app/__init__.py:
from flask import Flask
def create_app(config_name='default'):
app = Flask(__name__, instance_relative_config=True)
# Load configuration
app.config.from_object(f'config.{config_name}')
app.config.from_pyfile('config.py', silent=True)
# Register routes
from app import routes
app.register_blueprint(routes.bp)
return app
Create app/routes.py:
from flask import Blueprint, render_template, jsonify
bp = Blueprint('main', __name__)
@bp.route('/')
def index():
return render_template('index.html', title='Home')
@bp.route('/api/health')
def health():
return jsonify({
'status': 'healthy',
'message': 'Flask application is running'
})
@bp.route('/about')
def about():
return render_template('about.html', title='About')
Create config.py:
import os
class Config:
"""Base configuration"""
SECRET_KEY = os.environ.get('SECRET_KEY') or 'dev-secret-key-change-in-production'
SQLALCHEMY_TRACK_MODIFICATIONS = False
class DevelopmentConfig(Config):
"""Development configuration"""
DEBUG = True
SQLALCHEMY_DATABASE_URI = os.environ.get('DEV_DATABASE_URL') or \
'sqlite:///dev.db'
class ProductionConfig(Config):
"""Production configuration"""
DEBUG = False
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL')
SECRET_KEY = os.environ.get('SECRET_KEY') # Must be set in production!
class TestingConfig(Config):
"""Testing configuration"""
TESTING = True
SQLALCHEMY_DATABASE_URI = 'sqlite:///test.db'
config = {
'development': DevelopmentConfig,
'production': ProductionConfig,
'testing': TestingConfig,
'default': DevelopmentConfig
}
Create wsgi.py (entry point):
import os
from app import create_app
# Determine environment
env = os.environ.get('FLASK_ENV', 'production')
# Create application
app = create_app(env)
if __name__ == '__main__':
app.run()
Create basic template app/templates/base.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Flask App{% endblock %}</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body>
<nav>
<a href="{{ url_for('main.index') }}">Home</a>
<a href="{{ url_for('main.about') }}">About</a>
</nav>
<main>
{% block content %}{% endblock %}
</main>
<footer>
<p>© 2026 Your Company</p>
</footer>
</body>
</html>
Create app/templates/index.html:
{% extends "base.html" %}
{% block title %}Home - {{ super() }}{% endblock %}
{% block content %}
<h1>Welcome to Flask</h1>
<p>Your Flask application is running successfully!</p>
{% endblock %}
Test Your Application Locally
# Activate virtual environment
source venv/bin/activate
# Set environment variables
export FLASK_APP=wsgi.py
export FLASK_ENV=development
# Run development server
flask run --host=0.0.0.0 --port=5000
# Or use Python directly
python wsgi.py
Visit http://localhost:5000 in your browser.
Apache and mod_wsgi Configuration
Set Proper Permissions
# Set ownership to Apache user
sudo chown -R www-data:www-data /var/www/example.com/flask_app
# Set directory permissions
sudo find /var/www/example.com/flask_app -type d -exec chmod 755 {} \;
# Set file permissions
sudo find /var/www/example.com/flask_app -type f -exec chmod 644 {} \;
# Make virtual environment executable
sudo chmod +x /var/www/example.com/flask_app/venv/bin/*
Create WSGI Configuration File
sudo nano /var/www/example.com/flask_app/wsgi_app.wsgi
Add:
#!/usr/bin/env python3
import sys
import os
import logging
# Set up logging
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
# Add application directory to path
sys.path.insert(0, '/var/www/example.com/flask_app')
# Activate virtual environment
activate_this = '/var/www/example.com/flask_app/venv/bin/activate_this.py'
# For Python 3.3+, create activate_this.py if it doesn't exist
if not os.path.exists(activate_this):
# Alternative: modify sys.path
venv_path = '/var/www/example.com/flask_app/venv'
site_packages = os.path.join(venv_path, 'lib', 'python3.10', 'site-packages')
sys.path.insert(0, site_packages)
else:
with open(activate_this) as file_:
exec(file_.read(), dict(__file__=activate_this))
# Set environment variables
os.environ['FLASK_ENV'] = 'production'
os.environ['SECRET_KEY'] = 'your-secret-key-here' # Better: use .env file
# Import application
from wsgi import app as application
# Optional: set secret key from environment
# application.secret_key = os.environ.get('SECRET_KEY')
Security note: Never commit secret keys! Use environment variables or instance config.
Configure Apache Virtual Host
For a new Flask-only site:
sudo nano /etc/apache2/sites-available/flask-app.conf
For existing site (add Flask):
sudo nano /etc/apache2/sites-available/example.com-le-ssl.conf
Flask-only configuration:
<VirtualHost *:80>
ServerName example.com
ServerAdmin admin@example.com
# Redirect to HTTPS
Redirect permanent / https://example.com/
</VirtualHost>
<VirtualHost *:443>
ServerName example.com
ServerAdmin admin@example.com
# SSL Configuration (added by Certbot)
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
# WSGI Configuration
WSGIDaemonProcess flask_app python-home=/var/www/example.com/flask_app/venv python-path=/var/www/example.com/flask_app
WSGIProcessGroup flask_app
WSGIScriptAlias / /var/www/example.com/flask_app/wsgi_app.wsgi
WSGIPassAuthorization On
<Directory /var/www/example.com/flask_app>
WSGIApplicationGroup %{GLOBAL}
WSGIScriptReloading On
Require all granted
</Directory>
# Static files (served by Apache, not Flask)
Alias /static /var/www/example.com/flask_app/app/static
<Directory /var/www/example.com/flask_app/app/static>
Require all granted
</Directory>
# Logging
ErrorLog ${APACHE_LOG_DIR}/flask-error.log
CustomLog ${APACHE_LOG_DIR}/flask-access.log combined
LogLevel info
</VirtualHost>
Mixed configuration (Flask + Static HTML):
<VirtualHost *:443>
ServerName example.com
ServerAdmin admin@example.com
DocumentRoot /var/www/example.com/public_html
# SSL Configuration
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
# Serve static site from /public_html
<Directory /var/www/example.com/public_html>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
# Flask app at /app or /api
WSGIDaemonProcess flask_app python-home=/var/www/example.com/flask_app/venv python-path=/var/www/example.com/flask_app
WSGIProcessGroup flask_app
WSGIScriptAlias /app /var/www/example.com/flask_app/wsgi_app.wsgi
<Directory /var/www/example.com/flask_app>
Require all granted
</Directory>
# Flask static files
Alias /app/static /var/www/example.com/flask_app/app/static
<Directory /var/www/example.com/flask_app/app/static>
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/example-error.log
CustomLog ${APACHE_LOG_DIR}/example-access.log combined
</VirtualHost>
WSGI directives explained:
WSGIDaemonProcess- Creates isolated Python processpython-home- Points to virtual environmentpython-path- Application directoryWSGIProcessGroup- Associates requests with daemon processWSGIScriptAlias- Maps URL to WSGI scriptWSGIPassAuthorization- Passes HTTP auth headers to FlaskWSGIApplicationGroup %{GLOBAL}- Single interpreter (better compatibility)
Enable Site and Reload Apache
# Test Apache configuration
sudo apache2ctl configtest
# Enable the site
sudo a2ensite flask-app.conf
# Disable default site if needed
sudo a2dissite 000-default.conf
# Reload Apache
sudo systemctl reload apache2
# Check Apache status
sudo systemctl status apache2
Test Your Deployment
Visit your domain:
https://example.com
https://example.com/api/health
Production Deployment
Environment Variables
Never hardcode secrets! Use environment variables or instance configuration.
Create .env file:
nano /var/www/example.com/flask_app/.env
Add:
FLASK_ENV=production
SECRET_KEY=your-very-secret-key-generate-with-python-secrets
DATABASE_URL=mysql+pymysql://user:password@localhost/dbname
MAIL_SERVER=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=your-email@gmail.com
MAIL_PASSWORD=your-app-password
Secure the file:
sudo chown www-data:www-data /var/www/example.com/flask_app/.env
sudo chmod 600 /var/www/example.com/flask_app/.env
Generate secure secret key:
# In Python interpreter
import secrets
print(secrets.token_hex(32))
Load environment variables in Flask:
# In app/__init__.py or config.py
from dotenv load_dotenv
import os
# Load .env file
load_dotenv()
# Access variables
SECRET_KEY = os.environ.get('SECRET_KEY')
DATABASE_URL = os.environ.get('DATABASE_URL')
Instance Configuration
Instance-specific config (not in version control):
mkdir -p /var/www/example.com/flask_app/instance
nano /var/www/example.com/flask_app/instance/config.py
Add:
import os
SECRET_KEY = os.environ.get('SECRET_KEY')
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL')
MAIL_SERVER = os.environ.get('MAIL_SERVER')
MAIL_PORT = int(os.environ.get('MAIL_PORT', 587))
MAIL_USE_TLS = True
MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')
Production Checklist
Before deploying:
DEBUG = Falsein production config- Strong
SECRET_KEYset via environment - Database credentials secured
.envfile not in version control- Error logging configured
- HTTPS enabled and enforced
- Security headers configured
- CORS configured if needed for API
- Rate limiting implemented
- Input validation on all forms
- SQL injection prevention (use ORM)
- XSS protection (escape user input)
- CSRF protection enabled
- Regular backups scheduled
Database Integration
SQLAlchemy with MySQL
Install dependencies:
source venv/bin/activate
pip install Flask-SQLAlchemy pymysql cryptography
Configure database in config.py:
import os
class ProductionConfig(Config):
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
'mysql+pymysql://user:password@localhost/flask_db'
SQLALCHEMY_ENGINE_OPTIONS = {
'pool_size': 10,
'pool_recycle': 3600,
'pool_pre_ping': True
}
Initialize database in app/__init__.py:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
db = SQLAlchemy()
migrate = Migrate()
def create_app(config_name='default'):
app = Flask(__name__, instance_relative_config=True)
app.config.from_object(f'config.{config_name}')
# Initialize extensions
db.init_app(app)
migrate.init_app(app, db)
# Import models
from app import models
# Register blueprints
from app import routes
app.register_blueprint(routes.bp)
return app
Create models in app/models.py:
from datetime import datetime
from app import db
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
password_hash = db.Column(db.String(200))
created_at = db.Column(db.DateTime, default=datetime.utcnow)
def __repr__(self):
return f'<User {self.username}>'
class Post(db.Model):
__tablename__ = 'posts'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(200), nullable=False)
content = db.Column(db.Text, nullable=False)
author_id = db.Column(db.Integer, db.ForeignKey('users.id'))
created_at = db.Column(db.DateTime, default=datetime.utcnow)
author = db.relationship('User', backref='posts')
Create database migrations:
# Initialize migrations
flask db init
# Create migration
flask db migrate -m "Initial migration"
# Apply migration
flask db upgrade
Security Best Practices
Input Validation
from flask import request, abort
from marshmallow import Schema, fields, validate, ValidationError
class UserSchema(Schema):
username = fields.Str(required=True, validate=validate.Length(min=3, max=80))
email = fields.Email(required=True)
password = fields.Str(required=True, validate=validate.Length(min=8))
@app.route('/register', methods=['POST'])
def register():
schema = UserSchema()
try:
data = schema.load(request.json)
except ValidationError as err:
return {'errors': err.messages}, 400
# Process validated data
# ...
Password Hashing
from werkzeug.security import generate_password_hash, check_password_hash
# When creating user
user.password_hash = generate_password_hash(password)
# When checking password
if check_password_hash(user.password_hash, password):
# Password correct
pass
CSRF Protection
from flask_wtf.csrf import CSRFProtect
csrf = CSRFProtect()
def create_app():
app = Flask(__name__)
csrf.init_app(app)
return app
Rate Limiting
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
limiter = Limiter(
app,
key_func=get_remote_address,
default_limits=["200 per day", "50 per hour"]
)
@app.route('/api/login', methods=['POST'])
@limiter.limit("5 per minute")
def login():
# Login logic
pass
Security Headers
Add to Apache configuration:
<IfModule mod_headers.c>
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosniff"
Header always set X-XSS-Protection "1; mode=block"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';"
</IfModule>
Performance Optimization
Caching
from flask_caching import Cache
cache = Cache(config={'CACHE_TYPE': 'simple'})
def create_app():
app = Flask(__name__)
cache.init_app(app)
return app
@app.route('/expensive-operation')
@cache.cached(timeout=300)
def expensive_operation():
# Time-consuming operation
result = heavy_computation()
return result
Database Query Optimization
# Bad: N+1 queries
users = User.query.all()
for user in users:
print(user.posts) # Separate query for each user
# Good: Eager loading
users = User.query.options(db.joinedload(User.posts)).all()
for user in users:
print(user.posts) # Already loaded
Static File Optimization
In Apache config:
# Enable compression
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css
AddOutputFilterByType DEFLATE application/javascript application/json
</IfModule>
# Browser caching
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/jpg "access plus 1 year"
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType text/css "access plus 1 month"
ExpiresByType application/javascript "access plus 1 month"
</IfModule>
Connection Pooling
Already configured in SQLAlchemy:
SQLALCHEMY_ENGINE_OPTIONS = {
'pool_size': 10, # Max connections
'pool_recycle': 3600, # Recycle after 1 hour
'pool_pre_ping': True # Verify connections before use
}
Logging and Monitoring
Configure Logging
Create app/logger.py:
import logging
import os
from logging.handlers import RotatingFileHandler
def setup_logging(app):
if not app.debug:
# Ensure log directory exists
if not os.path.exists('logs'):
os.mkdir('logs')
# File handler
file_handler = RotatingFileHandler(
'logs/flask_app.log',
maxBytes=10240000, # 10MB
backupCount=10
)
file_handler.setFormatter(logging.Formatter(
'%(asctime)s %(levelname)s: %(message)s '
'[in %(pathname)s:%(lineno)d]'
))
file_handler.setLevel(logging.INFO)
app.logger.addHandler(file_handler)
app.logger.setLevel(logging.INFO)
app.logger.info('Flask app startup')
Use in app/__init__.py:
from app.logger import setup_logging
def create_app():
app = Flask(__name__)
# ... configuration ...
setup_logging(app)
return app
Error Handling
@app.errorhandler(404)
def not_found_error(error):
return render_template('404.html'), 404
@app.errorhandler(500)
def internal_error(error):
db.session.rollback()
app.logger.error(f'Server Error: {error}')
return render_template('500.html'), 500
@app.errorhandler(Exception)
def handle_exception(e):
app.logger.error(f'Unhandled exception: {e}')
return render_template('500.html'), 500
Application Monitoring
@app.before_request
def before_request():
g.start_time = time.time()
@app.after_request
def after_request(response):
diff = time.time() - g.start_time
app.logger.info(f'{request.method} {request.path} - {response.status_code} - {diff:.4f}s')
return response
Troubleshooting
Common Issues and Solutions
Issue: 500 Internal Server Error
# Check Apache error log
sudo tail -50 /var/log/apache2/flask-error.log
# Check Flask application log
sudo tail -50 /var/www/example.com/flask_app/logs/flask_app.log
# Test WSGI file directly
sudo -u www-data python3 /var/www/example.com/flask_app/wsgi_app.wsgi
Issue: Module not found
# Verify virtual environment path in WSGI config
# Ensure mod_wsgi is using correct Python
# Check Python path
sudo -u www-data /var/www/example.com/flask_app/venv/bin/python -c "import sys; print('\n'.join(sys.path))"
Issue: Permission denied
# Fix ownership
sudo chown -R www-data:www-data /var/www/example.com/flask_app
# Fix permissions
sudo chmod -R 755 /var/www/example.com/flask_app
sudo chmod 600 /var/www/example.com/flask_app/.env
Issue: Database connection failed
# Test database connection
mysql -u your_user -p your_database
# Verify DATABASE_URL in .env
# Check MySQL user permissions
Issue: Static files not loading
# Verify Alias directive in Apache config
# Check static directory permissions
# Clear browser cache
# Check Apache access log for 404s
Issue: Changes not reflected
# Touch WSGI file to reload
sudo touch /var/www/example.com/flask_app/wsgi_app.wsgi
# Or restart Apache
sudo systemctl restart apache2
Debug Mode (Development Only!)
Never use in production!
# In config.py
class DevelopmentConfig(Config):
DEBUG = True
# Set environment
export FLASK_ENV=development
What’s Changed Since Python 2.7
Major Python Updates
Python 2.7 → Python 3.11+:
- Print function:
print "text"→print("text") - Division:
5/2 = 2→5/2 = 2.5,5//2 = 2 - Strings: Unicode by default
- Type hints: Optional type annotations
- f-strings:
f"Hello {name}"(much better than.format())
Flask Updates
Flask 0.x → Flask 3.0:
- Application factory pattern - Now recommended
- Blueprints - Better code organization
- async/await support - Flask 2.0+
- Improved CLI - Better flask commands
- Better error handling
- JSON improvements - Built-in JSON handling
Deployment Changes
Then:
- Manual pip installations
- Global package conflicts common
sudo pip installeverywhere- Python 2/3 compatibility issues
Now:
- Virtual environments mandatory
- Better dependency management
- Clear Python 3 standard
- Modern mod_wsgi-py3 package
- Better production patterns
Virtual Environment
Old way (virtualenv):
sudo pip install virtualenv
virtualenv venv
New way (built-in venv):
python3 -m venv venv
Best Practices Evolution
Old:
- Single-file applications
- Global configuration
- Hardcoded secrets
- Basic error handling
New:
- Application factory pattern
- Blueprint organization
- Environment variables
- Comprehensive logging
- Type hints
- Automated testing
- CI/CD pipelines
Conclusion
You now have a production-ready Flask application running on Apache with mod_wsgi!
What You’ve Accomplished
✅ Modern Python 3 environment setup
✅ Virtual environment isolation
✅ Flask application with proper structure
✅ Apache integration via mod_wsgi
✅ Database connectivity
✅ Security best practices
✅ Performance optimization
✅ Logging and monitoring
✅ Production deployment configuration
Next Steps
Enhance your application:
- Add user authentication (Flask-Login)
- Implement RESTful API (Flask-RESTful)
- Add front-end framework (React, Vue)
- Implement real-time features (Flask-SocketIO)
- Add background tasks (Celery)
- Implement full-text search (Elasticsearch)
Deployment improvements:
- Set up CI/CD pipeline (GitHub Actions, GitLab CI)
- Implement monitoring (Prometheus, Grafana)
- Add error tracking (Sentry)
- Configure CDN for static files
- Implement horizontal scaling
- Use Docker containers
- Deploy to cloud (AWS, Azure, DigitalOcean)
Alternative deployment methods:
- Gunicorn + Nginx (more common than mod_wsgi)
- Docker + Docker Compose
- Kubernetes
- Platform as a Service (Heroku, PythonAnywhere)
- Serverless (AWS Lambda, Google Cloud Functions)
Learning Resources
- Flask Official Documentation
- Flask Mega-Tutorial
- Real Python Flask Tutorials
- Flask-SQLAlchemy Documentation
- Python Web Development Guide
Quick Reference Commands
# Virtual environment
python3 -m venv venv
source venv/bin/activate
deactivate
# Package management
pip install -r requirements.txt
pip freeze > requirements.txt
# Flask commands
export FLASK_APP=wsgi.py
flask run
flask shell
flask db init
flask db migrate
flask db upgrade
# Apache
sudo systemctl reload apache2
sudo apache2ctl configtest
sudo a2ensite flask-app.conf
# Logs
sudo tail -f /var/log/apache2/flask-error.log
tail -f logs/flask_app.log
# Troubleshooting
sudo touch wsgi_app.wsgi # Reload app
sudo systemctl restart apache2
Your Flask application is ready to serve dynamic content, APIs, and full web applications!
Related Posts:
- Building RESTful APIs with Flask
- Flask User Authentication Complete Guide
- Deploying Flask with Docker and Gunicorn
- Flask vs Django: Choosing the Right Framework
- Modern Python Web Development Best Practices