删除“ www”并使用nginx重定向到“ https”


57

我想在nginx中创建一个做两件事的规则:

  1. 删除“ www”。从请求URI
  2. 如果请求URI为“ http”,则重定向到“ https”

有很多示例说明了如何分别执行这些操作,但我想不出一个可以同时正确完成这两个问题的解决方案(即,不会创建重定向循环并正确处理所有情况)。

它需要处理所有这些情况:

1. http://www.example.com/path
2. https://www.example.com/path
3. http://example.com/path
4. https://example.com/path

这些都应该以https://example.com/path(#4)结尾而不循环。有任何想法吗?


我只是在DNS级别将www.mydomain.com重定向到mydomain.com,并在nginx中将301(非https)添加到https。似乎应该是¯\ _(ツ)_ /¯–
jonathanbell

Answers:


94

最好的方法是使用三个服务器块:一个将http重定向到https,一个将https www-name重定向到no-www,以及一个实际处理请求的服务器。使用额外的服务器块而不是ifs的原因是,使用哈希表执行服务器选择非常快。使用服务器级的if意味着对每个请求都运行if,这很浪费。另外,在重写中捕获请求的uri是浪费的,因为nginx已经在$ uri和$ request_uri变量中分别具有此信息(分别不带查询字符串和带查询字符串)。

server {
    server_name www.example.com example.com;
    return 301 https://example.com$request_uri;
}

server {
    listen 443 ssl;
    ssl_certificate /path/to/server.cert;
    ssl_certificate_key /path/to/server.key;
    server_name www.example.com;
    return 301 https://example.com$request_uri;
}

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

    <locations for processing requests>
}

2
中间块是否必要?第一个块是否已经从www重写为非www?
pbreitenbach's

3
第一个块仅处理http。中间的块是将https请求从https:// www.example.com/重定向到https:// example.com/所必需的。(对不起,多余的空格,否则我不能显示为https)
kolbyjack

1
只是一个较小的格式说明-如果您要避免建立链接,可以将注释文本放在反引号`中,即在波浪号下。它将显示为:https://example.com/
独眼巨人

9
第二个区块也需要证书信息。
ricka

3
尝试这个答案,我遇到了另一个问题。以为我可以301重定向www.sub.example.comsub.example.com,然后只为获取SSL证书sub.example.com现在我知道301重定向之前的SSL证书检查情况,所以它不能正常工作。此处提供更多说明:serverfault.com/a/358625/144811
Gruzzles

11

这对我有用:

server {
    listen              80;
    server_name         www.yourdomain.com yourdomain.com;
    return              301 https://yourdomain.com$request_uri;
}

server {
    listen              443 ssl;
    server_name         www.yourdomain.com;
    ssl_certificate     /path/to/certificate.crt;
    ssl_certificate_key /path/to/private/key.pem;
    ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
    return              301 https://yourdomain.com$request_uri;
}

server {
    listen              443 ssl;
    server_name         yourdomain.com;
    ssl_certificate     /path/to/certificate.crt;
    ssl_certificate_key /path/to/private/key.pem;
    ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;

    # do the proper handling of the request
}

请记住,这两个 yourdomain.com www.yourdomain.com 必须在你的SSL证书。这是可能的通配符证书或作为解释的服务器别名这里。在https://www.startssl.com上查看执行此操作的漂亮且免费的证书。(Edith:从Chrome版本56开始,将不再信任startssl证书。请尝试https://letsencrypt.org/。)


这实际上是可行的,但我认为可以以更清晰的方式完成,而无需大量重复的配置行。
zloynemec

@zloynemec可以将SSL内容放入单独的.conf文件中,并使用该include规则将其添加到两个SSL服务器块中。
Igettäjä

另外,如果您使用cloudflare,则需要支付$ 10 /月的证书才能重定向和代理2个子域(www + something)。让我知道是否有解决方法。
Freedo

7

在花了很多时间处理数百个类似案例之后,我提出了以下代码片段。它很短,可以很容易地进行调整以适合任何事物。

server {
    listen 80;
    listen 443 ssl;
    server_name example.com www.example.com;
    ssl_certificate /path/to/my/certs/example.com/fullchain.pem;
    ssl_certificate_key /path/to/my/certs/example.com/privkey.pem;

    # Redirect to the correct place, if needed
    set $https_redirect 0;
    if ($server_port = 80) { set $https_redirect 1; }
    if ($host ~ '^www\.') { set $https_redirect 1; }
    if ($https_redirect = 1) {
        return 301 https://example.com$request_uri;
    }

    location / {
    # ...
}

哦,但是if邪恶

是的,可以。但是它的存在是有原因的,对那些知道如何正确使用它的人不会造成伤害。;)


我喜欢这个,但是您对性能有何帮助吗?谢谢!
Freedo

1
老实说,我从来没有做过基准测试,但是我相信与单独的规则相比几乎不会产生影响,因为其效果几乎相同。
emyller

重定向基准?这不是真的相关吗?(真实的问题,而不是巨魔^^)
矩阵

3

我更喜欢返回一个响应代码,以便浏览器知道您正在将其重定向到另一个URL。

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

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

然后另一个服务器配置块 https

server {
        listen   443 ssl;
        server_name  example.com;
        ...
    }

0

为此目的如何创建服务器块:

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

然后重启nginx


重新启动时出现“服务器名冲突”错误。另外,该命令不会在端口443上侦听SSL,我也需要担心重定向https://www.example.com到该端口https://example.com
Devin

0

我认为这应该有效。

在普通的HTTP服务器定义上,建议使用类似anthonysomerset的东西,即:

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

然后在您的SSL服务器定义上:

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

这样,无论用户最初访问哪个URL,重定向仅应在每个请求中发生一次。


很好,谢谢。但是,if ($host = 'www.example.com') {由于您的正则表达式对我不起作用,因此我不得不将您的条件更改为。不知道为什么,因为它看起来正确。
Devin

请注意,如果是邪恶的,通常最好使用声明性方式。
布莱斯2015年

0

这是最终为我工作的完整示例。问题是我ssl_certificate在www重定向块中没有ssl详细信息(,等等)。请记住检查您的日志(sudo tail -f /var/log/nginx/error.log)!

# HTTP — redirect all traffic to HTTPS
server {
    listen 80;
    listen [::]:80 default_server ipv6only=on;
    return 301 https://$host$request_uri;
}

# HTTPS — redirects www to non-www
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name www.example.com;

    # Use the Let's Encrypt certificates
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # Include the SSL configuration from cipherli.st
    include snippets/ssl-params.conf;
    return 301 https://example.com$request_uri;
}

# HTTPS — proxy all requests to the app (port 3001)
server {
    # Enable HTTP/2
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name example.com sub.example.com;

    # Use the Let's Encrypt certificates
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # Include the SSL configuration from cipherli.st
    include snippets/ssl-params.conf;

    # For LetsEncrypt:
    location ~ /.well-known {
        root /var/www/html;
        allow all;
    }

    location / {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-NginX-Proxy true;
        proxy_pass http://localhost:3001;
        proxy_ssl_session_reuse off;
        proxy_set_header Host $http_host;
        proxy_cache_bypass $http_upgrade;
        proxy_redirect off;
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.