Skip to content

Commit c17c4a4

Browse files
bsolomon1124bzaczynskispacerest
authored
'Deploy a Django App With Gunicorn, Nginx, & HTTPS' companion repo (#121)
* 'Django Security Headers in Practice' companion repo * Rename per PR template guidelines * Remove logging call (redundant with httpie) * Additional header-related settings * Use a new SECRET_KEY * Gunicorn configuration * Spacing * Update ALLOWED_HOSTS * Typo * SK gitignore * Specify project requirements * Add <link> to normalize.css * Add CSP to middleware * Set Content-Security Policy (CSP) * Typo * HSTS * Increase SECURE_HSTS_SECONDS to 1 year * Nginx configuration file * Make the base path reachable * Add a JS static file for collectstatic * Path fix * Update nginx config * Remove localhost * Don't specify unncessary path in STATICFILES_DIRS * Turn of DEBUG * Bring in line with article * Call the toy project 'django-gunicorn-nginx' * black * Consolidate config under subdirectory * Consolidate requirements under subdirectory * Shorten requirement file names * Pin to Django 3 * Shorten gunicorn config file names * Loosely pin requirements * Build out Gunicorn config * Add util script for installing certbot * Remove extraneous setting * Use a template * Add to INSTALLED_APPS; ALLOWED_HOSTS wildcard * Remove outdated script * Turn off DEBUG * Include local and remote JS in template * Update nginx sites-available config * Update for article 0058e4b4 * Top-level sample project folders should contain a README.md file * Pin requirements to minor rather than major version * Add a description and intro howto in the readme * Language edit Co-authored-by: Bartosz Zaczyński <[email protected]> Co-authored-by: Sadie Parker <[email protected]>
1 parent 0c6cf10 commit c17c4a4

File tree

24 files changed

+346
-0
lines changed

24 files changed

+346
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ coverage.xml
5656
# Django stuff:
5757
*.log
5858
local_settings.py
59+
*.sqlite3
60+
nohup.out
5961

6062
# Flask stuff:
6163
instance/

django-gunicorn-nginx/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.DJANGO_SECRET_KEY

django-gunicorn-nginx/README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Securely Deploy a Django App With Gunicorn, Nginx, & HTTPS
2+
3+
This directory contains the Django project created in the tutorial [Securely Deploy a Django App With Gunicorn, Nginx, & HTTPS](https://realpython.com/django-nginx-gunicorn/).
4+
5+
It requires Python 3.8 or higher and Django 3.2.
6+
7+
Here's what this repository and the tutorial illustrate:
8+
9+
- How you can take your Django app **from development to production**
10+
- How you can **host your app** on a real-world public domain
11+
- How to introduce **Gunicorn** and **Nginx** into the request and response chain
12+
- How **HTTP headers** can fortify your site's HTTPS security
13+
14+
To get started, install requirements and migrate your database:
15+
16+
```bash
17+
python3 -m venv env
18+
source env/bin/activate
19+
python3 -m pip install -r requirements/prod.txt
20+
echo "export SECRET_KEY='$(openssl rand -hex 40)'" > .DJANGO_SECRET_KEY
21+
source .DJANGO_SECRET_KEY
22+
python3 manage.py migrate
23+
python3 manage.py check
24+
```
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
server_tokens off;
2+
access_log /var/log/nginx/supersecure.access.log;
3+
error_log /var/log/nginx/supersecure.error.log;
4+
5+
server {
6+
listen 80 default_server;
7+
return 444;
8+
}
9+
10+
server {
11+
server_name .supersecure.codes;
12+
listen 80;
13+
return 307 https://$host$request_uri;
14+
}
15+
16+
server {
17+
18+
location / {
19+
proxy_pass http://localhost:8000;
20+
proxy_set_header Host $host;
21+
proxy_set_header X-Forwarded-Proto $scheme;
22+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
23+
proxy_redirect off;
24+
}
25+
26+
location /static {
27+
autoindex on;
28+
alias /var/www/supersecure.codes/static/;
29+
}
30+
31+
listen 443 ssl; # managed by Certbot
32+
ssl_certificate /etc/letsencrypt/live/www.supersecure.codes/fullchain.pem; # managed by Certbot
33+
ssl_certificate_key /etc/letsencrypt/live/www.supersecure.codes/privkey.pem; # managed by Certbot
34+
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
35+
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
36+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
"""Gunicorn *development* config file"""
2+
3+
# Django WSGI application path in pattern MODULE_NAME:VARIABLE_NAME
4+
wsgi_app = "project.wsgi:application"
5+
# The granularity of Error log outputs
6+
loglevel = "debug"
7+
# The number of worker processes for handling requests
8+
workers = 2
9+
# The socket to bind
10+
bind = "0.0.0.0:8000"
11+
# Restart workers when code changes (development only!)
12+
reload = True
13+
# Write access and error info to /var/log
14+
accesslog = errorlog = "/var/log/gunicorn/dev.log"
15+
# Redirect stdout/stderr to log file
16+
capture_output = True
17+
# PID file so you can easily fetch process ID
18+
pidfile = "/var/run/gunicorn/dev.pid"
19+
# Daemonize the Gunicorn process (detach & enter background)
20+
daemon = True
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
"""Gunicorn *production* config file"""
2+
3+
import multiprocessing
4+
5+
# Django WSGI application path in pattern MODULE_NAME:VARIABLE_NAME
6+
wsgi_app = "project.wsgi:application"
7+
# The number of worker processes for handling requests
8+
workers = multiprocessing.cpu_count() * 2 + 1
9+
# The socket to bind
10+
bind = "0.0.0.0:8000"
11+
# Write access and error info to /var/log
12+
accesslog = "/var/log/gunicorn/access.log"
13+
errorlog = "/var/log/gunicorn/error.log"
14+
# Redirect stdout/stderr to log file
15+
capture_output = True
16+
# PID file so you can easily fetch process ID
17+
pidfile = "/var/run/gunicorn/prod.pid"
18+
# Daemonize the Gunicorn process (detach & enter background)
19+
daemon = True

django-gunicorn-nginx/manage.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/usr/bin/env python
2+
"""Django's command-line utility for administrative tasks."""
3+
import os
4+
import sys
5+
6+
7+
def main():
8+
"""Run administrative tasks."""
9+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings")
10+
try:
11+
from django.core.management import execute_from_command_line
12+
except ImportError as exc:
13+
raise ImportError(
14+
"Couldn't import Django. Are you sure it's installed and "
15+
"available on your PYTHONPATH environment variable? Did you "
16+
"forget to activate a virtual environment?"
17+
) from exc
18+
execute_from_command_line(sys.argv)
19+
20+
21+
if __name__ == "__main__":
22+
main()

django-gunicorn-nginx/myapp/__init__.py

Whitespace-only changes.

django-gunicorn-nginx/myapp/admin.py

Whitespace-only changes.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from django.apps import AppConfig
2+
3+
4+
class MyappConfig(AppConfig):
5+
default_auto_field = "django.db.models.BigAutoField"
6+
name = "myapp"

0 commit comments

Comments
 (0)