Lazily serving Django static content with NGINX - django

Let's say I've got Django code rooted in /var/my_app. I've got a MEDIA_ROOT configured at /var/my_app/files, and MEDIA_URL is /var/my_app/files at /files. Serving these files through UWSGI (or gunicorn, etc.) works fine.
Now I am trying to use the NGINX try_files directive to serve them without hitting UWSGI. Here's what I've got:
location /files/* {
try_files /var/my_app$uri $uri;
}
Wherein my understanding is that if I am attempting to access $hostname/files/photos/thumbs/file.jpg, the above location matches, $uri is /files/photos/thumbs/file.jpg, NGINX tests /var/my_app/files/photos/thumbs/file.jpg and should return the file if it exists.
Instead, when I access that URI, I see the request passed through to UWSGI in the UWSGI log, even though the file exists at /var/my_app/files/photos/thumbs/file.jpg. What am I doing wrong? I read the docs on try_files, but clearly I am misunderstanding something.
For reference, here is the Django location block:
location / {
include uwsgi_params;
uwsgi_param UWSGI_SETENV DATABASE_URL=[redacted]
uwsgi_pass 127.0.0.1:3034;
}
Added Context
This configuration is targeted at lazily serving Django-generated thumbnails (using easy-thumbnails) if they do not yet exist.

Related

Why is static content request going to uwsgi?

I'm setting up my Django project to work with uwsgi and nginx. For static content, I have the following in my nginx.conf:
location /static {
alias /Users/me/mystatic; # your Django project's static files - amend as required
}
I have set STATIC_ROOT to /Users/me/mystatic and called collectstatic to copy all the static files into that directory. In my uwsgi log, I see GET requests for the static content. Since nginx is supposed to serve the static content, why is the GET request sent to uwsgi?
Make sure that STATIC_URL is set as "/static/".
The nginx conf should look something like this:
server {
listen 80 ;
server_name XXXX;
client_max_body_size 4G;
location /static/ {
alias <path-to-collectstatic>
}
location / {
include uwsgi_params;
uwsgi_pass unix:/tmp/uwsgi_web.sock;
}
}
Notice the appending / in location block for static
Last thing, make sure that application server is running with DEBUG as False
The issue was that I had placed my own nginx.conf in /usr/local/etc/nginx/sites-enabled, not /usr/local/etc/nginx/servers. In /usr/local/etc/nginx/nginx.conf, include servers/*; is at the end. Therefore, UWSGI was still serving the static files. I followed the django-nginx-uwsgi tutorial, and I assumed I had to create a new sites-enabled directory. I am using OS X.
I see on Linux, though, /etc/nginx/nginx.conf has include include /etc/nginx/sites-enabled/*; at the end, so the steps in the tutorial are applicable on Linux.
The extra / at the end of /static doesn't make a difference, but it doesn't hurt to have it at the end of all paths.

Django project deployment: cannot load static files

I'm deploying a Django project using:
virtualenv
nginx
gunicorn
following this tutorial: https://www.digitalocean.com/community/tutorials/how-to-deploy-a-local-django-app-to-a-vps
My configuration
django settings
STATIC_ROOT = os.path.join(BASE_DIR, "static/")
STATIC_URL = '/static/'
/etc/nginx/sites-available/esmart2
GNU nano 2.0.9 File: /etc/nginx/sites-available/esmart2
server {
server_name 192.168.30.17;
access_log off;
location /static/ {
alias /new_esmart/esmart2/static/;
}
location / {
proxy_pass http://127.0.0.1:8001;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Real-IP $remote_addr;
add_header P3P 'CP="ALL DSP COR PSAa PSDa OUR NOR ONL UNI COM NAV"';
}
}
Running
(esmart_env) [root#eprssrv09 esmart2]# /new_esmart/esmart_env/bin/gunicorn --bind 192.168.30.17:8001 esmart2.wsgi:application
My Django project is running but:
Not Found: /static/admin/css/login.css
Any advice?
UPDATE 1
I think that nginx settings are been avoided:
deleting any reference in 'sites-enabled' folder, django project runs and the problem concerning static files remain.
UPDATE 2
if in Django settings debug=True I get
Not Found: /static/admin/css/base.css
Not Found: /static/admin/css/login.css
Not Found: /static/admin/css/base.css
It seems I solved BUT I don't think is the better solution. I mean, it's a good solution depending on the project requirements: mine allows using this solution.
install package
pip install whitenoise
on wsgi.py I added:
from whitenoise.django import DjangoWhiteNoise
application = get_wsgi_application()
application = DjangoWhiteNoise(application)
This way nginx site conf is ignored (but I think they had already ignored!)
Django WhiteNoise documentation
With a couple of lines of config WhiteNoise allows your web app to
serve its own static files, making it a self-contained unit that can
be deployed anywhere without relying on nginx, Amazon S3 or any other
external service. (Especially useful on Heroku, OpenShift and other
PaaS providers.)
It’s designed to work nicely with a CDN for high-traffic sites so you
don’t have to sacrifice performance to benefit from simplicity.
WhiteNoise works with any WSGI-compatible app but has some special
auto-configuration features for Django.
WhiteNoise takes care of best-practices for you, for instance:
Serving compressed content (gzip and Brotli formats, handling
Accept-Encoding and Vary headers correctly) Setting far-future cache
headers on content which won’t change Worried that serving static
files with Python is horribly inefficient? Still think you should be
using Amazon S3? Have a look at the Infrequently Asked Questions.
It looks to me like the issue is two-fold:
It looks like you may have an error in your nginx config. Is new_esmart/ really in your root directory/ or is it somewhere like /home/sam/new_esmart/? If it is not in your root directory, make sure to provide the complete path.
It looks like your STATIC_ROOT will point to path/to/new_esmart/static/ rather than /new_esmart/esmart2/static/. Keep in mind that STATIC_ROOT is the place where your static files will be copied to when you run collectstatic not the place you personally place static files when creating them initially.
So what you probably need to do is confirm that you really want your static files collected to /actual/path/to/new_esmart/static/ and then use that same path in your nginx config.
Also, as discussed in the comments, you could try adding listen 8001; to your nginx config:
server {
listen 8001;
server_name 192.168.30.17;
access_log off;
...

Disable execution of static files as code, with Django, Gunicorn, Nginx

Django 1.8 docs state that "If you are serving your own static files, be sure that handlers like Apache’s mod_php, which would execute static files as code, are disabled." How do I disable any such handlers that may exist for Gunicorn + Nginx?
Nginx by default does not have any handlers doing that. If there are no handlers configured in your server section in nginx configuration, there is nothing to worry about. If it's clean nginx install, there is nothing to worry about.
Also examine your nginx config if there is only one way to run python code - via gunicorn and django's wsgi.py file.
Basically the django devs are warning you about a well known web vulnerability.
https://www.owasp.org/index.php/Unrestricted_File_Upload
As nginx can only serve static files it must use an app server for dynamic pages, a handler (php-fpm, gunicorn, uwsgi).
Could be the case that you are using an old nginx's PHP config (with php-fpm as a handler), and you added gunicorn for serving a django app, then a user could manage to upload a PHP file to django's media dir and later execute it through your legacy PHP handler.
This is a very odd case, there are no chances this could happen to a fresh site, you never add a PHP handler for a django app.
A PHP handler looks like this:
location ~ \.php$ {
expires off;
fastcgi_pass 127.0.0.1:9001;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_keep_conn on;
}

Django to serve static files with exceptions

In my production environment I have Nginx serving static files for my Django application, but while developing I let Django so the work.
I need to have Nginx serving all static files but those in a certain subdirectory. So, Django side, I need to intercept that directory and treat it differently.
How can I make Django dev server intercept all the calls to /static/* but not those to /static/myspecialfiles/*´ and hence write a url route to handle the GET calls to said/static/myspecialfiles/*´?
You need to tackle this in two areas, Django and nginx.
For Django, since running a local setup (when DEBUG=True) already serves static content from STATIC_ROOT automatically, just add a URL conf for your special files:
# in urls.py
url(r'^/static/specialfiles/', SpecialView.as_view(), name='special'),
Then, in your nginx conf, make sure you ignore that path so it actually reaches Django:
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://localhost:8000;
}
location /static/specialfiles/ {
proxy_pass http://localhost:8000;
}
location /static/ {
alias /home/ubuntu/dev/example/static/;
}
}
Since the locations are parsed sequentially, your special files will hit the django block before the generic static block.
But really you'd be best to just move your special files somewhere else.

Nginx is not serving Django static files

I just configured my first django server for a very basic django-website. I'm using Django 1.4 and Nginx 1.0.14 with supervisor and gunicorn.
The problem is that Nginx is not serving the static files as suposed, but I don't know the why because is my first time using it.
This is the path where really lives my static files:
/home/cristian/envs/santalupe.com/santalupe/santalupe/static
And this is the setting I've in my nginx config file:
# Django admin media.
location /media/ {
autoindex on;
alias /home/cristian/envs/santalupe.com/lib/python2.7/site-packages/django/contrib/admin/static/;
}
# Site media
location /static/ {
autoindex on;
alias /home/cristian/envs/santalupe.com/santalupe/santalupe/static/;
}
Please let me know what I need to do in this case because I have not idea about the real problem.
You aren't serving the admin media from Nginx, just normal media. Try something like:
location /admin/media/ {
# this changes depending on your python version
root /home/cristian/envs/santalupe.com/lib/python2.7/site-packages/django/contrib;
}
Note how there's no trailing slash and the path ends at contrib. I'm using almost exactly this code successfully in production.
Maybe manage.py collectstatic on server help you?
Here is good description on right solution https://docs.djangoproject.com/en/dev/howto/static-files/#serving-static-files-in-production