Take the 2-minute tour ×
Server Fault is a question and answer site for professional system and network administrators. It's 100% free, no registration required.

I want to create a rule in nginx that does two things:

  1. Removes the "www." from the request URI
  2. 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?

share|improve this question
add comment

4 Answers

up vote 11 down vote accepted

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).

server {
    server_name www.example.com example.com;
    rewrite ^ https://example.com$request_uri? permanent;
}

server {
    listen 443;
    ssl on;
    <possibly other ssl directives if you have a separate cert and key for www>
    server_name www.example.com
    rewrite ^ https://example.com$request_uri? permanent;
}

server {
    listen 443;
    ssl on;
    ssl_certificate /path/to/server.cert;
    ssl_certificate_key /path/to/server.key;
    server_name example.com;

    <locations for processing requests>
}
share|improve this answer
    
Is the middle block necessary? Isn't the first block already rewriting from www to non-www? –  pbreitenbach May 16 '11 at 20:39
    
The first block only handles http. The middle block is necessary to redirect https requests from https:// www.example.com/ to https:// example.com/. (Sorry for the extra spaces, I can't make it show the https otherwise) –  kolbyjack May 17 '11 at 1:03
    
just a minor formatting note - if you want to avoid making a link, you can put comment text inside back-quotes ` , the one under tilde. It would show up like: https://example.com/ –  Cyclops Jun 9 '11 at 21:13
add comment

I prefer to return with a response code so the browser knows you are redirecting it to another URL.

server {
    listen   80;
    server_name  www.example.com;

    return 301 https://example.com$request_uri;
}

then another server configurations block for the https

server {
        listen   443 ssl;
        server_name  example.com;
        ...
    }
share|improve this answer
add comment

how about creating a server block for this purpose:

server{
    listen 80;
    server_name www.example.net example.net;
    rewrite ^(.*) https://example.net$1 permanent;
}

then restarting nginx

share|improve this answer
    
I get a "conflicting server name" error when restarting. Also, that command won't listen on port 443 for SSL and I need to worry about redirecting https://www.example.com to https://example.com as well. –  Devin Apr 11 '11 at 16:56
add comment

I think this should work.

On your plain HTTP server definition something like anthonysomerset suggested, that is:

rewrite ^(.*) https://example.net$1 permanent;

Then on your SSL server definition:

if ($host ~ /^www\./) {
  rewrite ^(.*) https://example.net$1 permanent;
}

This way the redirect should only happen once per request no matter which URL the user goes to originally.

share|improve this answer
    
That worked, thanks. I had to change your conditional to if ($host = 'www.example.com') { since your regex wasn't working for me, though. No idea why, as it looks correct. –  Devin Apr 11 '11 at 18:20
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.