Flask Nginx Configuration
Introduction
When deploying Flask applications to production environments, using Flask's built-in development server is not recommended due to its limitations in handling multiple concurrent requests, security concerns, and performance issues. This is where Nginx comes in as a powerful solution.
Nginx (pronounced "engine-x") is a high-performance web server that can function as a reverse proxy, load balancer, and HTTP cache. By placing Nginx in front of your Flask application, you can:
- Serve static files more efficiently
- Handle multiple client connections simultaneously
- Provide SSL termination
- Act as a buffer against traffic spikes
- Improve security by not exposing your Flask app directly
In this tutorial, we'll learn how to configure Nginx as a reverse proxy for your Flask application to create a robust production deployment setup.
Prerequisites
Before we begin, make sure you have:
- A Flask application ready for deployment
- A server with Ubuntu/Debian (though concepts apply to other distributions)
- Basic Linux command line knowledge
- Sudo/root access to install packages
Step 1: Installing Nginx
First, let's install Nginx on our server:
# Update package index
sudo apt update
# Install Nginx
sudo apt install nginx
# Start Nginx service
sudo systemctl start nginx
# Enable Nginx to start on system boot
sudo systemctl enable nginx
To verify that Nginx is running properly:
sudo systemctl status nginx
You should see an output indicating that the service is active (running).
Step 2: Setting Up Your Flask Application with Gunicorn
While Nginx will serve as our web server, we need a WSGI server to run our Flask application. Gunicorn (Green Unicorn) is a popular choice:
# Install Gunicorn
pip install gunicorn
Let's assume we have a Flask application structured like this:
/home/user/myflaskapp/
├── app.py
├── requirements.txt
├── static/
└── templates/
Where app.py
contains:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def home():
return "Hello from Flask!"
if __name__ == '__main__':
app.run(debug=True)
To run this with Gunicorn:
cd /home/user/myflaskapp
gunicorn --bind 127.0.0.1:8000 app:app
This starts your Flask application on localhost port 8000. The format app:app
refers to the Flask application object (app
) in the app.py
file.
Step 3: Creating a Systemd Service File for Your Flask App
To ensure our Flask application runs automatically and restarts on failure, we'll create a systemd service:
sudo nano /etc/systemd/system/myflaskapp.service
Add the following content:
[Unit]
Description=Gunicorn instance to serve myflaskapp
After=network.target
[Service]
User=user
Group=www-data
WorkingDirectory=/home/user/myflaskapp
Environment="PATH=/home/user/myflaskapp/venv/bin"
ExecStart=/home/user/myflaskapp/venv/bin/gunicorn --workers 3 --bind 127.0.0.1:8000 app:app
Restart=always
[Install]
WantedBy=multi-user.target
Be sure to adjust the paths and usernames to match your setup.
Enable and start the service:
sudo systemctl start myflaskapp
sudo systemctl enable myflaskapp
Step 4: Configuring Nginx as a Reverse Proxy
Now it's time to configure Nginx to forward requests to our Flask application:
sudo nano /etc/nginx/sites-available/myflaskapp
Add the following configuration:
server {
listen 80;
server_name your_domain.com www.your_domain.com;
location /static {
alias /home/user/myflaskapp/static;
}
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Let's understand the key components of this configuration:
listen 80
- Nginx will listen on port 80 (HTTP)server_name
- Specifies which domain names this server block should respond tolocation /static
- Configures Nginx to serve static files directlylocation /
- Proxies all other requests to our Flask application running on port 8000- The
proxy_set_header
directives pass important information to the Flask app
Create a symbolic link to enable the site:
sudo ln -s /etc/nginx/sites-available/myflaskapp /etc/nginx/sites-enabled/
Test the Nginx configuration:
sudo nginx -t
If the test is successful, restart Nginx:
sudo systemctl restart nginx
Step 5: Configuring SSL with Let's Encrypt (Optional but Recommended)
For a secure production environment, you should enable HTTPS using SSL certificates. Let's Encrypt provides free SSL certificates:
# Install Certbot
sudo apt install certbot python3-certbot-nginx
# Obtain and install a certificate
sudo certbot --nginx -d your_domain.com -d www.your_domain.com
Follow the prompts, and Certbot will automatically update your Nginx configuration to use HTTPS.
Step 6: Advanced Nginx Configuration
Optimizing Performance
Add these settings to your server block to improve performance:
server {
# ... existing configuration ...
# Enable gzip compression
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
# Set client max body size (for file uploads)
client_max_body_size 5M;
# Cache control for static assets
location /static {
alias /home/user/myflaskapp/static;
expires 7d;
}
}
Rate Limiting
Add rate limiting to protect against abuse:
# Add at the top of the server block
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
server {
# ... existing configuration ...
location / {
# Apply rate limiting
limit_req zone=mylimit burst=20 nodelay;
# ... existing proxy configuration ...
}
}
This configuration limits each client IP to 10 requests per second with a burst of up to 20 requests.
Real-world Example: Deploying a Flask Blog
Let's consider a more complete example for deploying a Flask blog application:
Directory structure:
/home/user/flaskblog/
├── blog.py
├── config.py
├── requirements.txt
├── static/
│ ├── css/
│ ├── js/
│ └── images/
├── templates/
└── venv/
Systemd service file (/etc/systemd/system/flaskblog.service
):
[Unit]
Description=Gunicorn instance to serve Flask blog
After=network.target
[Service]
User=user
Group=www-data
WorkingDirectory=/home/user/flaskblog
Environment="PATH=/home/user/flaskblog/venv/bin"
Environment="FLASK_APP=blog.py"
Environment="FLASK_ENV=production"
ExecStart=/home/user/flaskblog/venv/bin/gunicorn --workers 3 --bind unix:/home/user/flaskblog/flaskblog.sock -m 007 wsgi:app
Restart=always
[Install]
WantedBy=multi-user.target
Create a WSGI entry point (wsgi.py
):
from blog import app
if __name__ == "__main__":
app.run()
Nginx configuration (/etc/nginx/sites-available/flaskblog
):
server {
listen 80;
server_name blog.example.com;
location /static {
alias /home/user/flaskblog/static;
expires 30d;
}
location / {
proxy_pass http://unix:/home/user/flaskblog/flaskblog.sock;
include proxy_params;
}
}
The key difference in this example is that we're using a Unix socket (flaskblog.sock
) instead of a TCP port for communication between Gunicorn and Nginx, which can be more efficient for connections on the same server.
Troubleshooting Common Issues
502 Bad Gateway Error
If you see a "502 Bad Gateway" error, it typically means Nginx cannot communicate with your Flask application. Common causes include:
- Gunicorn not running - Check with
sudo systemctl status myflaskapp
- Incorrect socket or port configuration - Verify the
proxy_pass
directive matches how Gunicorn is configured - Permission issues - Ensure Nginx can access the socket file
To troubleshoot:
# Check Nginx error logs
sudo tail -f /var/log/nginx/error.log
# Check your Flask application logs
sudo journalctl -u myflaskapp
Static Files Not Loading
If static files aren't being served correctly:
- Check the path in your Nginx configuration
- Ensure the Nginx user has read permissions on your static directory
- Verify the URL paths in your Flask templates
# Set proper permissions
sudo chown -R user:www-data /home/user/myflaskapp/static
sudo chmod -R 755 /home/user/myflaskapp/static
Summary
In this tutorial, we've learned how to:
- Install and configure Nginx as a reverse proxy for a Flask application
- Set up Gunicorn as a WSGI server to run our Flask app
- Create a systemd service for reliable application management
- Configure SSL with Let's Encrypt for secure HTTPS connections
- Optimize Nginx for performance with caching and compression
- Implement rate limiting to protect against abuse
- Troubleshoot common deployment issues
By combining Flask, Gunicorn, and Nginx in this way, you've created a robust, production-ready deployment that can handle significant traffic while maintaining security and performance.
Additional Resources
- Nginx Official Documentation
- Flask Deployment Options
- Gunicorn Documentation
- Digital Ocean's Guide on Deploying Flask with Nginx
Exercises
- Configure Nginx to serve a Flask application with two separate applications under different URL paths (e.g.,
/app1
and/app2
) - Set up Nginx to cache responses from your Flask application for improved performance
- Configure a WebSocket connection through Nginx to a Flask application using Socket.IO
- Implement a custom error page in Nginx for 404 and 500 status codes
- Set up log rotation for your Nginx and Flask application logs
By practicing these exercises, you'll gain a deeper understanding of how Nginx and Flask work together in production environments.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)