Let's quickly make static files work. You might not understand perfectly what we're doing, but it will become very clear afterwards.
First, add these statements to
/etc/opt/$DJANGO_PROJECT/settings.py:
STATIC_ROOT = '/var/cache/$DJANGO_PROJECT/static/' STATIC_URL = '/static/'
Remember that after each change to your settings you should, in theory, recompile:
/opt/$DJANGO_PROJECT/venv/bin/python -m compileall \
/etc/opt/$DJANGO_PROJECTIt's not really a big deal if you forget to recompile, but we will deal with that later.
Second, create directory /var/cache/$DJANGO_PROJECT/static/:
mkdir -p /var/cache/$DJANGO_PROJECT/staticThe -p parameter tells mkdir to create not only the directory,
but, if needed, its parents as well.
Third, run collectstatic:
PYTHONPATH=/etc/opt/$DJANGO_PROJECT:/opt/$DJANGO_PROJECT \
/opt/$DJANGO_PROJECT/venv/bin/python \
/opt/$DJANGO_PROJECT/manage.py collectstatic \
--settings=settingsThis will copy all static files to the directory we specified in STATIC_ROOT. Don't worry if you don't understand it clearly, we will explain it in a minute.
Change /etc/nginx/sites-available/$DOMAIN to the following,
which only differs from the previous version in that the new location
/static {} block has been added at the end:
server {
listen 80;
listen [::]:80;
server_name $DOMAIN www.$DOMAIN;
root /var/www/$DOMAIN;
location / {
proxy_pass http://localhost:8000;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
client_max_body_size 20m;
}
location /static/ {
alias /var/cache/$DJANGO_PROJECT/static/;
}
}Don't forget to execute service nginx reload after that.
Now let's try to see if it works. Stop the Django development server if it is running on the server. Open your browser and visit http://$DOMAIN/. nginx should give you a 502. This is expected, since the backend is not working.
But now try to visit http://$DOMAIN/static/admin/img/icon_searchbox.png.
If you have django.contrib.admin in INSTALLED_APPS, it should
get a search icon (if you don't use django.contrib.admin, pick up
another static file that you expect to see, or browse the directory
/var/cache/$DJANGO_PROJECT/static).
:numref:`how_static_files_work_nginx` explains how this works.
The only thing that remains to clear up is what exactly these
location blocks mean. location /static/ means that the
configuration inside the block shall apply only if the path of the URL
begins with /static/. Likewise, location / applies if the path
of the URL begins with a slash. However, all paths begin with a slash,
so if the path begins with /static/ both location blocks match
the URL. Nginx only uses one location block. The rules with which
nginx chooses the location block that shall apply are complicated
and are described in the documentation for location, but in this
particular case, nginx chooses the longest matching prefix; so if the
path begins with /static/, nginx will choose location /static/.
Change /etc/apache2/sites-available/$DOMAIN.conf to the following:
<VirtualHost *:80>
ServerName $DOMAIN
ServerAlias www.$DOMAIN
DocumentRoot /var/www/$DOMAIN
ProxyPass /static/ !
ProxyPass / http://localhost:8000/
ProxyPreserveHost On
RequestHeader set X-Forwarded-Proto "http"
Alias /static/ /var/cache/$DJANGO_PROJECT/static/
<Directory /var/cache/$DJANGO_PROJECT/static/>
Require all granted
</Directory>
</VirtualHost>Don't forget to execute service apache2 reload after that.
Now let's try to see if it works. Stop the Django development server if it is running on the server. Open your browser and visit http://$DOMAIN/. Apache should give you a 503. This is expected, since the backend is not working.
But now try to visit http://$DOMAIN/static/admin/img/icon_searchbox.png.
If you have django.contrib.admin in INSTALLED_APPS, it should
get a search icon (if you don't use django.contrib.admin, pick up
another static file that you expect to see, or browse the directory
/var/cache/$DJANGO_PROJECT/static).
:numref:`how_static_files_work_apache` explains how this works.
Now let's examine how the configuration above produces these results.
The directive ProxyPass / http://localhost:8000/ tells Apache that,
if the URL path begins with /, then it should pass the request to
the backend. All URL paths begin with /, so the directive always
matches. But there is also the directive ProxyPass /static/ !, which
will match paths starting with /static/. When there are many
matching ProxyPass directives, the first one wins; so for path
/static/admin/img/icon_searchbox.png, ProxyPass /static/ ! wins.
The exclamation mark means "no proxy passing", so the directive means
"when a URL path begins with /static/, do not pass it to the
backend". Since it is not going to be passed to the backend, Apache
would normally combine it with the DocumentRoot and would thus try
to return the file
/var/www/$DOMAIN/static/admin/img/icon_searchbox.png, but the
Alias directive tells it to get
/var/cache/$DJANGO_PROJECT/static/admin/img/icon_searchbox.png
instead. By default, Apache will refuse to access files in directories
other than DocumentRoot, and will return 403, "Forbidden", in
requests to access them; so we add the directive Require all granted
for the static files directory, which means "everyone has permission to
read the files".
Media files are similar to static files, so let's go through them
quickly. We will store them in /var/opt/$DJANGO_PROJECT/media.
mkdir /var/opt/$DJANGO_PROJECT/media
chown $DJANGO_USER /var/opt/$DJANGO_PROJECT/mediaOne of the differences with static files is that we changed the
ownership of /var/opt/$DJANGO_PROJECT/media to $DJANGO_USER. The
reason is that Django needs to be writing there each time the user
uploads a file or requests to delete a file.
Add the following to /etc/opt/$DJANGO_PROJECT/settings.py:
MEDIA_ROOT = '/var/opt/$DJANGO_PROJECT/media/' MEDIA_URL = '/media/'
For nginx, add the following to /etc/nginx/sites-available/$DOMAIN:
location /media/ {
alias /var/opt/$DJANGO_PROJECT/media/;
}For Apache, add the following before ProxyPass /:
ProxyPass /media/ !and the following at the end of the VirtualHost block:
Alias /media/ /var/opt/$DJANGO_PROJECT/media/
<Directory /var/opt/$DJANGO_PROJECT/media/>
Require all granted
</Directory>Recompile your settings, reload the web server, and it's ready.
Your static and media files are now served properly by the web server instead of the Django development server, and I hope you understand clearly what we've done. Let's take a break and discuss the file locations that I've chosen:
| Program files | /opt/$DJANGO_PROJECT |
| Virtualenv | /opt/$DJANGO_PROJECT/venv |
| Media files | /var/opt/$DJANGO_PROJECT/media |
| Static files | /var/cache/$DJANGO_PROJECT/static |
| Configuration | /etc/opt/$DJANGO_PROJECT |
There are a couple more that we haven't seen yet, but the above more or less tell the whole story.
Many people prefer a much simpler setup instead. They put everything related to their project in a single directory, which is that of their repository root, like this:
| Program files | /srv/$DJANGO_PROJECT |
| Virtualenv | /srv/$DJANGO_PROJECT/venv |
| Media files | /srv/$DJANGO_PROJECT/media |
| Static files | /srv/$DJANGO_PROJECT/static |
| Configuration | /srv/$DJANGO_PROJECT/$DJANGO_PROJECT |
Although this setup seems simpler, I have preferred the other one for
several reasons. The first one is purely educational. When you get too
used to the simple setup, you might configure always the same
STATIC_ROOT, without really understanding what it does. The clean
separation of directories should also have helped you get a grip on
PYTHONPATH and DJANGO_SETTINGS_MODULE.
Separating in many directories is also cleaner and applies to many
different situations. If a Django application is packaged as a .deb
package, or as a pip-installable package, the tweak required with the
split directories scheme is minimal.
Finally, separating the directories makes it easier to backup only what
is needed. My backup solution (which we will see in the chapters about
recovery) may exclude /opt and /var/cache from the backup.
Since the static files can be regenerated, there is no need to back them
up.
Set
STATIC_ROOTto/var/cache/$DJANGO_PROJECT/static/.Set
STATIC_URLto/static/.Set
MEDIA_ROOTto/var/opt/$DJANGO_PROJECT/media/.Set
MEDIA_URLto/media/.Run
collectstatic.In nginx, set
location /static/ { alias /var/cache/$DJANGO_PROJECT/static/; }; likewise for media files.In Apache, add
ProxyPass /static/ !beforeProxyPass /, and addAlias /static/ /var/cache/$DJANGO_PROJECT/static/ <Directory /var/cache/$DJANGO_PROJECT/static/> Require all granted </Directory>
Likewise for media files.

