添加负载均衡器时,Django多租户站点的重定向循环

时间:2017-01-27 05:00:14

标签: django nginx load-balancing infinite-loop http-redirect

我有两个虚拟主机,每个主机运行五个站点(多租户)。服务器有三个IP地址。两个是公开面对的,一个是内部的。

两个面向公众的网站都有SSL证书。一个站点是我的暂存站点,有一个letsencrypt SSL证书,另一个是实时站点,并且有一个godaddy SSL证书。

我首先使用一个节点(我的云实例)设置Rackspace负载均衡器,将证书和密钥从我的服务器复制到平衡器,并成功使用以下nginx配置以使负载均衡器从我的代理我的站点服务于内部IP的Web服务器

upstream django {
    server unix:///run/uwsgi/app/introtest/socket;
}

# configuration of the server, first redirect http to https...
server {
    listen      10.181.104.195:80;
    if ($http_x_forwarded_proto = "http") {
        return 302 https://$http_host$request_uri;
    }

    # the domain name it will serve for
    charset     utf-8;

    # max upload size
    client_max_body_size 75M;   # adjust to taste

    # Django media
    location /media  {
        alias /srv/test/media;
    }

    location /static {
        alias /srv/test/static;
    }


    # Finally, send all non-media requests to the Django server.
    location / {
        if (-f /srv/maintenance_test.html) {
            return 503;
        } 
        uwsgi_pass  django;

        uwsgi_param  QUERY_STRING       $query_string;
        uwsgi_param  REQUEST_METHOD     $request_method;
        uwsgi_param  CONTENT_TYPE       $content_type;
        uwsgi_param  CONTENT_LENGTH     $content_length;

        uwsgi_param  REQUEST_URI        $request_uri;
        uwsgi_param  PATH_INFO          $document_uri;
        uwsgi_param  DOCUMENT_ROOT      $document_root;
        uwsgi_param  SERVER_PROTOCOL    $server_protocol;
        uwsgi_param  REQUEST_SCHEME     $scheme;
        uwsgi_param  HTTPS              $https if_not_empty;

        uwsgi_param  REMOTE_ADDR        $remote_addr;
        uwsgi_param  REMOTE_PORT        $remote_port;
        uwsgi_param  SERVER_PORT        $server_port;
        uwsgi_param  SERVER_NAME        $server_name;
        uwsgi_param  X-Real-IP          $remote_addr;
        uwsgi_param  X-Forwarded-For    $proxy_add_x_forwarded_for;
        uwsgi_param  X-Forwarded-Host   $server_name;

    }

    # Error pages
    error_page 503 /maintenance_test.html; 
    location = /maintenance_test.html {
        root /srv;
    }

}

顺便说一句,如果我可以提供帮助,我不会使用永久重定向,也不会使用登台服务器。实时服务器已经设置好并且有永久重定向到https,但我想我们总是希望将实时站点重定向到SSL,因此重定向是301。

将我的暂存站点的根域的DNS条目更改为负载均衡器后,这很好用。

tail -f /var/log/nginx/access.log

显示请求来自负载均衡器的内部IP地址,并且页面正在正确提供。

我改回了所有内容(即nginx conf和登台根域的DNS条目),并且可以从网络服务器进行暂存服务。然后我将godaddy SSL证书信息复制到负载均衡器。然后使用以下用于实时服务器的nginx配置:

upstream intro_live {
    server unix:///run/uwsgi/app/introsites/socket;
}

server {
    listen <SERVER PUBLIC IP>:80;
    listen <SERVER PUBLIC IP>:443;

    location / {
        return 503;
    }
    error_page 503 /interruption.html; 
    location = /interruption.html {
        root /srv;
    }
}


# configuration of the server
server {
    # the port your site will be served on
    listen  10.181.104.195:80;
    # reidrect http to https from load balancer
    if ($http_x_forwarded_proto = "http") {
        set $http_test  S${http_host};
    }

    if ($http_test = 'Sintrotravel.com') {
        rewrite ^ https://www.introtravel.com$request_uri;
    }
    if ($http_test = 'Sozintro.com') {
        rewrite ^ https://www.ozintro.com$request_uri permanent;
    }
    if ($http_test = 'Sbalintro.com') {
        rewrite ^ https://www.balintro.com$request_uri permanent;
    }
    if ($http_test = 'Sthaintro.com') {
        rewrite ^ https://www.thaintro.com$request_uri permanent;
    }
    if ($http_test = 'Svietnamintro.com') {
        rewrite ^ https://www.vietnamintro.com$request_uri permanent;
    }

    charset     utf-8;

    # max upload size
    client_max_body_size 75M;   # adjust to taste

    # Django media
    location /media  {
        alias /srv/intro/media;
        expires 7d;
        add_header Pragma public;
        add_header Cache-Control "public";
    }

    location /static {
        alias /srv/intro/static;
        expires 1d;
        add_header Pragma public;
        add_header Cache-Control "public";
    }


    # Finally, send all non-media requests to the Django server.
    location / {
        if (-f /srv/maintenance_on.html) {
            return 503;
        } 
        uwsgi_pass  intro_live;

        uwsgi_param  QUERY_STRING       $query_string;
        uwsgi_param  REQUEST_METHOD     $request_method;
        uwsgi_param  CONTENT_TYPE       $content_type;
        uwsgi_param  CONTENT_LENGTH     $content_length;

        uwsgi_param  REQUEST_URI        $request_uri;
        uwsgi_param  PATH_INFO          $document_uri;
        uwsgi_param  DOCUMENT_ROOT      $document_root;
        uwsgi_param  SERVER_PROTOCOL    $server_protocol;
        uwsgi_param  REQUEST_SCHEME     $scheme;
        uwsgi_param  HTTPS              $https if_not_empty;

        uwsgi_param  REMOTE_ADDR        $remote_addr;
        uwsgi_param  REMOTE_PORT        $remote_port;
        uwsgi_param  SERVER_PORT        $server_port;
        uwsgi_param  SERVER_NAME        $server_name;
        uwsgi_param  X-Real-IP          $remote_addr;
        uwsgi_param  X-Forwarded-For    $proxy_add_x_forwarded_for;
        uwsgi_param  X-Forwarded-Host   $server_name;

    }

    # Error pages
    error_page 503 /maintenance_on.html; 
    location = /maintenance_on.html {
        root /srv;
    }
}
  • 第一个upstream django指向我的uwsgi配置。
  • 第二个服务器配置侦听服务器的面向公众的地址,这样如果某个人的网站DNS条目尚未更新,他们会得到一个静态页面,说服务器处于维护状态。
  • 第三个服务器配置侦听内部地址上的端口80,检查登台服务器是否已设置http_x_forwarded_proto,以及是否已为域设置测试变量。我专门将来自五个站点的http流量重定向到https。
  • 最后503个东西检测到文件的存在,如果存在,则该网站进入维护模式。

