Nginx 网站实现 HTTP 和 HTTPS 同时访问并自动转换

kooglezhang · 2016年12月23日 · 最后由 starshine 回复于 2016年12月26日 · 18677 次阅读

个人的视频小站 www.koogle.cc 由于升级为 HTTPS 后,视频播放页由于第三方视频资源 为 HTTP, 导致浏览器地址红色提示

不得已必须要同时兼容 HTTP 和 HTTPS, 但是又不想网站退回到 HTTP, 所以在 NGINX 同时监听 80 和 433 端口,

但是但是其他地址除了播放页意外,如果是来自 HTTP 的请求,都要自动跳转到 HTTPS 协议进行访问

由于 NGINX 的条件判断 不直接支持 多条件联合,即不像 ruby 那样可以

 if condition1 && condition2 
   # do something
end

所以在做 rewrite 的时候,需要将单条件输出,拼写到下一个条件的结果里面,如:我想要将所有 播放页 https://www.koogle.cc/show/T76YxailMzhd.html

转换为 HTTP,刚开始想到了用正则表达式

if ($request_uri ~  ^/show/(.*)$ ) {
    rewrite ^/(.*)$ http://www.koogle.cc/$1  permanent; 
}

重新部署重启后,页面提示有多重跳转,后来发现不论是 HTTP 和 HTTPS 都有符合这个条件,所有就会出错,

所以应该是指在 HTTPS 模式下才执行这个操作,所以还要加上对 HTTPS 作为条件去并行

if ($request_uri ~  ^/show/(.*)$ ) {
    set $show "show";
}

if ($scheme = 'https') {
    set $show "${show}https";
}

if ($show = "showhttps") {
   rewrite ^/(.*)$ http://www.koogle.cc/$1 permanent;
}

完成了这一步以后,终于可以安心的观看视频,但是紧接着我又遇到一个烦恼的痛苦,因为这个页面除了播放以外,

还有其他页面链接,如登录,栏目,这些链接都 变成 http://www.koogle.cc, 这样跳转过去的链接又成了原来的 HTTP,必须得再做一步判断,条件刚好相反

if ($request_uri !~ ^/show/(.*)$) {
    set $not_show "notshow";
}

if ($scheme = 'http') {
    set $not_show "${not_show}http";
}

if ($not_show = "notshowhttp") {
    rewrite ^/(.*)$ https://www.koogle.cc/$1 permanent;
}
huacnlee [该话题已被删除] 提及了此话题。 12月23日 15:33

搞复杂了,Rails 里面启用 force_ssl 然后将 80 和 443 放到同一个 server block 里面,跳转的事情就搞定了。

config/environments/production.rb

# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
config.force_ssl = true

Nginx 配置:

server {
  listen 80 default_server;
  listen 443 ssl;
  ...
}

#2 楼 @huacnlee 之前是全部链接都是 HTTPS 设置的,但是后来因为实际问题,得做兼容性退步

#2 楼 @huacnlee 一般在生产服务器上,我偏好使用云主机服务商提供的负载均衡服务(LB,LoadBalance),然后把证书部署在 LB 上,访问路径一般是 HTTPS -> LB -> HTTP -> Router -> VPS 这样,好处是整个后端全部走 HTTP,不操心自己手动配置 nginx 证书,也没有 HTTPS 带来的开销,关键是点击下鼠标就搞定了。

兼容 HTTP 的话,看需求,使用 Nginx 代理,80 端口来的请求直接重定向到 443。 @huacnlee 改 Rails 配置是一种方式,但个人观点不太建议。下面给你贴出个例子参考:

server {
  listen 443 ssl;

  listen [::]:443 ssl ipv6only=on;

  server_name example.com;

  ssl on;

  ssl_certificate /etc/nginx/sslkey/your-ssl.pem;

  ssl_certificate_key /etc/nginx/sslkey/your-ssl.key;

  ssl_session_timeout 5m;

  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

  ssl_ciphers AESGCM:ALL:!DH:!EXPORT:!RC4:+HIGH:!MEDIUM:!LOW:!aNULL:!eNULL;

  ssl_prefer_server_ciphers on;

  root   /home/www/path;
  location / {
    index index.php  index.html index.htm;
    try_files $uri $uri/ /index.php?$query_string;
  }
  error_page  404              /404.html;
  error_page   500 502 503 504  /50x.html;
  location ~ \.php$ {
    fastcgi_pass   127.0.0.1:9000;
    fastcgi_index  index.php;
    fastcgi_param  SCRIPT_FILENAME   $document_root$fastcgi_script_name;
    include        fastcgi_params;
  }
}
server {
  listen 80;
  listen [::]:80 ipv6only=on;
  server_name     www.example.com example.com;
  return 301 https://example.com$request_uri;
}

不建议在 nginx 中用 if,楼上正解。

#5 楼 @tkvern 您好,今天将您的这个方案部署到线上后,省去了冗余的条件判断,确实比我之前的那个方法实现起来更加的简洁实用,谢谢了

@tkvern 您好,我的配置时不使用 默认 80 端口,而是使用 其他端口 24790,怎么实现 http 和 https 同时兼容访问???

#8 楼 @starshine 配置中listen相应的端口即可

@tkvern 你好,我下面的配置 搞了很久了 没有搞定,不知道怎么配置才能实现既能 https 访问 又能 http 访问,请帮忙看看吧,谢谢。

server {

listen 443 ssl;

listen 24780;

ssl on;

ssl_certificate /opt/zs/33iq.crt;

ssl_certificate_key /opt/zs/33iq.key;

root /var/www/demo/current/;

location / {

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

proxy_set_header Host $http_host;

proxy_pass http://star;

}

}

server {

listen 24780;

server_name demo.star.net:24780;

return 301 https://${server_name}$request_uri;

}

需要 登录 后方可回复, 如果你还没有账号请 注册新账号