
1. 从一次内网系统登录异常说起上周帮朋友处理他公司内部OA系统的问题现象很典型在Chrome地址栏里敲入http://106.38.235.201:7080/cas/login?servicehttp%3a%2f%2f106.38.235.201%3a7080这样的地址回车之后浏览器直接跳到了一个https://开头的页面然后就是一片空白或者证书报错登录页面根本打不开。他反复确认服务器上的CAS服务是正常跑着的用curl在命令行里请求也能拿到200响应唯独浏览器不听话。这个场景其实非常普遍。浏览器输入http网址后自动跳转https是近几年Chrome、Edge、Firefox等主流浏览器陆续强化的一个默认行为。它的出发点很好——推动全网加密、减少明文传输带来的隐私泄露风险。但对于大量还在用http协议的内网系统、老旧业务平台、开发测试环境来说这个好心的默认行为直接变成了拦路虎。你明明想访问的是http浏览器却自作主张给你换成https结果就是连接失败、页面打不开、接口调不通。这篇文章就是围绕这个具体问题展开的。我会把浏览器为什么会跳、在哪些情况下跳、怎么判断是浏览器跳的还是服务器跳的、以及针对不同场景个人开发、企业内网、批量部署分别怎么解决一层一层讲清楚。不管你是刚入行的运维、被内网系统折磨的开发还是只想让自己电脑上某个http页面能正常打开的普通用户都能从里面找到可以直接抄作业的方案。核心关键词就三个http、https、浏览器跳转我们围绕它们把这件事彻底说透。2. 浏览器到底在什么条件下会把http换成https很多人以为浏览器是看到http就跳https其实没这么粗暴。Chrome从很早就开始推进这件事但触发条件是分层的不同版本、不同设置、不同域名状态下行为完全不一样。搞清楚触发条件才能对症下药。2.1 HSTS最强势也最容易被忽略的机制HSTS全称HTTP Strict Transport Security是服务器通过响应头Strict-Transport-Security告诉浏览器以后访问我一律用https的一种策略。一旦浏览器记住了某个域名的HSTS策略在有效期内由max-age指定你手动输入http://它也会在发出请求之前就在本地把协议改成https连一次http请求都不会发出去。这就是为什么有些站点你清空了地址栏重新输http也没用——跳转发生在浏览器内部根本没到服务器。HSTS的典型响应头长这样Strict-Transport-Security: max-age31536000; includeSubDomains; preloadmax-age31536000表示记住一年includeSubDomains表示所有子域名一并生效preload表示申请加入浏览器内置的预加载列表。一旦进了preload列表那更是出厂就带着用户想改都改不了。判断某个域名是否被HSTS记住Chrome里可以访问chrome://net-internals/#hsts在Query HSTS/PKP domain里输入域名查询。如果查得到说明本地已经存了策略。这个页面同时也是删除策略的入口后面讲解决方案时会用到。2.2 HTTPS-First模式Chrome近几年的默认升级行为从Chrome 115左右开始Google逐步推行HTTPS-First模式。它的逻辑是当你在地址栏输入一个没有指定协议的地址或者输入http地址时浏览器会先尝试https如果https连不上比如超时、证书错误、连接被拒再回退到http。注意这里的顺序——先https后http。对于纯http的内网服务器https端口根本没开浏览器尝试https会失败理论上应该回退。但实际中经常出现的情况是服务器443端口被别的服务占着或者有个中间设备响应了https握手但证书不对浏览器就卡在https那一步既不成功也不干脆失败于是页面就白屏或者报证书错误。这个模式在不同Chrome版本里开关状态不一样可以通过chrome://flags/#https-first-mode查看和调整。不过要注意flags里的选项会随版本更新被移除或改名不能作为长期方案。2.3 地址栏输入行为与总是使用安全连接设置Chrome设置里有一个选项叫总是使用安全连接Always use secure connections路径在设置 → 隐私和安全 → 安全。开启后浏览器会主动把http升级为https升级失败时给出警告页面而不是静默回退。这个设置和HTTPS-First有重叠但不等同。它的存在意味着即使某个域名没有HSTS记录只要这个开关开着浏览器也会尝试升级。很多用户是在不知情的情况下被系统更新或者某些软件顺手打开的这个开关。2.4 服务器端301/302跳转和浏览器行为要区分开还有一种跳转根本不是浏览器干的而是服务器返回了301或302重定向Location头指向https地址。这种情况用浏览器的开发者工具看Network面板第一个请求的响应状态码就是301/302Response Headers里有Location: https://...。区分这两种情况非常关键现象浏览器端跳转HSTS/HTTPS-First服务器端跳转301/302Network面板首个请求直接就是https请求看不到http请求能看到http请求响应301/302响应头无特殊标记有Location指向https清除浏览器HSTS后可能恢复正常依然跳转换一个浏览器测试可能不跳取决于该浏览器策略依然跳转我见过不少人折腾半天浏览器设置最后发现是Nginx配置里写了一句return 301 https://$host$request_uri;把http流量全导走了。所以排查第一步永远是打开开发者工具看第一个请求到底是什么协议、响应码是多少。3. 一步步定位到底是浏览器跳的还是服务器跳的排查这类问题最忌讳上来就改配置。我习惯按下面的顺序走一遍基本十分钟内能定位到根因。3.1 用开发者工具锁定跳转发生的位置打开Chrome按F12调出开发者工具切到Network面板勾选Preserve log保留日志然后在地址栏输入http地址回车。观察请求列表如果列表里只有一条https请求没有任何http记录说明跳转发生在浏览器发出请求之前基本可以判定是HSTS或HTTPS-First。如果列表里先有一条http请求状态码301或302然后才是https请求说明是服务器端重定向。如果http请求状态码是307或308同样是服务器端重定向只是语义上更严格307/308不允许改变请求方法。这一步做完方向就定了一半。3.2 用curl绕开浏览器验证服务器真实行为浏览器有各种缓存和策略命令行工具最干净。在终端里执行curl -I http://106.38.235.201:7080/cas/login-I表示只取响应头。看返回的Status Code和Location头返回200没有Location说明服务器本身不跳转问题在浏览器端。返回301或302Location指向https说明服务器在跳。返回Connection refused或超时说明http端口本身就没通那是另一个问题。再补一条强制用https请求看看curl -Ik https://106.38.235.201:7080/cas/login如果这条报证书错误或者连接失败而http那条是200那就彻底确认了服务器只提供http服务浏览器却硬要往https上撞。3.3 查HSTS记录和浏览器安全设置如果curl确认服务器不跳转那就回到浏览器端查两件事第一访问chrome://net-internals/#hsts在查询框输入域名注意HSTS是按域名记录的IP地址通常不会被HSTS记住但域名会。如果查到记录就是HSTS在作祟。第二检查设置 → 隐私和安全 → 安全 → 总是使用安全连接是否开启。开启的话先关掉试试。3.4 换浏览器和换设备做交叉验证同一个地址用Firefox或者Edge打开看看。如果Firefox不跳、Chrome跳那基本锁定是Chrome的策略问题。如果所有浏览器都跳那更可能是服务器端重定向或者网络中间设备比如某些企业网关会做协议升级。这个交叉验证能帮你排除掉是不是我这一台机器的问题也能避免在错误的方向上浪费时间。提示排查时一定要用无痕窗口CtrlShiftN再测一遍。无痕窗口不加载已有缓存和部分站点数据能排除缓存干扰。但注意HSTS记录在无痕模式下是独立的无痕窗口里不跳不代表正常窗口里不跳。4. 针对不同场景的解决方案清单定位清楚之后解决方案就有的放矢了。我把常见场景分成四类每类给具体操作。4.1 个人开发测试快速让http页面能打开如果你只是自己开发调试需要临时访问某个http地址最直接的办法是清除该域名的HSTS记录。打开chrome://net-internals/#hsts找到Delete domain security policies区域输入域名点Delete。删完之后再访问http地址浏览器就不会再强制升级了。如果总是使用安全连接开着去设置里关掉。路径设置 → 隐私和安全 → 安全 → 总是使用安全连接关闭开关。对于HTTPS-First模式可以临时通过flags调整访问chrome://flags/#https-first-mode把它设为Disabled重启浏览器。但我要强调这只是临时手段Chrome版本更新后这个flag可能消失不能依赖。还有一个更省事的办法直接在地址栏输入完整协议。比如输入http://106.38.235.201:7080/cas/login而不是106.38.235.201:7080/cas/login。对于没有HSTS记录的站点明确指定http协议通常能阻止浏览器的自动升级。但如果有HSTS记录这招无效。4.2 企业内网系统从服务器和客户端两头下手企业内网系统往往涉及几十上百台终端不可能让每个用户去改浏览器设置。这时候要从两个层面解决。服务器层面如果这个系统确实只提供http服务那就确保服务器不要发送任何HSTS头也不要做http到https的重定向。检查Nginx/Apache/IIS配置里有没有类似下面的语句有就删掉或注释# Nginx中常见的强制跳转配置内网http系统要移除 # return 301 https://$host$request_uri; # add_header Strict-Transport-Security max-age31536000 always;客户端层面如果浏览器策略已经生效可以通过组策略Windows域环境统一配置Chrome的策略。Chrome支持通过注册表或组策略模板管理其中有一项HSTSPolicyBypassList可以把指定域名加入HSTS绕过列表。具体是在组策略里配置Chrome的HSTSPolicyBypassList策略填入内网域名这样即使有HSTS记录也会被绕过。对于没有域控的环境可以做一个注册表文件批量导入Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Google\Chrome] HSTSPolicyBypassListexample.internal,oa.company.local导入后重启Chrome生效。这个方案的好处是集中管理不用逐台机器改。4.3 老旧业务平台用反向代理做协议适配有些老系统改不动服务器就是纯http但用户浏览器又越来越激进。这种情况下比较优雅的方案是在前面加一层反向代理由代理来同时提供http和https并把https流量转成http回源。以Nginx为例配置大致是这样server { listen 80; server_name oa.internal; # http直接放行不做跳转 location / { proxy_pass http://127.0.0.1:7080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } } server { listen 443 ssl; server_name oa.internal; ssl_certificate /etc/nginx/certs/oa.crt; ssl_certificate_key /etc/nginx/certs/oa.key; location / { proxy_pass http://127.0.0.1:7080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }这样无论用户访问http还是https都能正常打开后端老系统不用动。证书可以用自签的内网环境配合把根证书推送到客户端信任列表即可。这个方案我在好几个客户现场用过稳定性很好唯一要注意的是自签证书的信任分发。4.4 批量终端用启动参数或策略统一处理如果只是少数几台机器需要临时处理可以给Chrome快捷方式加启动参数。右键Chrome快捷方式 → 属性 → 在目标末尾加上--unsafely-treat-insecure-origin-as-securehttp://106.38.235.201:7080这个参数的作用是把指定源当作安全源对待能解决一部分因为不安全来源导致的限制。但要注意这个参数主要影响的是部分Web API的可用性对HSTS强制跳转的绕过效果有限不能包治百病。更彻底的是用前面说的组策略方案。如果是Mac环境可以用配置描述文件.mobileconfig来管理Chrome策略原理类似。5. 几个容易踩的坑和实测经验这块是我这些年处理这类问题攒下来的实战心得很多是文档里不会写的。5.1 清HSTS不等于万事大吉很多人清完HSTS记录发现还是跳。原因通常是includeSubDomains导致子域名也被记录你只删了主域名或者preload列表里的域名本地删除根本没用因为浏览器内置列表里就有。preload列表是编译进浏览器二进制的用户端无法删除只能等Google从列表里移除。判断方法在chrome://net-internals/#hsts查询时如果显示domain is in the preload list那就是内置的本地操作无效。5.2 IP地址和域名的行为不一样HSTS策略是按域名记录的纯IP地址通常不会被HSTS记住。所以如果你的系统是用IP访问的浏览器跳转更可能是HTTPS-First模式或者总是使用安全连接导致的而不是HSTS。这个区别决定了你该去改哪里。我见过有人在chrome://net-internals/#hsts里反复查一个IP查不到就懵了其实方向从一开始就错了。5.3 端口号会影响判断http默认80端口https默认443端口。但内网系统经常用非标准端口比如7080。当浏览器尝试把http升级为https时端口号的处理规则是如果原地址显式写了端口升级后端口保持不变。也就是说http://host:7080会被尝试升级为https://host:7080而不是https://host:443。如果服务器7080端口只跑http那https握手自然失败。理解这一点能帮你快速判断为什么升级会失败。5.4 缓存和Service Worker的干扰有些站点注册了Service Worker即使你清了HSTSService Worker里可能还缓存着跳转逻辑。排查时在开发者工具的Application面板里找到Service Workers勾选Update on reload或者直接Unregister再测。另外chrome://settings/clearBrowserData里清除缓存的图片和文件以及Cookie及其他站点数据也有帮助。5.5 不同Chrome版本行为差异明显Chrome 109最后一个支持Win7的版本和Chrome 120在HTTPS-First的默认行为上就有区别。企业环境里如果终端Chrome版本不统一会出现有的机器跳有的不跳的诡异现象。建议先统一版本再统一策略否则排查起来会被版本差异带偏。我一般会先让用户报一下chrome://version里的版本号心里有个底。6. 从根上理解http和https的区别决定了这一切要真正搞明白浏览器为什么这么执着于https得回到http和https的本质区别上。http是明文传输请求和响应在网络上裸奔中间任何一个节点都能看到内容、甚至篡改内容。https在http和TCP之间加了一层TLS做了加密和身份验证。对于涉及登录、支付、个人信息的场景https是必须的。浏览器的所有强制升级行为本质上都是在推动这个安全底线。但问题在于安全是有成本的。https需要证书、需要TLS握手、需要服务器配置。对于纯内网、物理隔离、或者只是开发测试的环境这个成本有时候是不必要的。浏览器的策略是一刀切地假设所有http都不安全这就和实际场景产生了冲突。理解了这层矛盾你就明白为什么解决方案要么是让服务器支持https顺应浏览器要么是让浏览器放行特定http绕过策略。没有第三种魔法。选择哪条路取决于你的场景能上https就上https这是长久之计实在上不了就用策略绕过但要清楚这是权宜之计浏览器未来只会越来越严格。我在实际项目里的做法是新系统一律https老系统能改造就改造实在改不动的用反向代理兜底同时把绕过策略作为最后手段并且记录在案定期review。这样既保证了当下的可用性也不至于积累一堆技术债。最后分享一个我常用的快速判断口诀先看Network再跑curl查HSTS关安全连接换浏览器验证。这五步走完九成以上的http跳https问题都能定位到根因。剩下的就是根据场景选方案了。