14. Django REST API: Deployment Notes
Django REST API – Deployment Notes

In this final post of the series, we will review important deployment notes for Django REST APIs.
Before moving an API to production, we need to think about security, environment variables, allowed hosts, CORS, HTTPS, databases, static files, and server configuration.



 

Development vs production
A Django project running locally is not the same as a Django project running in production.

During development, we usually use:

DEBUG = True
SQLite database
Local development server
Localhost URLs
Simple authentication testing
Browsable API enabled

In production, the API is exposed to real users and real requests.

DEBUG = False
Real domain name
Production database
HTTPS
Environment variables
Proper CORS configuration
Production web server
Logging and monitoring

This means we need to prepare the project carefully before deployment.

 

Never deploy with DEBUG=True
One of the most important production rules is to disable debug mode.

DEBUG = False

When DEBUG=True, Django may show detailed error pages with sensitive information about your code, settings, environment, and database queries.

That is useful during development, but dangerous in production.

Development → DEBUG = True
Production  → DEBUG = False

Django’s deployment checklist also highlights that ALLOWED_HOSTS must be properly configured when DEBUG=False. Otherwise, Django will reject requests with a Bad Request response. 

 

Setting ALLOWED_HOSTS
ALLOWED_HOSTS defines which hostnames or domains your Django project is allowed to serve.

For local development, you may have:

ALLOWED_HOSTS = ['localhost', '127.0.0.1']

In production, you should add your real domain.

ALLOWED_HOSTS = ['api.example.com', 'www.example.com']

Avoid using a wildcard unless you fully understand the security implications.

ALLOWED_HOSTS = ['*']

For a real API, be explicit.

Use only the domains that should serve your API.

 

Using environment variables
Sensitive or environment-specific values should not be hardcoded directly in settings.py.

Examples include:

SECRET_KEY
DEBUG
DATABASE_URL
ALLOWED_HOSTS
CORS_ALLOWED_ORIGINS
EMAIL settings
External API keys

Instead, these values should come from environment variables.

import os

SECRET_KEY = os.environ.get('SECRET_KEY')
DEBUG = os.environ.get('DEBUG') == 'True'

ALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', '').split(',')

This makes it easier to use different settings for development, staging, and production.

Development → Local settings
Staging     → Test production-like settings
Production  → Real production settings

 

SECRET_KEY
Django’s SECRET_KEY should be kept private.

SECRET_KEY = os.environ.get('SECRET_KEY')

Do not commit the production secret key to GitHub or any public repository.

Never expose the production SECRET_KEY.

If a production secret key is leaked, you should rotate it and review the security impact.

 

Database configuration
During development, SQLite is simple and convenient.

SQLite
db.sqlite3

For production, it is common to use a more robust database such as PostgreSQL.

PostgreSQL
Managed database service
Backups
Connection pooling
Monitoring

A production database should have backups and should not depend only on files inside the application server.

In many deployments, database settings are configured using a database URL.

postgres://user:password@host:5432/database_name

The exact configuration depends on the hosting provider or server setup you choose.

 

Static files
Even if we are building an API, Django may still need static files.

For example:

Django Admin CSS
Django Admin JavaScript
Browsable API assets
Swagger UI assets
Redoc assets

Before deployment, configure STATIC_ROOT.

STATIC_URL = 'static/'
STATIC_ROOT = BASE_DIR / 'staticfiles'

Then collect static files:

python manage.py collectstatic

In production, these files are usually served by a web server, CDN, or storage service rather than Django directly.

 

CORS
CORS stands for Cross-Origin Resource Sharing.

It matters when your frontend and backend are hosted on different origins.

Frontend: https://app.example.com
Backend:  https://api.example.com

In that case, the browser needs permission to allow the frontend to call the API.

A common package for handling this in Django is django-cors-headers.

pip install django-cors-headers

Add it to INSTALLED_APPS:

INSTALLED_APPS = [
    ...

    'corsheaders',
]

Add the middleware near the top of the middleware list:

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',
    ...

    'django.middleware.common.CommonMiddleware',
]

Then configure the allowed frontend origins.

