I have an nginx 1.6.2 server running as a backend behind a load balancer that does SSL termination. All communication to the backend servers goes over HTTP.

Diagram of what's going on:

          /--http---> frontend:80  --\
client --+                            +--http---> backend:8000
          \--https--> frontend:443 --/

                      LOAD BALANCER                BACKENDS

For testing purposes I only have one backend at the moment. The load balancer runs HAProxy 1.5, which I have some control over.

I have a pretty typical try_files directive in my server block in the backend nginx config:

server {
    server_name frontend;
    ...
    try_files $uri $uri/ =404;
    ...
}

Now, by default, when I access a directory without a trailing slash, e.g. https://frontend/somedir, nginx wants to send an HTTP 301 redirect to an absolute URL like http://frontend:8000/somedir/.

I can make nginx omit the 8000 port number using port_in_redirect off.

However I can't seem to correct the http:// scheme at the beginning of the redirect nginx is generating. The best I can get nginx to do is a redirect from https://frontend/somedir to http://frontend/somedir/, effectively stripping SSL!

The load balancer is sending an X-Forwarded-Proto header but I do not see any way for nginx to consult it when crafting its redirect; in fact there is a 2012 answer saying nginx outright cannot do this, and the solution is to replace the load balancer with nginx. IMHO this is too trivial a thing to warrant such a drastic stack change.

Has anything changed since 2012 here? I don't really want to rewrite these redirects at the HAProxy level: actual intentional HTTPS to HTTP redirects from the web application might get "re-HTTPSed" if I just always rewrite the scheme for the Location: response header to be the same as the scheme the request was made with.

EDIT:

Here is a minimized config showing nginx produces absolute Location: URLs. Note there are no rewrites.

user nobody nobody;
worker_processes auto;
worker_rlimit_nofile 4096;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

events {
    worker_connections 1024;
    multi_accept on;
}

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

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;

    # TODO: Tune fastcgi_buffers/other buffers

    # Configure keepalive connections
    keepalive_timeout 15;
    keepalive_requests 1000;

    # Hide server version.
    server_tokens off;

    # Do not allow any directory indexes anywhere.
    # This is the default, but it is here for extra paranoia.
    autoindex off;

    # gzip text content.
    gzip on;
    gzip_vary on;
    gzip_disable "msie6";
    gzip_comp_level 2;
    gzip_min_length 1024;
    gzip_types  text/css
                text/plain
                text/xml
                application/json
                application/javascript;

    server {
        listen 8000 default_server;

        root /usr/share/nginx/html;
        index index.html index.htm;

        server_name localhost;

        location / {
            try_files $uri $uri/ =404;
        }
    }
}

And if you use curl to view the headers -- note I have created a directory testdir under /usr/share/nginx/html:

[myuser@dev nginx]$ curl -i http://localhost:8000/testdir
HTTP/1.1 301 Moved Permanently
Server: nginx
Date: Thu, 26 Mar 2015 14:35:49 GMT
Content-Type: text/html
Content-Length: 178
Location: http://localhost:8000/testdir/
Connection: keep-alive

<html>
<head><title>301 Moved Permanently</title></head>
<body bgcolor="white">
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx</center>
</body>
</html>
  • nginx wants to send an HTTP 301 redirect to an absolute URL - nginx doesn't do that unless you have an explicit rewrite rule to do so - please show your full config. – AD7six Mar 26 '15 at 8:27
  • I added the config, trimmed down to just illustrate this issue. I don't do any explicit rewrites. – mutron Mar 26 '15 at 14:38
  • 1
    You should configure your balancer to replace protocol in Location: header since nginx has no idea what was original protocol. – Alexey Ten Mar 30 '15 at 6:47
  • 2
    @AlexeyTen would you like to post that as an answer so I can approve it? I have not found a way to do this in nginx either, so a change at the load balancer does indeed appears to be the only way. – mutron Apr 16 '15 at 3:49
  • This is still a bug with the latest nginX release! Does anybody know if there is a bug report already? Could not find one. – Roel van Duijnhoven May 17 at 14:29

You need to tell the load balancer to rewrite http to https in the requests like so:

proxy_redirect http:// $scheme://;

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.