Below are the nginx.conf
and gunicorn.start.sh
files that I use for the Django application that I'm running (which, by the way, I didn't write).
When I make any one-time change to dynamic content, then repeatedly refresh the browser, the rendered content switches/flaps variously between PRE and POST changes made. But it should just update once, and never again.
The only thing that momentarily fixes the issue is restarting
gunicorn
:myapp$ sudo supervisorctl restart gunicorn
But this only works until the next change to dynamic content. No good.
I've tried numerous browsers, and it happens even when I clear their caches. Makes no difference.
- I also temporarily tried
uWSGI
instead ofgunicorn
(just to see), and the same issue occurs. - A
tail -f gunicorn.access.log
during browser refreshes shows HTTP statuses that vary between200
(content was downloaded) and304
(you already have the content; redirecting you to your local cache). But dynamic content shouldn't be cached. Note that when running the application using the development server, like this:
myapp$ python manage.py runserver --settings live 0.0.0.0:8080
the issue doesn't occur; but neither does this use
nginx
norgunicorn
.
Any ideas?
I've tried everything I know, but don't know what is causing this flapping on dynamic content.
I don't see anything below that would cause this re-rendering of before/after dynamic content change (i.e. caching).
Thank you in advance!
#! /bin/bash
#
set -ue
#
# ==========================================================================
# -- The Django myapp Application UNIX HOME accunt location.
# -- The Django myapp Application location.
# ==========================================================================
export HOME="/home/myapp/"
export MYAPP_HOME="${HOME}/MYAPP/"
cd ${MYAPP_HOME} # The CWD *must* be this when this is launched!
# ==========================================================================
# ==========================================================================
# Gunicorn binary to run. A particular Gunicorn version is needed for this
# APP, so we pip(1m) installed it into its Python-2 virtual environment.
# ==========================================================================
GUNICORN="${HOME}/.virtualenvs/myapp/bin/gunicorn"
# ==========================================================================
# ==========================================================================
# -- Gunicorn PID-file, ACCESS-log and ERROR-log locations.
# -- Where to BIND Gunicorn to. We're using a UNIX DOMAIN (not TCP) SOCKET.
# ==========================================================================
ACCESS_LOG="${MYAPP_HOME}/ROOTFS.d/var/log/gunicorn.access.log"
ERROR_LOG="${MYAPP_HOME}/ROOTFS.d/var/log/gunicorn.error.log"
PID_FILE="${MYAPP_HOME}/ROOTFS.d/var/run/gunicorn.pid"
#
BIND="unix:/var/run/myapp.d/myapp.sock" # Communicate with nginx via socket.
# ==========================================================================
# IMPORTANT: gunicorn only serves up dynamic content; not static or CSS content.
# The nginx part of the stack is needed for the static content.
# https://stackoverflow.com/questions/20175243/django-gunicorn-not-load-static-files
# ==========================================================================
# ==========================================================================
# Gunicorn performance tunable parameters.
# ==========================================================================
NUM_WORKERS=3 # Number of worker processes.
MAX_REQUESTS=1000 # Number or requests to serve.
# ==========================================================================
# ==========================================================================
# -- WSGI Python module that starts the process.
# -- Name of the application.
# ==========================================================================
DJANGO_SETTINGS_MODULE='live' # ./live.py (Our settings file.).
DJANGO_WSGI_MODULE="wsgi"
NAME="myapp"
# ==========================================================================
# ==========================================================================
# Launch the WSGI Application Web Service.
# ==========================================================================
echo "gunicorn(1) starting with DJANGO_SETTINGS_MODULE=${DJANGO_SETTINGS_MODULE}"
# ==========================================================================
exec $GUNICORN ${DJANGO_WSGI_MODULE}:application \
--env DJANGO_SETTINGS_MODULE=${DJANGO_SETTINGS_MODULE} \
--name $NAME \
--workers $NUM_WORKERS \
--max-requests $MAX_REQUESTS \
--bind $BIND \
--access-logfile ${ACCESS_LOG} \
--error-logfile ${ERROR_LOG} \
--pid ${PID_FILE} \
--user myapp
# ==========================================================================
upstream myapp_server {
server unix:/var/run/myapp.d/myapp.sock fail_timeout=0;
# =============================================================
# Path to DYNAMIC (NOT STATIC) content (when using GUNICORN).
# =============================================================
}
# When no server names match then crash out.
server {
return 404;
}
server {
listen 80;
server_name example.com 103.11.65.155;
return 301 https://example.com$request_uri; # Rewrites HTTP to HTTPS
#rewrite ^ https://example.com permanent;
}
server {
# ==========================================================================
listen 443 ssl; # managed by Certbot
server_name example.com 103.11.65.155;
keepalive_timeout 25s;
client_max_body_size 5M;
root /home/myapp/MYAPP/;
# ==========================================================================
# Path used for the static-files location-block below.
# NOT for the dynamic-content location-block below.
# ==========================================================================
# ==========================================================================
# SSL Certificate for 'example.com' (not for 'www.example.com').
# ==========================================================================
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
# ==========================================================================
# ==========================================================================
access_log /home/myapp/MYAPP/ROOTFS.d/var/log/nginx.access.log;
error_log /home/myapp/MYAPP/ROOTFS.d/var/log/nginx.error.log;
# ==========================================================================
# ==========================================================================
# Location block for STATIC FILES ...
# ==========================================================================
# The string in between /xyx/ must be the same as the value for
# settings.STATIC_URL (in settings.py). Typically this is '/static/', but
# for MYAPP's it is '/m/'. We redundantly define one for '/static/'
# too, just in case there's a hidden dependency on that traditional path.
# ==========================================================================
location /m/ {
alias /home/myapp/MYAPP/static/;
autoindex on;
expires max;
add_header Pragma public;
add_header Cache-Control "public";
access_log off;
}
location /static/ {
alias /home/myapp/MYAPP/static/;
autoindex on;
expires max;
add_header Pragma public;
add_header Cache-Control "public";
access_log off;
}
# ==========================================================================
# ==========================================================================
# Location block for DYNAMIC CONTENT served by GUNICORN ...
# ==========================================================================
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://myapp_server;
proxy_buffering off;
}
# ==========================================================================
}
server {
# =========================================================
# Redirect to WWW --to--> Non-WWW.
# =========================================================
listen 80;
server_name www.example.com;
return 301 $scheme://example.com$request_uri;
# =========================================================
# ==========================================================================
# SSL Certificate for 'www.example.com' (not for 'example.com').
# ==========================================================================
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/www.example.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/www.example.com/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
# ==========================================================================
}