/etc/nginx/conf.d/中没有任何内容,我的/etc/nginx/nginx.conf看起来像这样:

user www-data;
worker_processes 20;
pid /run/nginx.pid;

events {
        worker_connections 768;
        # multi_accept on;
}

http {

        ##
        # Basic Settings
        ##

        sendfile on;
        tcp_nopush on;
        tcp_nodelay on;

        keepalive_timeout 65;
        types_hash_max_size 2048;

        # server_tokens off;
        # server_names_hash_bucket_size 64;
        # server_name_in_redirect off;

        include /etc/nginx/mime.types;
        default_type application/octet-stream;

        ##
        # SSL Settings
        ##

        ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
        ssl_prefer_server_ciphers on;

        ##
        # Logging Settings
        ##

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

        ##
        # Gzip Settings
        ##

        gzip on;
        gzip_disable "msie6";

        gzip_vary on;
        gzip_proxied any;
        gzip_comp_level 6;
        gzip_buffers 16 8k;
        gzip_http_version 1.1;
        gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

        ##
        # Virtual Host Configs
        ##

        include /etc/nginx/conf.d/*.conf;
        include /etc/nginx/sites-enabled/*;
}

一旦我将五个站点的DNS A记录设置为指向负载均衡器,重定向到https就可以了,但我在每个页面中都有一个重定向循环。所以,我从配置中取出了所有重定向,即

    if ($http_test = 'Sintrotravel.com') {
        rewrite ^ https://www.introtravel.com$request_uri;
    }
    if ($http_test = 'Sozintro.com') {
        rewrite ^ https://www.ozintro.com$request_uri permanent;
    }
    if ($http_test = 'Sbalintro.com') {
        rewrite ^ https://www.balintro.com$request_uri permanent;
    }
    if ($http_test = 'Sthaintro.com') {
        rewrite ^ https://www.thaintro.com$request_uri permanent;
    }
    if ($http_test = 'Svietnamintro.com') {
        rewrite ^ https://www.vietnamintro.com$request_uri permanent;
    }

并重新启动nginx。我还有一个重定向循环。为什么???我知道重写是永久性的,但这不是因为return 302rewrite的区别,因为当我取出 all 时,服务器仍处于无限重定向循环中重定向。由于显而易见的原因,我不能花很多时间尝试实时网站。真的,我需要在10-15分钟内完成任何人有任何建议吗?

2 个答案:

答案 0 :(得分:0)

考虑到这一点,我意识到它必须是Django应用程序才是问题所在。与nginx无关。有一个设置

SECURE_SSL_REDIRECT = True

使django将http流量重定向到https(在暂存时这是假的)。当您添加负载均衡器时,这会成为一个问题,因为负载均衡器会收到https流量,但它只会将端口80上的非安全流量传递到我的Web服务器/应用程序。这就是你重定向条件的原因

if ($http_x_forwarded_proto = "http") {...
nginx配置中的

(由负载均衡器设置)。仅从nginx以这种方式删除重定向不会解决问题,我不得不从django中完全删除它。

无论您多么有经验,如果您因为分散的性质而匆忙,DNS会让您感到困惑,当一个赚钱的网站瘫痪时,您就会匆忙。

答案 1 :(得分:0)

Django可以通过以下方式解决这个问题:

https://docs.djangoproject.com/en/1.11/ref/settings/#std:setting-SECURE_PROXY_SSL_HEADER

添加: SECURE_PROXY_SSL_HEADER =('HTTP_X_FORWARDED_PROTO','https')

确保您的代理设置适当的标头将阻止重定向循环。会发生什么事情是你的代理没有告诉Django连接是安全的,所以它保持重定向到安全版本,虽然它已经是安全的 - 因此无限循环。提供标题是解决此问题的方法之一。