我想在 nginx 中创建一条规则,执行两件事:
- 从请求 URI 中删除“www.”
- 如果请求 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)不循环。有什么想法吗?
答案1
实现此目的的最佳方法是使用三个服务器块:一个用于将 http 重定向到 https,一个用于将 https www-name 重定向到 no-www,一个用于实际处理请求。使用额外的服务器块而不是 if 的原因是服务器选择是使用哈希表执行的,并且速度非常快。使用服务器级 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
这对我有用:
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获得可实现此功能且优质的免费证书。(伊迪丝:从 Chrome 版本 56 开始,startssl 证书将不再受信任。尝试https://letsencrypt.org/反而。)
答案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
是邪恶的!
是的,它能是。但它的存在是有原因的,并且不会对那些知道如何正确使用它的人造成伤害。;)
答案4
如果你有多个域名并且您正在寻找更多通用方法在不损失性能且不一直列出所有域的情况下,请检查这一点。
怎么运行的?
- 监听 80 并将所有 http 重定向到 https - 包括
http://www.
将转到https://www.
- 监听 433,但仅限于
www
服务器名称并重定向到非www
使用正则表达式 - 监听每个非
www
服务器名称的 433 - 所有流量都将流向这里
快嗎?
是的!虽然我们使用了 RegExp,但它只在www
-versions 块中返回 301。因此,所有正常流量都将被处理,而无需任何额外的处理成本。
# Redirect everything to HTTPS (including "www")
server {
listen 80 default_server;
listen [::]:80 default_server;
return 301 https://$host$request_uri;
}
# Redirect away from "www" versions:
server {
listen [::]:443 ssl http2;
listen 443 ssl http2;
server_name www.example-1.com
www.example-2.com
www.example-3.com;
# using generic "www" removal // https://stackoverflow.com/questions/11323735/nginx-remove-www-and-respond-to-both/45676731#45676731
if ( $host ~ ^www\.(.+)$ ) {
set $without_www $1;
rewrite ^ $scheme://$without_www$uri permanent;
}
# SSL settings:
ssl_certificate /etc/letsencrypt/live/example/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/example/privkey.pem; # managed by Certbot
}
# And finally one "server" block for each of your domains
server {
listen [::]:443 ssl http2;
listen 443 ssl http2;
server_name example-1.com;
# SSL settings, etc...
}
server {
server_name example-2.com;
# ... listen, ssl, etc...
}
server {
server_name example-3.com;
# ... etc
}
附言:如果您需要 SSL 设置方面的帮助,请查看Mozilla SSL 配置生成器:
https://ssl-config.mozilla.org/