I want to create a rule in nginx that does two things:
- Removes the "www." from the request URI
- Redirects to "https" if the request URI is "http"
There are plenty of examples of how to do each of those things individually, but I can't figure out a solution that does both correctly (i.e. doesn't create a redirect loop and handles all cases properly).
It needs to handle all of these cases:
1. http://www.example.com/path
2. https://www.example.com/path
3. http://example.com/path
4. https://example.com/path
These should all end up at https://example.com/path (#4) without looping. Any ideas?
The best way to accomplish this is using three server blocks: one to redirect http to https, one to redirect the https www-name to no-www, and one to actually handle requests. The reason for using extra server blocks instead of ifs is that server selection is performed using a hash table, and is very fast. Using a server-level if means the if is run for every request, which is wasteful. Also, capturing the requested uri in the rewrite is wasteful, as nginx already has this information in the $uri and $request_uri variables (without and with query string, respectively).
This works for me:
Keep in mind that both
yourdomain.com
andwww.yourdomain.com
must be in your SSL certificate. This is possible with a wildcard certificate or with a Server Alternate Name as explained here. Check https://www.startssl.com for nice and free certificates that do this. (Edith: beginning with Chrome version 56, startssl certificates will not be trusted anymore. Try https://letsencrypt.org/ instead.)After spending so much time with hundreds of similar cases, I've come up with the following snippet. It's short and can be easily tweaked to fit anything.
Yes it can be. But it exists for a reason, and should do no harm to those who know how to use it properly. ;)
I prefer to return with a response code so the browser knows you are redirecting it to another URL.
then another server configurations block for the
https
how about creating a server block for this purpose:
then restarting nginx
I think this should work.
On your plain HTTP server definition something like anthonysomerset suggested, that is:
Then on your SSL server definition:
This way the redirect should only happen once per request no matter which URL the user goes to originally.
Here's the full example that ended up working for me. The problem was that I didn't have the ssl details (
ssl_certificate
, etc.) in the www redirect block. Remember to check your logs (sudo tail -f /var/log/nginx/error.log
)!This, works for me
p.s. with other solutions I got: ERR_TOO_MANY_REDIRECTS
If you have many domains and you are looking for a more generic approach without loosing performance and without listing all domains all the time, check this.
How it works?
http://www.
which will go tohttps://www.
www
server names and redirect to non-www
using regular expressionwww
server name - this is where all the traffic will end upIs it fast?
Yes! Even though we use RegExp, it's only in the
www
-versions block which returns 301. So all normal traffic will be handled without any additional processing cost.PS: if you need help with SSL setup, checkout Mozilla SSL Configuration Generator:
https://ssl-config.mozilla.org/