# Best Practices for Securing Django in Production

Deploying Django in production requires careful security configurations, especially when using cloud platforms like **AWS, DigitalOcean, GCP, or Azure**. While Django provides security features out of the box, a misconfigured production environment can expose your application to attacks.

This guide will expand on common security mistakes and **how to configure Django securely in production** with best practices for **AWS, DigitalOcean, GCP, and other cloud platforms**.

## **1\. Setting Up Django’s Production Security Configurations**

### **1.1 Disable Debug Mode**

Leaving `DEBUG=True` in production is a major security risk. It exposes environment variables, database credentials, and internal stack traces.

**Fix:**

```python
import os

DEBUG = os.getenv("DJANGO_DEBUG", "False") == "True"
ALLOWED_HOSTS = ["yourdomain.com"]
```

Use **environment variables** to manage settings securely instead of hardcoding them.

### **1.2 Restrict Allowed Hosts**

Only allow requests from your domain:

```python
ALLOWED_HOSTS = ["yourdomain.com", "api.yourdomain.com"]
```

For cloud-based environments (AWS/GCP), set `ALLOWED_HOSTS` dynamically:

```python
ALLOWED_HOSTS = os.getenv("DJANGO_ALLOWED_HOSTS", "").split(",")
```

## **2\. Securing Database in Production**

### **2.1 Secure Database Credentials Using Environment Variables**

Never hardcode database credentials in [`settings.py`](http://settings.py). Instead, store them in **AWS SSM Parameter Store**, **GCP Secret Manager**, **DigitalOcean App Secrets**, or `.env` files.

Example for **AWS RDS / PostgreSQL**:

```python
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': os.getenv("DB_NAME"),
        'USER': os.getenv("DB_USER"),
        'PASSWORD': os.getenv("DB_PASSWORD"),
        'HOST': os.getenv("DB_HOST"),  # RDS instance or Cloud SQL
        'PORT': os.getenv("DB_PORT", "5432"),
        'OPTIONS': {
            'sslmode': 'require',  # Enforce SSL
        },
    }
}
```

### **2.2 Use IAM Roles for RDS (AWS)**

Instead of storing passwords, use **IAM authentication** with AWS RDS:

```bash
psql "host=your-rds-instance.amazonaws.com dbname=yourdb sslmode=verify-full sslrootcert=rds-ca.pem"
```

### **2.3 Enable SSL for Database Connections**

For **PostgreSQL on AWS RDS, DigitalOcean DBaaS, or GCP Cloud SQL**, enforce SSL to encrypt data in transit.

```python
'OPTIONS': {
    'sslmode': 'require',
}
```

Verify with:

```bash
psql "sslmode=require"
```

## **3\. Securing Static and Media Files**

### **3.1 Use a CDN for Static & Media Files**

Serving static and media files via **S3, DigitalOcean Spaces, or Google Cloud Storage (GCS)** reduces server load and increases security.

**Example for AWS S3:**

```python
INSTALLED_APPS += ["storages"]

AWS_STORAGE_BUCKET_NAME = os.getenv("AWS_STORAGE_BUCKET_NAME")
AWS_S3_CUSTOM_DOMAIN = f"{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com"
DEFAULT_FILE_STORAGE = "storages.backends.s3boto3.S3Boto3Storage"
```

For **DigitalOcean Spaces:**

```python
DEFAULT_FILE_STORAGE = "storages.backends.s3boto3.S3Boto3Storage"
AWS_S3_ENDPOINT_URL = "https://nyc3.digitaloceanspaces.com"
```

For **Google Cloud Storage (GCP):**

```python
DEFAULT_FILE_STORAGE = "storages.backends.gcloud.GoogleCloudStorage"
GS_BUCKET_NAME = os.getenv("GS_BUCKET_NAME")
```

### **3.2 Restrict Public Access to Sensitive Files**

* Set **private** permissions for user-uploaded files.
    
* Use **pre-signed URLs** for downloads.
    

```python
AWS_S3_OBJECT_PARAMETERS = {
    "CacheControl": "max-age=86400",
    "ACL": "private"
}
```

## **4\. Secure Authentication & Sessions**

### **4.1 Use Secure Password Hashing**

Enable **Argon2** for better security:

```python
PASSWORD_HASHERS = [
    'django.contrib.auth.hashers.Argon2PasswordHasher',
]
```

### **4.2 Enforce HTTPS for Sessions**

```python
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
```

### **4.3 Enable Multi-Factor Authentication (MFA)**

Use **django-otp** for MFA in Django Admin:

```bash
pip install django-otp
```

Add to `INSTALLED_APPS`:

```python
INSTALLED_APPS += ["django_otp", "django_otp.plugins.otp_totp"]
```

## **5\. Protect Against SQL Injection & XSS**

### **5.1 Prevent SQL Injection**

Always use **ORM** or parameterized queries:

```python
cursor.execute("SELECT * FROM users WHERE username = %s", [username])
```

### **5.2 Prevent Cross-Site Scripting (XSS)**

* Use Django’s **auto-escaping** in templates.
    
* Enable **content security policy (CSP)** middleware:
    

```bash
pip install django-csp
```

Add CSP Headers:

```python
MIDDLEWARE += ["csp.middleware.CSPMiddleware"]
CSP_DEFAULT_SRC = ["'self'"]
CSP_SCRIPT_SRC = ["'self'", "'unsafe-inline'"]
```

## **6\. Configure Web Server Security**

### **6.1 Use Gunicorn for Deployment**

```bash
gunicorn --workers 3 myproject.wsgi
```

### **6.2 Harden Nginx Configuration**

```nginx
server {
    listen 80;
    server_name yourdomain.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name yourdomain.com;
    
    ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}
```

## **7\. Enforce Logging & Monitoring**

### **7.1 Configure Django Logging**

```python
LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    "handlers": {
        "file": {
            "level": "ERROR",
            "class": "logging.FileHandler",
            "filename": "/var/log/django_errors.log",
        },
    },
    "loggers": {
        "django": {
            "handlers": ["file"],
            "level": "ERROR",
            "propagate": True,
        },
    },
}
```

### **7.2 Use AWS CloudWatch for Logs**

For **AWS ECS or EC2**, install **CloudWatch Agent**:

```bash
sudo yum install amazon-cloudwatch-agent
```

Configure `awslogs`:

```bash
[general]
state_file = /var/awslogs/state/agent-state

[/var/log/django]
file = /var/log/django_errors.log
log_group_name = django_logs
```

Start logging service:

```bash
sudo systemctl start awslogs
```

## **8\. Set Up Firewalls & DDoS Protection**

### **8.1 Restrict Database Access**

For **AWS RDS**, create **security groups**:

```bash
aws ec2 authorize-security-group-ingress --group-id sg-12345 --protocol tcp --port 5432 --source-ip your-server-ip/32
```

For **DigitalOcean**, enable **Cloud Firewalls**.

For **GCP**, restrict database access using **VPC firewall rules**.

### **8.2 Use AWS WAF for DDoS Protection**

```bash
aws waf create-web-acl --name MyWebACL
```

For **Cloudflare**, enable **DDoS protection** under Firewall Rules.

---

## **Final Thoughts**

Securing Django in production requires **configuring security settings, using cloud-native security tools, and continuously monitoring vulnerabilities**. Whether deploying on **AWS, DigitalOcean, GCP, or other platforms**, follow these best practices to keep your application secure.

Feel free to reach me at [AhmadWKhan.com](https://AhmadWKhan.com) to discuss your application’s security issues.
