Skip to main content

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:

bash
# 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:

bash
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:

bash
# 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:

python
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:

bash
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:

bash
sudo nano /etc/systemd/system/myflaskapp.service

Add the following content:

ini
[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:

bash
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:

bash
sudo nano /etc/nginx/sites-available/myflaskapp

Add the following configuration:

nginx
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 to
  • location /static - Configures Nginx to serve static files directly
  • location / - 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:

bash
sudo ln -s /etc/nginx/sites-available/myflaskapp /etc/nginx/sites-enabled/

Test the Nginx configuration:

bash
sudo nginx -t

If the test is successful, restart Nginx:

bash
sudo systemctl restart nginx

For a secure production environment, you should enable HTTPS using SSL certificates. Let's Encrypt provides free SSL certificates:

bash
# 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:

nginx
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:

nginx
# 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):

ini
[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):

python
from blog import app

if __name__ == "__main__":
app.run()

Nginx configuration (/etc/nginx/sites-available/flaskblog):

nginx
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:

  1. Gunicorn not running - Check with sudo systemctl status myflaskapp
  2. Incorrect socket or port configuration - Verify the proxy_pass directive matches how Gunicorn is configured
  3. Permission issues - Ensure Nginx can access the socket file

To troubleshoot:

bash
# 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:

  1. Check the path in your Nginx configuration
  2. Ensure the Nginx user has read permissions on your static directory
  3. Verify the URL paths in your Flask templates
bash
# 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:

  1. Install and configure Nginx as a reverse proxy for a Flask application
  2. Set up Gunicorn as a WSGI server to run our Flask app
  3. Create a systemd service for reliable application management
  4. Configure SSL with Let's Encrypt for secure HTTPS connections
  5. Optimize Nginx for performance with caching and compression
  6. Implement rate limiting to protect against abuse
  7. 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

Exercises

  1. Configure Nginx to serve a Flask application with two separate applications under different URL paths (e.g., /app1 and /app2)
  2. Set up Nginx to cache responses from your Flask application for improved performance
  3. Configure a WebSocket connection through Nginx to a Flask application using Socket.IO
  4. Implement a custom error page in Nginx for 404 and 500 status codes
  5. 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! :)