有个常见需求是这样的:一个Django应用,在开发时,URL是以/为根目录的;而部署时,需要给它一个前缀,比如叫/prefix/。 它的使用场景是,在一个域名里托管多个应用,它们仅以前缀区分。

这个需求可以拆解成两个具体的要求:

  1. 在外面访问时是有/prefix/前缀的,而在应用那一层,不知道有这个前缀,仍然以为是/
  2. 在内部进行相对URL的生成时,虽然不知道前缀,但是要有前缀。 比如,从外面的首页/prefix/,希望点击一个链接后跳转到/prefix/home/; 而里面的应用在不知道前缀的情况下,生成的链接是/prefix/home/,而非/home/

WSGI协议的相关知识

What is WSGI

WSGI is the Web Server Gateway Interface. It is a specification that describes how a web server communicates with web applications, and how web applications can be chained together to process one request.

WSGI协议通过SCRIPT_NAMEPATH_INFO,来实现加前缀的操作。

from urllib import quote
url = environ['wsgi.url_scheme']+'://'

if environ.get('HTTP_HOST'):
    url += environ['HTTP_HOST']
else:
    url += environ['SERVER_NAME']

    if environ['wsgi.url_scheme'] == 'https':
        if environ['SERVER_PORT'] != '443':
           url += ':' + environ['SERVER_PORT']
    else:
        if environ['SERVER_PORT'] != '80':
           url += ':' + environ['SERVER_PORT']

url += quote(environ.get('SCRIPT_NAME', ''))
url += quote(environ.get('PATH_INFO', ''))
if environ.get('QUERY_STRING'):
    url += '?' + environ['QUERY_STRING']

从上述代码,源于WSGI的v1.01版本的URL Reconstruction描述,是推荐的URL构建算法。

一个URL的通用形式如下:

scheme:[//[user[:password]@]host[:port]][/path][?query][#fragment]

可以看出,SCRIPT_NAMEPATH_INFO,共同构成了[/path]这部分。 一般情况下,SCRIPT_NAME相当于为空,/path就等价于PATH_INFO。 而如果SCRIPT_NAME=/prefix,就实现了孤前面的第1个要求; 如果PATH_INFO等于去掉前缀后的部分,就实现了第2个要求。

WSGI协议参考

HTTP服务器方案

现在,主要的两个HTTP服务器------Apache httpd(通常简称Apache)和Nginx,都支持WSGI协议。

Apache httpd

Apache httpd通过模块mod_wsgiWSGI协议进行支持。 它对这个功能的实现与配置,简单而强大。

WSGIScriptAlias /prefix /PATH/TO/DJANGO/wsgi.py

但由于孤目前对Apache httpd并不太熟,也不实际使用,所以就说到这。

Nginx

旧方案

location ~ ^/prefix/ {
        ...
        uwsgi_param SCRIPT_NAME /prefix;
        uwsgi_modifier1 30;
    }

这是网上流传最多的设置方式。 然而,由于Nginx不支持修改PATH_INFO,所以需要uwsgi_modifier1 30这种丑陋的设置。 否则,内部被访问的应用,实际得到的访问链接是/prefix/prefix/...这种形式的。

uwsgi_modifier1 30的机