How do I redirect HTTPS to HTTP on NGINX?

前端 未结 6 1153
孤城傲影
孤城傲影 2020-12-23 13:33

Is there a way to redirect HTTPS requests to HTTP by adding a rule in the domain\'s vhost file?

6条回答
  •  有刺的猬
    2020-12-23 13:51

    rewrite and if should be avoided with Nginx. The famous line is, "Nginx is not Apache": in other words, Nginx has better ways to handle URLs than rewriting. return is still technically part of the rewrite module, but it doesn't carry the overhead of rewrite, and isn't as caveat-ridden as if.

    Nginx has an entire page on why if is "evil". It also provides a constructive page explaining why rewrite and if are bad, and how you can work around it. Here's what the page has to say regarding rewrite and if:

    This is a wrong, cumbersome, and ineffective way.

    You can solve this problem properly using return:

    server {
        listen 443 ssl;
    
        # You will need a wildcard certificate if you want to specify multiple
        # hostnames here.
        server_name domain.example www.domain.example;
    
        # If you have a certificate that is shared among several servers, you
        # can move these outside the `server` block.
        ssl_certificate /path/to/cert.pem;
        ssl_certificate_key /path/to/cert.key;
    
        # 301          indicates a permanent redirect.  If your redirect is
        #              temporary, you can change it to 302 or omit the number
        #              altogether.
        # $http_host   is the hostname and, if applicable, port--unlike $host,
        #              which will break on non-standard ports
        # $request_uri is the raw URI requested by the client, including any
        #              querystring
        return 301 http://$http_host$request_uri;
    }
    

    If you expect a lot of bots that don't send a Host header, you can use $host instead of $http_host as long as you stick to ports 80 and 443. Otherwise, you'll need to dynamically populate an $http_host substitute. This code is efficient and safe as long as it appears in the root of server (rather than in a location block), despite using if. However, you'd need to be using a default server for this to be applicable, which should be avoided with https.

    set $request_host $server_name:$server_port;
    if ($http_host) {
        set $request_host $http_host;
    }
    

    If you want to enforce SSL/TLS for specific paths, but forbid it otherwise:

    server {
        listen 443 ssl;
        server_name domain.example;
    
        ssl_certificate /path/to/cert.pem;
        ssl_certificate_key /path/to/cert.key;
    
        location / {
            return 301 http://$host$request_uri;
        }
    
        location /secure/ {
            try_files $uri =404;
        }
    }
    
    server {
        listen 80;
        server_name domain.example;
    
        location / {
            try_files $uri =404;
        }
    
        location /secure/ {
            return 301 https://$http_host$request_uri;
        }
    }
    

    If your server isn't in direct communication with the client--for example, if you're using CloudFlare--things get a bit more complicated. You'll need to ensure that any server in direct communication with the client adds an appropriate X-Forwarded-Proto header to the request.

    Using this is a messy proposition; for a full explanation, see IfIsEvil. In order for this to be useful, the if block cannot be inside a location block, for a variety of complex reasons. This forces the use of rewrite for URI testing. In short, if you have to use this on a production server... don't. Think of it this way: if you've outgrown Apache, you've outgrown this solution.

    /secure, /secure/, and anything in /secure/ will enforce https, while all other URIs will enforce http. The (?! ) PCRE construct is a negative lookahead assertion. (?: ) is a non-capturing group.

    server {
        # If you're using https between servers, you'll need to modify the listen
        # block and ensure that proper ssl_* statements are either present or
        # inherited.
        listen 80;
        server_name domain.example;
    
        if ($http_x_forwarded_proto = https) {
            rewrite ^(?!/secure)/ http://$http_host$request_uri? permanent;
        }
        if ($http_x_forwarded_proto != https) {
            rewrite ^/secure(?:/|$) https://$http_host$request_uri? permanent;
        }
    }
    

提交回复
热议问题