V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
NGINX
NGINX Trac
3rd Party Modules
Security Advisories
CHANGES
OpenResty
ngx_lua
Tengine
在线学习资源
NGINX 开发从入门到精通
NGINX Modules
ngx_echo
iyaozhen
V2EX  ›  NGINX

Nginx Https 跳转配置问题

  •  2
     
  •   iyaozhen ·
    iyaozhen · 2015-07-09 11:15:07 +08:00 · 10211 次点击
    这是一个创建于 3410 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我的需求:
    http://iyaozhen.com -> https://iyaozhen.com
    http://www.iyaozhen.com -> https://iyaozhen.com
    这两个已经实现:

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

    我还需要 https://www.iyaozhen.com -> https://iyaozhen.com
    按理我是需要这样配置:

    server {
            listen 443 ssl;
            listen [::]:443 ssl;
    
            server_name www.iyaozhen.com;
    
            return 301 https://iyaozhen.com$request_uri;
    }
    

    但我发现这样配置的话所以网站都不能访问了,而且我不配置的话同样能跳转过去(不知道为什么)。
    这都是小问题,更大的问题是 https://demo.iyaozhen.com/ (或者其它有设置 DNS 解析,但没在 Nginx 里面设置 server 的域名)能访问到 iyaozhen.com 的内容(https 那里有红x)。

    # prevent processing requests with undefined server names
    server {
            listen 80 default_server;
            listen [::]:80 default_server;
    
            server_name _;
    
            return 444;
    }
    

    主域 iyaozhen.com 的配置:

    server {
            listen 443 ssl spdy;
            listen [::]:443 ssl spdy;
    
            # SSL configuration
            # don’t use SSLv3 ref: POODLE
            ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
            # 省略其它的设置……
            ssl_prefer_server_ciphers on;
            ssl_session_timeout 1d;
            ssl_session_cache shared:SSL:10m;
            # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
            add_header Strict-Transport-Security max-age=15768000;
    
            server_name iyaozhen.com;
    }
    

    我发现问题的根源是简单的 listen 443 然后做跳转是不行的,必须要配置证书啥的。这怎么办?

    36 条回复    2015-07-09 23:58:57 +08:00
    FifiLyu
        1
    FifiLyu  
       2015-07-09 11:24:45 +08:00   ❤️ 1
    一个IP地址正常情况是只支持一个 https vhost,所以你访问 demo 访问到了 iyaozhen.com。这个 demo 有红叉是因为访问的域名和ssl证书认证的域名不一致。访问是demo.iyaozhen.com,证书是 www.iyaozhen.com 以及 iyaozhen.com

    如果要一个IP支持多个 https vhost,请开启 nginx 的 SNI 支持。但是,不建议这样做。因为可能访问https 都会出现红叉。
    iyaozhen
        2
    iyaozhen  
    OP
       2015-07-09 11:31:04 +08:00
    @FifiLyu 嗯,这个我知道。我是单域名证书。我不是要 https://demo.iyaozhen.com/ 能访问到我的主域博客。而是 https://demo.iyaozhen.com/ 不能访问,http://demo.iyaozhen.com/ 能正常访问其本来的站点。
    ryd994
        3
    ryd994  
       2015-07-09 11:53:37 +08:00 via Android   ❤️ 1
    要http正常的话只要正确填写server_name即可
    https的怎么都不会正常
    geekzu
        4
    geekzu  
       2015-07-09 11:58:26 +08:00 via Android   ❤️ 1
    只要你想让443 能通过https访问,必须要配置证书
    geekzu
        5
    geekzu  
       2015-07-09 11:58:41 +08:00 via Android
    即使是只做跳转
    iyaozhen
        6
    iyaozhen  
    OP
       2015-07-09 13:01:03 +08:00
    @geekzu 看来是这样。跳转这个是个小问题。但 https://demo.iyaozhen.com/ (或者其它有设置 DNS 解析,但没在 Nginx 里面设置 server 的域名)能访问到 iyaozhen.com 的内容(https 那里有红x)。这个怎么解决。
    geekzu
        7
    geekzu  
       2015-07-09 13:11:10 +08:00 via Android   ❤️ 1
    @iyaozhen 这个因为是共享ip没法解决,除非你给 demo.iyaozhen.com 也配置上ssl
    chon
        8
    chon  
       2015-07-09 13:15:47 +08:00   ❤️ 1
    @iyaozhen https连接建立是在判断server_name之前的,所以所有的https连接都会指向 iyaozhen.com
    可以加一行判断host的:
    if ($host !~* ^iyaozhen.com$ ) {
    return 404;
    }
    不知道有没有更好的方案。
    iyaozhen
        9
    iyaozhen  
    OP
       2015-07-09 13:17:06 +08:00
    @geekzu 明白了,我没有许可的证书(泛域名证书)再设置 443 端口的 default_server。单域名证书,有点略坑呀。
    iyaozhen
        10
    iyaozhen  
    OP
       2015-07-09 13:18:06 +08:00
    @chon 嗯,谢谢。明白了。
    imlonghao
        11
    imlonghao  
       2015-07-09 13:44:11 +08:00 via Android
    没人用 HSTS 做 http 跳转 https 的么?
    iyaozhen
        12
    iyaozhen  
    OP
       2015-07-09 13:55:12 +08:00
    @imlonghao 有啊,配置里面不是写了嘛。不过 IE9 等不支持 HSTS,还是要再加个 server 做跳转。
    alect
        13
    alect  
       2015-07-09 14:35:54 +08:00   ❤️ 1
    lz自己把自己坑了,申请证书的时候加上不要用裸域名而是带www的,否则有些直接给你签发的证书就只有裸域名了
    dallaslu
        14
    dallaslu  
       2015-07-09 14:47:36 +08:00   ❤️ 1
    配一个 80 端口的 server:
    server_name iyaozhen.com www.iyaozhen.com;
    if ($host ~ ^(www\.)?iyaozhen\.com) {
    rewrite ^(.*) https://iyaozhen.com$1 permanent;
    }

    只配一个 443 端口的 server:
    server_name iyaozhen.com www.iyaozhen.com;
    if ($host ~ ^www\.iyaozhen\.com) {
    rewrite ^(.*) https://iyaozhen.com$1 permanent;
    }
    if ($host !~ ^(www\.)?iyaozhen\.com) {
    rewrite ^(.*) http://$host$1 permanent;
    }

    (未经测试)
    pupboss
        15
    pupboss  
       2015-07-09 14:50:09 +08:00   ❤️ 1
    @imlonghao HSTS 在 http 下并没卵用,已经试过了
    pupboss
        16
    pupboss  
       2015-07-09 14:52:17 +08:00
    server {

    listen 80;
    server_name pupboss.com;
    return 301 https://$server_name$request_uri;
    }

    server {

    listen 80;
    listen 443 ssl;
    server_name www.pupboss.com;
    rewrite ^/(.*)$ https://pupboss.com/$1 permanent;
    }
    iyaozhen
        17
    iyaozhen  
    OP
       2015-07-09 15:02:40 +08:00
    @dallaslu 谢谢,你这种方式应该也可以。目前我是这样解决的:
    server {
    listen 80;
    listen [::]:80;

    listen 443 ssl;
    listen [::]:443 ssl;

    server_name www.iyaozhen.com;

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

    按理说 www.iyaozhen.com 是没有配置证书的,只监听 443 端口,不过实际测试发现 https://www.iyaozhen.com 能跳转到 https://iyaozhen.com

    顺带请教个问题 !~ 是 !=,~ 是 = 吗?这是什么语法?
    iyaozhen
        18
    iyaozhen  
    OP
       2015-07-09 15:08:27 +08:00
    @pupboss 好方法,我就是像你这样配置的。

    是的,HSTS 在只访问 http 时是没用的。
    HSTS 的作用应该是访问了一次 https 的网站后,浏览器检测到了这个协议,当你以后输入域名时(即使手动输入 http://)会跳转到对应的 https 网站。
    iyaozhen
        19
    iyaozhen  
    OP
       2015-07-09 15:10:21 +08:00
    @alect 应该也是给我签了带 www 的。https://www.ssllabs.com/ssltest/index.html 检测结果:
    Common names iyaozhen.com
    Alternative names iyaozhen.com www.iyaozhen.com
    Prefix handling Both (with and without WWW)
    FifiLyu
        20
    FifiLyu  
       2015-07-09 15:22:57 +08:00   ❤️ 1
    @iyaozhen 因为你只有一个 ssl 站点,任何域名解析到你的IP地址。都能通过https://test.abc.com 访问到 https://iyaozhen.com 的内容。这个你是关不掉的。

    变通的做法是,开启 SNI 支持。然后,新建一个 nginx 的 ssl 站点,root指向一个目录,目录下没有任何东西。不过,这样肯定不好。

    如果是用的 Linux ,使用 iptables 做一个域名白名单防火墙,443端口只允许 iyaozhen 域名访问。
    白名单设置参考: https://github.com/fifilyu/module-http-whitelist 中的 “代替方案”
    iyaozhen
        21
    iyaozhen  
    OP
       2015-07-09 15:28:55 +08:00
    @FifiLyu 嗯,谢谢。目前采用的是更粗暴的方法。

    iyaozhen.com 的 server 设置为 default_server 然后在里面做判断:
    if ($host != iyaozhen.com) {
    return 444;
    }
    FifiLyu
        22
    FifiLyu  
       2015-07-09 15:30:20 +08:00
    @iyaozhen 233 确实粗暴。
    iyaozhen
        23
    iyaozhen  
    OP
       2015-07-09 15:37:43 +08:00
    @pupboss 有个小小的疑问。像你那样配置,按理说 www.iyaozhen.com 是没有配置证书的,只监听 443 端口,不过实际测试发现 https://www.iyaozhen.com 能跳转到 https://iyaozhen.com。 这是为什么?
    alect
        24
    alect  
       2015-07-09 16:52:47 +08:00   ❤️ 1
    倒是对lz已经成功实现的一个功能提一个建议
    lz想把http请求都转换为https请求,个人不建议用301,而是用hsts实现
    然后就只要考虑把www请求转发到裸域就行了
    pupboss
        25
    pupboss  
       2015-07-09 18:24:35 +08:00   ❤️ 1
    @iyaozhen 我觉得这个类似脚本语言,一行一行运行,上面没错误就不报错,如果上面写了证书信息,而且是错误的,我觉得会报错,直接给 rewrite 掉,差不多就相当于程序里面的 return 了,下面再错误都没关系
    iyaozhen
        26
    iyaozhen  
    OP
       2015-07-09 18:55:12 +08:00 via Android
    @alect 嗯,我看过说不建议用 301,但我不太明白,可否解释下。我觉得这表示我的资源都是在 https 上,感觉没问题呀。

    hsts 有些浏览器不支持。其它原因在18楼有说明。
    iyaozhen
        27
    iyaozhen  
    OP
       2015-07-09 19:02:46 +08:00 via Android
    @pupboss 嗯,好像是的。而且我域名证书带 www 和不带的都可以。或者说是 Nginx 自己智能做了处理。比如说我访问 https://xxx.iyaozhen.com 用的是主域的证书,显示连接(算法)没问题,但域名没有公审记录。
    pupboss
        28
    pupboss  
       2015-07-09 19:08:09 +08:00
    @iyaozhen 哈哈,公审记录我也不知道是啥,和 Nginx 设置没多大问题,我猜有可能是 EV 证书之类的,绿色地址框,因为 Github,cat.net ,是 EV 证书,没有 '但域名没有公审记录'
    iyaozhen
        29
    iyaozhen  
    OP
       2015-07-09 19:18:47 +08:00 via Android
    @pupboss 额,我说错了,是不受信任。没有公审记录是你说的那样,另外一回事了。
    pupboss
        30
    pupboss  
       2015-07-09 19:40:27 +08:00 via iPhone
    @iyaozhen 不信任一般是证书链不对,浏览器肯定信任根的,中间的链你要手动添加到你域名证书后面
    dallaslu
        31
    dallaslu  
       2015-07-09 21:36:19 +08:00   ❤️ 1
    @iyaozhen ~ 是正则匹配的意思,!~ 就是不匹配后面的正则。上面有位V友的回复提到 !~*,其中*的意思是不区分大小写。
    dallaslu
        32
    dallaslu  
       2015-07-09 21:41:57 +08:00
    @iyaozhen 如果 http 协议通过 301 跳转到 https,那么浏览器会记住这个选择,以后再访问 http://iyaozhen.com 时,就会直接访问 https://iyaozhen.com 而不再经过服务器跳转,这里并没有任何问题。

    问题在于,如果初次通过 https 访问,但是没有设置HSTS;然后再访问 http 链接,浏览器就会正常执行一次 http 请求,顺带把之前从 https 中获取的 cookie 发出去了,这时就可能会被嗅探到。

    所以 HSTS 要比单纯的 301 要好。

    以上是个人理解。
    dallaslu
        33
    dallaslu  
       2015-07-09 21:47:16 +08:00
    @iyaozhen 没有做配置的情况下, www.iyaozhen.com 跳转到 iyaozhen.com,可能是因为 WordPress 的默认功能。
    iyaozhen
        34
    iyaozhen  
    OP
       2015-07-09 22:33:09 +08:00
    @dallaslu 原来 Nginx 正则语法是这样的。谢谢

    “顺带把之前从 https 中获取的 cookie 发出去了,这时就可能会被嗅探到。”——这个倒是没有考虑到。不过我 301 和 HSTS 都配置了。
    caola
        35
    caola  
       2015-07-09 23:21:47 +08:00   ❤️ 1
    为什么要把 80 的 443 端口的分开写两次呢?
    下面是我自己的主要配置,并开启了SPDY(www的跳转到根域名)
    ================================
    server {
    listen 80;
    listen 443 ssl spdy;
    server_name cao.la www.cao.la;
    index index.php index.html;
    root /home/wwwroot/caola;

    ssl_certificate cert/cao.la.crt;
    ssl_certificate_key cert/cao.la.pem;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #允许的协议
    ssl_ciphers AES128:AES256:GCM:!DH:!RC4:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS; #加密算法
    ssl_session_timeout 10m; #客户端会话缓存时间
    ssl_session_cache builtin:1000 shared:SSL:10m; #SSL会话缓存类型和大小
    ssl_prefer_server_ciphers on; #优化SSL,服务器密码优先于客户端
    ssl_buffer_size 1400; # 1400 bytes to fit in one MTU
    spdy_headers_comp 6; #SPDY报头压缩级别[0-9]

    add_header X-Frame-Options SAMEORIGIN; #拒绝被嵌入框架(iframe…)
    add_header Strict-Transport-Security "max-age=8640000; includeSubDomains"; #子域没有全部部署https请去掉includeSubDomains
    add_header X-Content-Type-Options: nosniff; #禁用浏览器的类型猜测

    if ($ssl_protocol = "") { return 301 https://$server_name$request_uri; }
    if ($host != 'cao.la' ) { return 301 https://cao.la$request_uri; }

    }
    iyaozhen
        36
    iyaozhen  
    OP
       2015-07-09 23:58:57 +08:00
    @caola 嗯,你这样也是好办法,在 server 配置内部做判断,然后跳转。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2851 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 47ms · UTC 02:21 · PVG 10:21 · LAX 18:21 · JFK 21:21
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.