CORS_ALLOWED_ORIGINS = [
    'https://app.example.com',
]

The django-cors-headers documentation defines CORS_ALLOWED_ORIGINS as the list of origins that are allowed to make cross-site HTTP requests.
 

Do not use CORS_ALLOW_ALL_ORIGINS = True in production unless you have a very specific reason and understand the risks.

Development → Local frontend origins
Production  → Only trusted frontend domains

 

CSRF trusted origins
If your API uses session authentication, browser-based forms, or the browsable API, CSRF settings can matter.

For production domains, you may need to configure CSRF_TRUSTED_ORIGINS.

CSRF_TRUSTED_ORIGINS = [
    'https://app.example.com',
    'https://api.example.com',
]

Django’s settings documentation notes that CSRF_TRUSTED_ORIGINS is used for trusted origins on unsafe requests such as POST requests. 

This is especially relevant when your frontend and backend use different domains or subdomains.

 

HTTPS
Production APIs should use HTTPS.

HTTPS protects data between the client and the server.

Login credentials
Authentication tokens
Session cookies
API request bodies
API responses

Without HTTPS, sensitive data can be exposed while travelling across the network.

In production, HTTPS is usually handled by:

Reverse proxy
Load balancer
Hosting platform
Nginx with TLS certificate
Cloud provider certificate manager

For a real deployment, make sure your API is only accessed through https://.

 

Security settings
Django includes several security-related settings that are useful in production.

SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True

These settings help enforce HTTPS and secure cookies.

If Django is behind a reverse proxy that handles HTTPS, you may also need:

SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

Only use this when your proxy is correctly configured to send this header.

Security settings depend on your hosting setup, so always test them carefully before going live.

 

Authentication in production
In this series, we used session authentication and basic authentication for learning and testing.

'DEFAULT_AUTHENTICATION_CLASSES': [
    'rest_framework.authentication.SessionAuthentication',
    'rest_framework.authentication.BasicAuthentication',
]

For production APIs consumed by React, mobile apps, or external clients, token-based authentication or JWT is often more practical.

Token Authentication
JWT Authentication
OAuth2
Session Authentication for browser-based apps

Django REST Framework describes authentication as the mechanism that associates incoming requests with credentials, which are then used by permissions and throttling policies. 
 

Choose the authentication method based on the type of clients that will consume your API.

 

Permissions and ownership
Before deployment, review your permission rules carefully.

For this project, we added ownership protection.

def get_queryset(self):
    return Project.objects.filter(owner=self.request.user)

And for tasks:

def get_queryset(self):
    return Task.objects.filter(project__owner=self.request.user)

Before going live, test that users cannot access other users’ data.

User A cannot view User B projects
User A cannot edit User B projects
User A cannot delete User B projects
User A cannot create tasks inside User B projects

This is one of the most important checks in APIs with private user data.

 

API documentation in production
In the previous post, we added documentation URLs.

/api/schema/
/api/docs/
/api/redoc/

Before deployment, decide whether these pages should be public or private.

Public API     → Documentation may be public
Private API    → Documentation should usually be protected
Internal API   → Documentation may be internal only

For some projects, public documentation is useful.

For others, exposing documentation can reveal too much about internal API structure.

Choose based on the type of API you are building.

 

Running deployment checks
Django includes a deployment check command.

python manage.py check --deploy

This command checks for common production configuration issues.

Security settings
DEBUG configuration
Allowed hosts
Cookie security
HTTPS-related settings

The official Django deployment checklist recommends reviewing deployment settings carefully before production. 

This command does not replace a full security review, but it is a useful starting point.

 

Production server
The Django development server should not be used as a production server.

python manage.py runserver

This command is useful for development only.

In production, Django is usually served by an application server such as Gunicorn or uWSGI.

Nginx
Gunicorn
Django
PostgreSQL

A common request flow looks like this:

Client
  ↓
Nginx
  ↓
Gunicorn
  ↓
Django REST API
  ↓
Database

Nginx can handle HTTPS, static files, reverse proxying, and request forwarding to Gunicorn.

 

