title: Can I redirect non-SSL traffic that comes in on an SSL port with Nginx
body: <p>I have a fairly standard setup with nginx fronting a django app. I want the django app to be SSL only, and so I have two listen blocks in my nginx conf, with the traffic coming in on port 80 (HTTP) being redirected to port 443 (SSL). This is working as expected.</p>
  
  <p>I am running this setup inside a VM that has port forwarding turned on, such that I can browser the site from the host machine by going to port 8080 (HTTP) or 8081 (SSL). Again, this work fine, as expected.</p>
  
  <p>The problem comes when I am redirected internally from the Django app during a registration workflow. Because Django never sees the SSL status (SSL is terminated at nginx, and traffic to the app is forwarded on port 5000 over HTTP), but does see the port, the redirect is getting mangled**.</p>
  
  <p><strong>The net result of all of this is that I have traffic being directed into nginx on the SSL port, that is not SSL, e.g. <code>http://127.0.0.1:443/</code>. Is there any way to configure nginx to handle this?</strong> </p>
  
  <p>** NB I am setting the X-Forwarded-Proto header in Nginx, and Django is picking up the correct .is_secure() value, this is a specific problem with an external library not checking is_secure and just redirecting on the incoming URL scheme.</p>
  
  <p><strong>[UPDATE 1]</strong></p>
  
  <p>Attached are the relevant config settings. This is from the Vagrantfile itself, showing the port forwarding:</p>
  
  <pre><code>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)
  </code></pre>
  
  <p>Using the above port forwarding configuration, if I browse to the site on the host machine on the HTTP port (8080), then the request is accepted, and nginx (see below) redirects this request to HTTPS  (running on port 8081). Once I am on HTTPS the site itself works fine:</p>
  
  <pre><code>(host) http://127.0.0.1:8080  -&gt; forwarded to -&gt; (guest vm) http://127.0.0.1:80  
  (host) https://127.0.0.1:8081 -&gt; forwarded to -&gt; (guest vm) https://127.0.0.1:443
  </code></pre>
  
  <p>The problem occurs when I get a redirect internally from Django which mixed scheme &amp; protocol, and ends up with a request to <code>http:\\127.0.0.1:8081\...</code>, which fails, as nginx is expecting traffic on 8081 to be SSL.</p>
  
  <p>What I really want is a rule that says 'listen on 443 for both SSL and non-SSL and redirect non-SSL'.</p>
  
  <p>This is the relevant nginx configuration:</p>
  
  <pre><code># 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;
      }
  }
  </code></pre>