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

I have a signup page on a subdomain like: https://signup.mysite.com

It should only be accessible via HTTPS but I'm worried people might somehow stumble upon it via HTTP and get a 404.

My html/server block in nginx looks like this:

html {
  server {
    listen 443;
    server_name signup.mysite.com;

    ssl                        on;
    ssl_certificate        /path/to/my/cert;
    ssl_certificate_key  /path/to/my/key;

    ssl_session_timeout 30m;

    location / {
      root /path/to/my/rails/app/public;
      index index.html;
        passenger_enabled on;
    }
  }
}

What can I add so that people who go to http://signup.mysite.com get redirected to https://signup.mysite.com ? (FYI I know there are Rails plugins that can force SSL but was hoping to avoid that)

share|improve this question

7 Answers 7

up vote 66 down vote accepted

According to nginx pitfalls, it's slightly better to omit the unnecessary capture, using $request_uri instead. In that case, append a question mark to prevent nginx from doubling any query args.

server {
    listen      80;
    server_name signup.mysite.com;
    rewrite     ^   https://$server_name$request_uri? permanent;
}
share|improve this answer
    
You rock, thanks. –  Callmeed Mar 22 '11 at 19:38
24  
Or, according to the site you linked, "BETTER": return 301 http://domain.com$request_uri; –  nh2 Aug 14 '12 at 4:56
5  
one comment. the $server_name$ picks up the first server_name variable. So be aware of this if you have non FQN names in your configuration –  engineerDave Aug 15 '12 at 15:48

The best way as it described in the official how-to is by using the return directive:

server {
    listen      80;
    server_name signup.mysite.com;
    return 301 https://signup.mysite.com$request_uri;
}
share|improve this answer
2  
shortest answer and worked perfectly in my case –  mateusz.fiolka Sep 8 '12 at 22:37
    
This is generally recommended because it returns a 301 Moved Permanently (your links have permanently moved) as well as re-writing –  sgb Dec 17 at 17:17

If you are using the new dual HTTP and HTTPS server definition, you can use the following:

server {
    listen   80;
    listen   [::]:80;
    listen   443 default ssl;

    server_name www.example.com;

    ssl_certificate        /path/to/my/cert;
    ssl_certificate_key  /path/to/my/key;

    if ($ssl_protocol = "") {
       rewrite ^   https://$server_name$request_uri? permanent;
    }
}

This appears to work for me and doesn't cause redirect loops.

Edit:

Replaced:

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

with Pratik's rewrite line.

share|improve this answer
    
Like this example, just use the rewrite rule from the Pratik's answer –  Ryan Gibbons Nov 30 '11 at 17:23
    
@DavidPashley your solution worked like a charm for me. Thanks –  Jayesh Gopalan May 17 '12 at 12:23
    
If you are using the new dual HTTP and HTTPS server definition then you should separate it. –  VBart Feb 23 '13 at 14:42
    
elegant and works perfect! –  jipipayo Sep 17 at 17:01

This is the correct and most efficient way if you want to keep it all in one server block:

server {
    listen   80;
    listen   [::]:80;
    listen   443 default_server ssl;

    server_name www.example.com;

    ssl_certificate        /path/to/my/cert;
    ssl_certificate_key  /path/to/my/key;

    if ($scheme = http) {
        return 301 https://$server_name$request_uri;
    }
}

Everything else above, using "rewrite" or "if ssl_protocol" etc is slower and worse.

Here is the same, but even more efficient, by only running the rewrite on the http protocol it avoids having to check the $scheme variable on every request. But seriously, it's such a minor thing that you don't need to separate them.

server {
    listen   80;
    listen   [::]:80;

    server_name www.example.com;

    return 301 https://$server_name$request_uri;
}
server {
    listen   443 default_server ssl;

    server_name www.example.com;

    ssl_certificate        /path/to/my/cert;
    ssl_certificate_key  /path/to/my/key;
}
share|improve this answer
4  
Great, some coward voted this answer down without saying why, even though this answer is correct. Maybe another one of those "if is evil" cultists. If you bother to read the Nginx documentation about If, you will know that IfIsNOTEvil, just CERTAIN uses of it within a location{} context, none of which we do here. My answer is absolutely the correct way of doing things! –  DELETEDACC Feb 26 '13 at 21:50
1  
I didn't down vote this, but I would like to point out that default has been changed to 'default_server' in the most recent versions. –  spuder Oct 22 '13 at 3:32

Yet another variant, that preserves the Host: request header and follows the "GOOD" example on nginx pitfalls:

server {
    listen   10.0.0.134:80 default_server;

    server_name  site1;
    server_name  site2;
    server_name  10.0.0.134;

    return 301 https://$host$request_uri;
}

Here are the results. Note that using $server_name instead of $host would always redirect to https://site1.

# curl -Is http://site1/ | grep Location
Location: https://site1/

# curl -Is http://site2/ | grep Location
Location: https://site2/


# curl -Is http://site1/foo/bar | grep Location
Location: https://site1/foo/bar

# curl -Is http://site1/foo/bar?baz=qux | grep Location
Location: https://site1/foo/bar?baz=qux
share|improve this answer
    
Note that using $server_name instead of $host would always redirect to https://site1 isn't that what $request_uri is for? –  Pineapple Under the Sea Apr 27 at 9:49
1  
$request_uri does not contain a host or domain name. In other words, it always starts with a "/" character. –  Peter Apr 27 at 11:17

Make sure you set 'secure' on any cookies, or else they'll be sent on the HTTP request and could be grabbed by a tool like Firesheep.

share|improve this answer
server {
    listen x.x.x.x:80;

    server_name domain.tld;
    server_name www.domian.tld;
    server_name ipv4.domain.tld;

    rewrite     ^   https://$server_name$request_uri? permanent;
}

This works better i think. x.x.x.x refers to your server's IP. If you are working with Plesk 12, you can do that by changing the "nginx.conf" file in the directory "/var/www/vhosts/system/domain.tld/conf" for whichever domain you want. Do not forget to restart nginx service after you save the configuration.

share|improve this answer

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.