Example Gunicorn command
For a project where the Django settings module is inside config, a simple Gunicorn command could look like this:

gunicorn config.wsgi:application

In a real server, this is usually managed by a process manager or container system.

systemd
Docker
Docker Compose
Platform service manager
Cloud deployment platform

The exact setup depends on where you deploy the API.

 

Example deployment flow
A typical deployment flow might include these steps:

Push code to repository
Server pulls latest code
Install dependencies
Load environment variables
Run database migrations
Collect static files
Restart application server
Check logs
Test production endpoints

Commands may look like this:

pip install -r requirements.txt
python manage.py migrate
python manage.py collectstatic --noinput
python manage.py check --deploy

After this, restart the production service using your server or hosting platform tools.

 

Requirements file
Before deployment, create a requirements.txt file with your dependencies.

pip freeze > requirements.txt

This allows the production server to install the same Python packages used by your project.

Django
djangorestframework
django-filter
django-cors-headers
drf-spectacular
gunicorn
psycopg

Depending on your database and server setup, you may need additional packages.

 

Logging
In production, logs are essential.

They help you understand what is happening when something goes wrong.

Server errors
Failed requests
Authentication issues
Database problems
Unexpected exceptions
Performance problems

At minimum, make sure your hosting platform or server keeps application logs.

For larger projects, you may want structured logging and external monitoring tools.

 

Backups
A production API should have backups.

For most APIs, the database is the most important part to protect.

Database
Uploaded media files
Environment configuration
Deployment configuration

Backups should be automatic and tested.

A backup is only useful if you know how to restore it.

Before going live, make sure you understand how to restore the database if something goes wrong.

 

Monitoring
After deployment, the API should be monitored.

Is the API online?
Are requests failing?
Are response times increasing?
Is the database healthy?
Are there too many errors?
Is disk space running out?

Monitoring helps you detect problems before users report them.

For small projects, hosting platform logs may be enough.

For serious production systems, use proper monitoring and alerting tools.

 

Production checklist
Before deploying a Django REST API, review this checklist.

DEBUG is False
SECRET_KEY is stored securely
ALLOWED_HOSTS is configured
Environment variables are used
Production database is configured
Database backups exist
CORS is restricted to trusted origins
CSRF trusted origins are configured if needed
HTTPS is enabled
Secure cookie settings are reviewed
Authentication method is production-ready
Permissions and ownership are tested
Pagination is enabled for list endpoints
API documentation visibility is reviewed
Static files are collected
Migrations are applied
Logs are available
python manage.py check --deploy has been reviewed

This checklist does not cover every possible production scenario, but it gives a strong starting point.

 

Recommended production mindset
Deployment is not only about making the API available online.

It is also about making it safe, stable, and maintainable.

Keep secrets out of the codebase
Use HTTPS
Restrict access carefully
Validate all incoming data
Monitor errors
Keep dependencies updated
Backup important data
Test before deploying changes

A working API is good.

A secure and maintainable API is better.

 

What we have learned
In this post, we reviewed important deployment notes for Django REST APIs.

Production settings are different from development settings
DEBUG must be disabled in production
ALLOWED_HOSTS must include the production domains
Secrets should come from environment variables
Production APIs should use HTTPS
CORS should be restricted to trusted origins
Session, CSRF, and cookie settings need attention
Production databases need backups
Static files must be collected
The development server should not be used in production
Django provides check --deploy for deployment checks
Logs and monitoring are important after deployment

With this final step, our Django REST API series now covers the full journey from basic concepts to production considerations.

 

Series conclusion
Throughout this series, we built and improved a Django REST API step by step.

Introduction to REST APIs
Project setup
Models, serializers, and views
CRUD endpoints
APIView, generic views, and ViewSets
Routers and URL structure
Authentication
Permissions and ownership
Filtering, search, and ordering
Pagination
Validation and error handling
Testing APIs
Swagger/OpenAPI documentation
Deployment notes

This gives a solid foundation for building real APIs with Django REST Framework.

From here, a natural next step would be consuming this API from a frontend application, such as React.

React frontend → Django REST API