Unable to get nginx internal redirect to work

Balaji Kutty picture Balaji Kutty · Nov 20, 2015 · Viewed 8k times · Source

I'm using ubuntu 14.04 and running nginx 1.4.6 as reverse proxy server to talk to my django backend which runs on uwsgi. I'm unable to get the internal redirect to work, that is, the request does not reach django at all. Here is my nginx configuration /etc/nginx/site-enabled/default file. Please let me know what is wrong with my configuration.

server {
        listen 8080;
        listen 8443 default_server ssl;
        server_name localhost;
        client_max_body_size    50M;
        access_log      /var/log/nginx/nf.access.log;
        error_log       /var/log/nginx/nf.error_log debug;
        ssl_certificate      /etc/ssl/nf/nf.crt;
        ssl_certificate_key  /etc/ssl/nf/nf.key;
        location / {
                proxy_pass http://localhost:8000;
        }
        location /static/ {
                root /home/northfacing;
        }
        location /media/ {
                internal;
                root /home/northfacing;
        }
}

Adding my uwsgi configuration.

[uwsgi]
chdir=/home/northfacing/reia
module=reia.wsgi:application
master=True
pidfile=/home/northfacing/reia/reia-uwsgi.pid
vacuum=True
max-requests=5000
daemonize=/home/northfacing/reia/log/reia-uwsgi.log
http = 127.0.0.1:8000

Adding my uwsgi startup script

#!/bin/bash
USER="northfacing"
PIDFILE="/home/northfacing/reia/reia-uwsgi.pid"

function start(){
    su - ${USER} /bin/sh -c "source /home/northfacing/nfenv/bin/activate && exec uwsgi --pidfile=${PIDFILE} --master --ini /etc/init.d/reia-uwsgi.ini"
}

function stop(){
    kill -9 `cat ${PIDFILE}`
}

$1

/home/northfacing/nfenv is my python environment directory.

Answer

GwynBleidD picture GwynBleidD · Nov 20, 2015

If you want django to handle permissions for accessing your media files, first thing to do is to pass all requests into django. I'm assuming that /home/northfacing is your project root dir (dir where by default manage.py will be placed), your static files are collected into public/static subdirectory in your project and media files are stored in public/media.

Basing on that assumptions, here is basic configuration for that behaviour:

server {

    listen 8080;
    server_name localhost;

    client_max_body_size 50M;

    access_log      /var/log/nginx/nf.access.log;
    error_log       /var/log/nginx/nf.error_log debug;
    ssl_certificate      /etc/ssl/nf/nf.crt;
    ssl_certificate_key  /etc/ssl/nf/nf.key;

    root /home/northfacing/public/;

    location @default {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        include /etc/nginx/proxy_params;

        proxy_pass softwaremind_server;
        break;
    }

    location /static/ {
        try_files $uri @default; # just some simple default action, so you can show django's 404 page instead of nginx default
    }

    location /media/ {
        internal;
        error_page 401 403 404 = @default;
    }

    location / {
        try_files /maintenance.html @default; # you can disable whole page with simple message simply by creating maintenance.html with that message
    }
}

Simple explanation: all requests to urls in /media/ are treated as internal, so nginx will serve 404, 401 or 403 error if entered directly. But in that location our proxy server (django in that case) is set as handler, so it will get request and will be able to check if user have access rights.

If there is no access, django can throw it's own error. If acces is granted, django should return an empty response with X-Accel-Redirect set to file path. Simple view for that can look like this:

class MediaView(View):

    def get(self, request):

        if not request.user.is_authenticated():
            raise Http404

        response = HttpResponse()
        response.status_code = 200
        response['X-Accel-Redirect'] = request.path

        # all this headers are cleared-out, so nginx can serve it's own, based on served file
        del response['Content-Type']
        del response['Content-Disposition']
        del response['Accept-Ranges']
        del response['Set-Cookie']
        del response['Cache-Control']
        del response['Expires']
        return response

And in urls.py:

    url(r'^media/', MediaView.as_view(), name="media")