Redirect www to non-www on Nginx with SSL gives re

2019-08-10 13:33发布

问题:

I apologize if this seems like a deja vu. There are plenty of posts about similar issues, and I read them all (and tried them out unsuccessfully).

My setup: Rails 4, Puma, Nginx, SSL Cert for both https://www and https://

I am using a combined block so I get a redirect to SSL. However, I would like to redirect https://www.domain.com to https://domain.com Everything works fine with the setup you will see below until I add the redirect rule (return 301 https://$host$request_uri;), then I get a redirect loop.

I added "proxy_set_header X-Forwarded-Proto $scheme;" to my @app location for force_ssl (which is set to true in the Rails config file), but that did not solve the issue.

I would really appreciate expert advise here, and please, if you see any points of improvement in my setup, beyond just fixing the redirect loop, please let me know.

nginx.conf:

user root;
worker_processes 4;
pid /var/run/nginx.pid;

#setup where nginx will log errors to 
# and where the nginx process id resides
error_log  /var/log/nginx/error.log error;
#pid        /var/run/nginx.pid;

events {
  worker_connections  1024;
  accept_mutex off;
  use epoll;
}


http {
  include /etc/nginx/mime.types;
  types_hash_max_size 2048;
  default_type application/octet-stream;
  #access_log /tmp/nginx.access.log combined;

  # use the kernel sendfile
  sendfile      on;
  # prepend http headers before sendfile() 
  tcp_nopush    on;

  keepalive_timeout  25;
  tcp_nodelay        on;

  gzip on;
  gzip_http_version 1.0;
  gzip_proxied any;
  gzip_min_length 500;
  gzip_disable "MSIE [1-6]\.";
  gzip_types text/plain text/html text/xml text/css
             text/comma-separated-values
             text/javascript application/x-javascript
             application/atom+xml;

  #Hide server info
  server_tokens off;

    upstream app_server {
      server unix:/root/sites/mina_deploy/shared/tmp/sockets/puma.sock
        fail_timeout=0;
    }

  # configure the virtual host
  server {

    server_name domain.com www.domain.com 162.555.555.162;

    root /root/sites/mina_deploy/current/public;
    # port to listen for requests on
    listen 80 default deferred;
    listen 443 ssl;

    ####### THIS REDIRECT CAUSES A LOOP ########
    #return       301 https://$host$request_uri;

    ssl_certificate    /etc/ssl/ssl-bundle.crt;
    ssl_certificate_key     /etc/ssl/myserver.key;
    #enables all versions of TLS, but not SSLv2 or 3 which are weak and now deprecated.
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    #Disables all weak ciphers
    ssl_ciphers 'AES128+EECDH:AES128+EDH';
    ssl_session_cache shared:SSL:10m;
    ssl_prefer_server_ciphers on;
    ssl_dhparam /etc/ssl/dhparam.pem;
    # maximum accepted body size of client request 
    client_max_body_size 4G;
    # the server will close connections after this time 
    keepalive_timeout 5;
    add_header Strict-Transport-Security max-age=63072000;
    #add_header X-Frame-Options DENY;
    add_header Access-Control-Allow-Origin '*';
    add_header X-Content-Type-Options nosniff;
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 8.8.8.8 8.8.4.4 valid=300s;
    resolver_timeout 5s;

    location ~ ^/(system|assets)/  {
      gzip_static on;
      error_page 405 = $uri;
      expires max;
      add_header Cache-Control public;
      break;
    } 

   try_files $uri/index.html $uri @app;

     location @app {
     # pass to the upstream unicorn server mentioned above 
     proxy_pass http://app_server;
     proxy_redirect off;

     proxy_set_header   Host              $host;
     proxy_set_header   X-Real-IP         $remote_addr;
     proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
     proxy_set_header   X-Forwarded-Proto $scheme;

     proxy_read_timeout 300;
     }
    }
}

回答1:

The thing I did was have multiple server blocks. You mentioned that you want www.domain.com to redirect to domain.com. In this case I would do

server {
    listen 80;
    server_name www.domain.com;
    return 301 https://domain.com$request_uri;
}

Then remove your www.domain.com from your server_name in your original block. Also I would break up your redirects from 80 to 443 in separate blocks as well. So you would repeat this process if a user tried to go to https://www.domain.com you would have a server that says similar things.

server {
    listen 443;
    server_name www.domain.com;
    return 301 https://domain.com$request_uri;
}

And one to listen for http traffic on the domain you want, but redirected to https traffic.

server {
    listen 80;
    server_name domain.com;
    return 301 https://domain.com$request_uri;
}

Then you can listen to just port 443 in your server block where you want everyone to go and no redirects are in that block.

You can view documentation for nginx here which will show you that this is the proper way to rewrite

Replying to your comment, Use the three blocks that I have written, and in your original server block, you will need to remove

server_name domain.com www.domain.com 162.555.555.162;

and also remove

listen 80 deferred;

and add

server_name domain.com;

Also, just making sure you know that for this to work, you will have to have your domain and www subdomain pointing at your server