13

I have already gone through some previous threads: How do I set subdirectory in nginx with Django how to deploy django under a suburl behind nginx Serving flask app on subdirectory nginx + uwsgi

The basic lesson is that you should only need to configure your site(s-available) to achieve this. I have now tried various permutations of

server {
    listen 80;
    server_name www.example.com;

    location = /favicon.ico { 
        access_log off; 
        log_not_found off; 
    }

    location /static/ {
        root /path/to/project;
    }

    location /project/ {
        root            /path/to/project;
        include         /etc/nginx/uwsgi_params;
        uwsgi_param     SCRIPT_NAME /project;
        uwsgi_modifier1 30;
        uwsgi_param PATH_INFO "$1";
        uwsgi_pass      unix:/tmp/project.sock;
    }
}

Everything runs perfectly when I define location to be "/" (and remove SCRIPT_NAME, modifier1, PATH_INFO and root doesn't matter. But trying to use a subdirectory always results in Page not found (404):

Request URL:    http://www.example.com/project/project

(edit) It's ADDING a directory to the request. What am I not figuring out?

(tried forced_script_name - should't have to use this and gives other types of headaches - and uwsgi config setting)

EDIT:

location /project/ {
    root            /path/to/project;
    include         /etc/nginx/uwsgi_params;
    uwsgi_param     SCRIPT_NAME /project;
    uwsgi_pass      unix:/tmp/project.sock;
}

Does not work ... The socket is there and works when I configure for / - I just can't see what I'm missing.

UPDATE:

location ~ /project(?<path_info>/.*|$) {
    include         /etc/nginx/uwsgi_params;
    uwsgi_pass      unix:/tmp/project.sock;
    uwsgi_param     PATH_INFO $path_info;
    uwsgi_param     SCRIPT_NAME /project;
}

This loads up the site but all links point to http://example.com/link/to/something instead of http://example.com/project/link/to/something

RAM
  • 2,257
  • 2
  • 19
  • 41
Bjorn
  • 455
  • 4
  • 13

5 Answers5

13

The nginx uwsgi_modifier1 is deprecated in uWSGI.

Your goal is to be able to host a wsgi app from anywhere without the app needing to be adjusted to account for where it's being served from.

The current method for doing this in uWSGI is to map mountpoints for each URI app combination like so:

[uwsgi]
socket = 127.0.0.1:3031
; mount apps
mount = /app1=app1.py
mount = /app2=app2.py
; rewrite SCRIPT_NAME and PATH_INFO accordingly
manage-script-name = true

Hosting multiple apps in the same process (aka managing SCRIPT_NAME and PATH_INFO)

mount can take the place of module

For Django specifically,

; Before
module = django_app.wsgi:application
; After
mount = /django_app=django_app.wsgi:application
manage-script-name = true
shanemgrey
  • 2,280
  • 1
  • 26
  • 33
  • Finally! After much searching, I found your solution fit. Thank you! – clapas Jul 09 '17 at 14:42
  • 3
    Glad to be of help. Getting uWSGI running right was one of the hardest things I've ever done ;-) – shanemgrey Jul 09 '17 at 15:52
  • Don't forget to always use "url_for()". For example, redirect(url_for('myroute')) instead of just redirect('/myroute'). Also, in your html files, use href={{ url_for('myroute') }} and action={{ url_for('myroute') }}. This stupid mistake cost me a whole day of frustration. – thedude Apr 29 '22 at 13:09
  • I like this one! As I understand, (kind of expensive) PCRE library isn't invoked here in opposite to `route-run=fixpathinfo:` method given in another answer (however, both are workable). – Ivan Shatsky Jun 15 '22 at 20:07
1

Cleanest Method for Latest Nginx/uWSGI Versions

Since uwsgi_modifier1 30 is removed in the latest versions and I felt like the mount-point stuff was too hacky, I had to use a newer method to serve Django in a subdirectory:

uWSGI config:

[uwsgi]
socket =        /tmp/project.sock

# Requires PCRE support compiled into uWSGI
route-run =     fixpathinfo:

Nginx config:

location /project {
    include         /etc/nginx/uwsgi_params;
    uwsgi_pass      unix:/tmp/project.sock;
    uwsgi_param     SCRIPT_NAME /project; # Pass the URL prefix to uWSGI so the "fixpathinfo:" route-rule can strip it out
}

NOTE: fixpathinfo: requires PCRE support to be compiled into uWSGI.

So if things aren't working, try installing libpcre and libpcre-dev, then recompile uwsgi with pip install -I --no-cache-dir uwsgi. uWSGI's internal routing subsystem requires the PCRE library to be installed before uWSGI is compiled/installed. More information on uWSGI and PCRE.

Shane
  • 489
  • 4
  • 9
0

First of all, remove uwsgi_modifier1 30;. Django will handle SCRIPT_NAME by itself and don't need to have PATH_INFO rewritten by uWSGI. It can be harmful if SCRIPT_NAME isn't removed from headers by uWSGI.

Secondly, remove uwsgi_param PATH_INFO "$1"; from nginx config. PATH_INFO is already defined in uwsgi_params file, and it should be $document_uri (as it is in uwsgi_params), not $1 if you're passing SCRIPT_NAME to django.

After that tweaks, django should treat SCRIPT_NAME as URL prefix and will adjust url dispatcher and url reversing to that.

GwynBleidD
  • 20,081
  • 5
  • 46
  • 77
  • 1
    End results are: location /project/ { root /path/to/project; include /etc/nginx/uwsgi_params; uwsgi_param SCRIPT_NAME /project; uwsgi_pass unix:/tmp/project.sock; } (after restarting nginx and uwsgi) - same results = request URL http://www.example.com/project/project This when I am trying to browse http://www.example.com/project – Bjorn Mar 04 '16 at 13:43
  • Why is it _adding_ a directory to there request? – Bjorn Mar 07 '16 at 08:59
0

If the application is so simple that a simple "/prefix" can be added to one line in urls.py then I prefer this simple solution without anything more.

Otherwise the "/prefix" must be appended to the domain column in the record for your site in Sites table in Django admin. The domain should be then "example.com/project" with the second solution, because Django must know the domain and the prefix, especially for correct redirect. Naturally the prefix must be also stripped from URL of the request by webserver, as you do it now in nginx settings.

I usually split the verification of such deployments to two questions:

  • Does report the web server (nginx) the expected shorter or longer URLs to Django?
  • Does know Django its full base URL and does it use that in all web pages consistently?

I use both nginx log and Django logging enabled to see which of them is eventually miscofigured. (You didn't wrote enough important information.)

An important case that should be tested is: Verify that web pages that require athentication are redirected to login page correctly back and forth, even if you try these URLs after logout.

More details can be found in the similar question.

Community
  • 1
  • 1
hynekcer
  • 14,942
  • 6
  • 61
  • 99
  • the problem is now how to point to different static directories (after the update mentioned in op) – Bjorn Mar 10 '16 at 22:39
  • Is your problem that the web page contains invalid references to static files or the right static urls are not found by nginx? I expect that you could provide static files correctly if the project will be in "/". Nothing is changed with static files if the dynamic pages of project are in "/project". I understand that you asked for "/project" prefix because you need more projects on the same domain. It was not clearly expressed that you accept the classic "/static/appname/", e.g. "/static/admin" without the "/project" anywhere in the url. I think so because you have not specified anything yet. – hynekcer Mar 10 '16 at 23:52
  • I've got it working so that I'm finding the static files just fine and the /project/ is responding correctly - but for some reason project/app isn't giving me anything, nginx is passing the control to some default project/app/index.html handler - not my django app :/ – Bjorn Mar 11 '16 at 15:12
  • Hmm, the question is steadily changing while changing the configuration and something stops working, but the principle of answer is the same: The solution is usually easy if you look in logs and see if the problem is (currently) in nginx or really in Django. I try a presumed correct url and I also look if this url is generated by Django. Also /var/log/nginx/error.log is very useful for problems with nginx configuration, especially messages at startup. – hynekcer Mar 11 '16 at 16:38
  • Eventually I gave up and just added a prefix to settings that I applied to the project url file and static_url – Bjorn Mar 14 '16 at 22:53
0

Eventually gave up on trying to do this "neatly".

The final solution was just to make a settings variable that I prefixed to the static_url and projects urls.py file. No SCRIPT_NAME or anything complicated on the nginx side.

Bjorn
  • 455
  • 4
  • 13