How do I redirect HTTPS to HTTP on NGINX?
Why is something like that useful? At first look I wasn't sure if it could be done. But it presented an interesting question.
You might try putting a redirect statement in your config file and restarting your server. Two possibilities might happen:
- The server will issue the redirect - what you seem to want.
- The server will first do the https exchange, and THEN issue the redirect, in which case, what's the point?
Will add more if I come up with something more concrete.
UPDATE: (couple of hours later)You could try this. You need to put this in your nginx.conf file -
server { listen 443; server_name _ *; rewrite ^(.*) http://$host$1 permanent; }
Sends a permanent redirect to the client. I am assuming you are using port 443 (default) for https.
server { listen 80; server_name _ *; ...}
Add this so that your normal http requests on port 80 are undisturbed.
UPDATE: 18th Dec 2016- server_name _
should be used instead of server_name _ *
in nginx versions > 0.6.25 (thanks to @Luca Steeb)
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; }}