I want to rewrite all http requests on my web server to be https requests, I started with the following:
server { listen 80; location / { rewrite ^(.*) https://mysite.com$1 permanent; } ...
One Problem is that this strips away any subdomain information (e.g., node1.mysite.com/folder), how could I rewrite the above to reroute everything to https and maintain the sub-domain?
Correct way in new versions of nginx
Turn out my first answer to this question was correct at certain time, but it turned into another pitfall - to stay up to date please check Taxing rewrite pitfalls
I have been corrected by many SE users, so the credit goes to them, but more importantly, here is the correct code:
NOTE: The best way to do this was provided by https://serverfault.com/a/401632/3641 - but is repeated here:
In the simplest case your host will be fixed to be your service you want to send them to - this will do a 301 redirect to the browser and the browser URL will update accordingly.
Below is the previous answer, which is inefficient due to regex, a simple 301 is great as shown by @kmindi
I have been using nginx 0.8.39 and above, and used the following:
Sends a permanent redirect to the client.
I think the best and only way should be using a HTTP 301 Moved Permanently redirect like this:
The HTTP 301 Moved Permanently redirect is also the most efficient because there is no regex to be evaluated, according to already mentioned pitfails.
The new HTTP 308 Moved Permanently preserves the Request method and is supported by major browsers. For example, using
308
prevents browsers from changing the request method fromPOST
toGET
for the redirect request.If you want to preserve the hostname and subdomain this is the way.
This does still work if you have no DNS, as I am also using it locally. I am requesting for example with
http://192.168.0.100/index.php
and will get redirected to exactlyhttps://192.168.0.100/index.php
.I use
listen [::]:80
on my host because i havebindv6only
set tofalse
, so it also binds to ipv4 socket. change it tolisten 80
if you don't want IPv6 or want to bind elsewhere.The solution from Saif Bechan uses the
server_name
which in my case is localhost but that is not reachable over a network.The solution from Michael Neale is good, but according to the pitfails, there is a better solution with redirect 301 ;)
Within the server block you can also do the following:
The above didn't work for with with new subdomains being created all the time. e.g. AAA.example.com BBB.example.com for about 30 subdomains.
Finally got a config working with the following:
I posted a comment on the correct answer a long, long time ago with a very important correction, but I feel it is necessary to highlight this correction in its own answer. None of the previous answers are safe to use if at any point you had unsecure HTTP set up and expect user content, have forms, host an API, or have configured any website, tool, application, or utility to speak to your site.
The problem occurs when a
POST
request is made to your server. If the server response with a plain30x
redirect the POST content will be lost. What happens is that the browser/client will upgrade the request to SSL but downgrade thePOST
to aGET
request. ThePOST
parameters will be lost and incorrect request will be made to your server.The solution is simple. You need to use a
HTTP 1.1 307
redirect. This is detailed in RFC 7231 S6.4.7:The solution, adapted from the accepted solution, is to use
307
in your redirect code:I managed to do it like this:
https://stackoverflow.com/a/36777526/6076984
I am running ngnix behind an AWS ELB. The ELB is talking to ngnix over http. Since the ELB has no way to send out redirects to clients, I check for the X-Forwarded-Proto header and redirect:
If you
return 301 https://$host$request_uri;
as the default response on port 80, then your server may sooner or later get on a list of open proxies[1] and start being abused to send traffic elsewhere on the Internet. If your logs fill up with messages like this one, then you know it's happened to you:The problem is that
$host
will echo back whatever the browser sends in theHost
header or even the hostname from HTTP's opening line, like this one:Because of that problem, some other answers here recommend using
$server_name
instead of$host
.$server_name
always evaluates to what you put in theserver_name
declaration. But if you have multiple subdomains there or use a wildcard, that won't work, because$server_name
only uses the first entry after theserver_name
declaration, and more importantly will just echo back a wildcard (not expand it).So how to support multiple domains while maintaining security? On my own systems I've dealt with this dilemma by first listing a
default_server
block that doesn't use$host
, and then listing a wildcard block that does:(You could also list more than one domain in the second block.)
With that combination, unmatched domains will get redirected somewhere hardcoded (always
example.com
), and domains that match your own will go to the right place. Your server won't be useful as an open proxy, so you won't be attracting trouble.If you are feeling ornery, I suppose you could also make the
default_server
block match none of your legitimate domains and serve something offensive. . . .[1] Technically "proxy" is the wrong word, because your server isn't going out and fulfilling requests for the clients, just sending a redirect, but I'm not sure what the right word would be. I'm also not sure what the goal is, but it fills up your logs with noise and consumes your CPU and bandwidth, so you might as well put a stop to it.
Looks like nobody really got it 100% right. To have port 80 requests go to their 443 equivalents for an entire webserver, you need to use the listen directive, not the server_name directive to specify the catch-all name. See also https://nginx.org/en/docs/http/request_processing.html
And make sure you check what's already in /etc/nginx/conf.d/ because more often than not I had issues where the default.conf returned some existing vhost. My order of working with nginx issues is always starting by moving out the default file, putting it back commenting out line by line to see where it goes wrong.