我想在nginx中创建一个执行以下两项操作的规则:


删除“ www”。从请求URI
,如果请求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。似乎应该是¯\ _(ツ)_ /¯

#1 楼

实现此目的的最佳方法是使用三个服务器块:一个将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>
}


评论


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

– pbreitenbach
2011年5月16日20:39

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

– kolbyjack
2011年5月17日在1:03



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

–独眼巨人
2011年6月9日在21:13

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

–ricka
16年5月6日在1:20

尝试这个答案,我遇到了另一个问题。以为我可以将301从www.sub.example.com重定向到sub.example.com,然后仅获取sub.example.com的SSL证书现在,我知道ssl证书检查发生在301重定向之前,因此它无法工作。此处提供更多说明:serverfault.com/a/358625/144811

–烦恼
16年11月24日在18:08

#2 楼

这对我有用:

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.comwww.yourdomain.com都必须在您的SSL证书中。使用通配符证书或服务器备用名称(如此处所述)是可行的。在https://www.startssl.com上查看执行此操作的漂亮且免费的证书。 (伊迪丝:从Chrome版本56开始,不再受信任startssl证书。请改用https://letsencrypt.org/。)

评论


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

– zloynemec
17年9月18日在8:48

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

–Igettäjä
18年4月2日在9:03

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

– Freedo
18-09-24在4:17

#3 楼

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

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
18-09-24在4:17

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

– emyller
18-09-24在20:38

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

–矩阵
18-11-23在4:42

#4 楼

我更喜欢返回一个响应代码,以便浏览器知道您将其重定向到另一个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;
        ...
    }


#5 楼

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

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


,然后重新启动nginx

评论


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

–德文
2011年4月11日在16:56



#6 楼

我认为这应该可行。

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

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


然后在SSL服务器上定义:

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


这样,无论用户最初访问哪个URL,重定向都应仅对每个请求执行一次。

评论


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

–德文
2011年4月11日18:20



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

–布莱斯(Blaise)
15年3月21日在6:35

#7 楼

这是最终为我工作的完整示例。问题是我在www重定向块中没有ssl详细信息(ssl_certificate等)。记住要检查您的日志(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;
    }
}