16

我有一个相当标准的设置,nginx 面向 django 应用程序。我希望 django 应用程序仅是 SSL,因此我的 nginx conf 中有两个监听块,来自端口 80(HTTP)的流量被重定向到端口 443(SSL)。这按预期工作。

我在打开了端口转发的 VM 中运行此设置,这样我就可以通过访问端口 8080 (HTTP) 或 8081 (SSL) 从主机浏览站点。同样,正如预期的那样,这项工作正常。

当我在注册工作流程期间从 Django 应用程序内部重定向时,问题就出现了。因为 Django 从来没有看到 SSL 状态(SSL 在 nginx 处终止,并且应用程序的流量通过 HTTP 在端口 5000 上转发),但确实看到了端口,所以重定向被破坏了**。

所有这一切的最终结果是我将流量定向到 SSL 端口上的 nginx,而不是 SSL,例如http://127.0.0.1:443/. 有没有办法配置 nginx 来处理这个问题?

** 注意我在 Nginx 中设置 X-Forwarded-Proto 标头,而 Django 正在获取正确的 .is_secure() 值,这是外部库未检查 is_secure 并且仅重定向传入 URL 方案的特定问题。

[更新 1]

附件是相关的配置设置。这是来自 Vagrantfile 本身,显示了端口转发:

config.vm.forward_port 80, 8080     # website, via nginx (redirects to SSL:8081)
config.vm.forward_port 443, 8081    # website, via nginx (accepts SSL)
config.vm.forward_port 5000, 8180   # website, via gunicorn (direct)

使用上面的端口转发配置,如果我在 HTTP 端口(8080)上浏览到主机上的站点,则请求被接受,并且 nginx(见下文)将此请求重定向到 HTTPS(在端口 8081 上运行)。一旦我使用 HTTPS,网站本身就可以正常工作:

(host) http://127.0.0.1:8080  -> forwarded to -> (guest vm) http://127.0.0.1:80  
(host) https://127.0.0.1:8081 -> forwarded to -> (guest vm) https://127.0.0.1:443

当我从 Django 内部获得混合方案和协议的重定向时,会出现问题,并以请求http:\\127.0.0.1:8081\...失败告终,因为 nginx 期望 8081 上的流量是 SSL。

我真正想要的是一条规则,上面写着“在 443 上监听 SSL 和非 SSL 并重定向非 SSL”。

这是相关的nginx配置:

# Django app is served by Gunicorn, running under port 5000 (via Foreman)
upstream gunicorn {
    server 127.0.0.1:5000 fail_timeout=0;
}

server {
    listen 80;
    # 8081 is the port I am forwarding to on the host machine
    rewrite ^ https://127.0.0.1:8081$request_uri? permanent;
}

server {
    listen 443;

    ssl on;
    ssl_protocols       SSLv3 TLSv1;
    ssl_ciphers         HIGH:!ADH:!MD5;
    ssl_prefer_server_ciphers on;
    ssl_certificate     /etc/nginx/ssl/self-signed.crt;
    ssl_certificate_key /etc/nginx/ssl/self-signed.key;

    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    location /static/ {
        alias /app/static/;
    }
    location /media/ {
        alias /app/media/;
    }
    location / {
        # everything else is to be served by the django app (upstream 'gunicorn')
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        # this is set to ensure that django is_secure returns True
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host $http_host;
        proxy_pass http://gunicorn;
    }
}
4

1 回答 1

28

您应该查看本文档的“错误处理”部分:

http://nginx.org/en/docs/http/ngx_http_ssl_module.html

非标准错误代码 497 可用于处理已发送到 HTTPS 端口的纯 HTTP 请求。

像这样的东西应该可以工作(未经测试):

error_page 497 https://$host$request_uri;

命名位置也可以在 error_page 中使用,有关详细信息,请参阅http://nginx.org/r/error_page

于 2013-01-09T15:54:54.700 回答