
1. 从“网景”这个名字说起它到底给互联网留下了什么聊到互联网的起源很多人第一反应是各种大厂的名字但真正把“上网”这件事从实验室搬到普通人桌面上的是网景Netscape。我第一次接触网页开发的时候老师讲的第一句话就是“没有网景你现在可能还在终端里敲命令看文本。”这话听起来夸张但细想一下网景当年做的几件事几乎定义了今天互联网的底层玩法。网景公司最核心的贡献不是某一款浏览器本身而是它把HTML、JavaScript、SSL这三样东西串成了一条完整的链路。HTML负责内容结构JavaScript负责交互逻辑SSL负责传输安全。这三者组合在一起才让“网页”从一个静态文档变成了可以交互、可以交易、可以信任的载体。今天你在浏览器里看到的每一个页面背后都跑着这套逻辑。这篇文章适合谁看如果你是刚入行的前端或者运维想搞清楚为什么有些“老古董”技术到现在还绕不开那这篇内容能帮你把脉络理清。如果你已经工作几年但一直没弄明白SSL握手为什么老出问题、img标签为什么有时候加载不出来、JavaScript的事件机制到底怎么跑那这篇也能给你一些实操层面的参考。我会从网景的历史切入然后落到具体的HTML、JavaScript、SSL实操细节上最后分享一些排查问题的经验。提示本文涉及的技术点都是当前主流开发中高频出现的尤其是SSL相关错误和img资源加载问题几乎每个项目都会遇到。2. 网景的技术遗产HTML、JavaScript、SSL是怎么被串起来的2.1 HTML从“超文本”到“页面骨架”的进化网景当年推动HTML标准的时候核心思路很简单让文档之间可以互相跳转。这就是“超文本”的原始含义。但后来事情变得复杂了因为人们不满足于只放文字还要放图片、表格、表单。于是img标签出现了table出现了form出现了。今天你写一个最基础的HTML页面开头一定是!DOCTYPE html然后html langzh-CN接着head里放meta charsetUTF-8。这套结构看起来理所当然但在网景那个年代浏览器之间的兼容性是一团乱麻。网景和另一家浏览器厂商各自实现了一套标签开发者得写两套代码。后来W3C介入才慢慢统一了标准。我实际做项目的时候发现很多人对img标签的理解停留在“放个图片地址就行”。但真正要控制图片加载行为得关注几个属性src、alt、loading、decoding。比如loadinglazy可以让图片滚动到可视区域再加载这对长页面性能提升非常明显。还有srcset和sizes可以根据屏幕宽度加载不同分辨率的图片移动端能省不少流量。注意img标签的src如果写的是相对路径在Django的static文件夹里经常显示不出来因为Django的静态文件服务需要配置STATIC_URL和STATICFILES_DIRS。这个问题后面会详细讲。2.2 JavaScript让页面“活”起来的脚本语言网景当年招了一个人叫Brendan Eich给了他十天时间设计一门脚本语言。结果他搞出了JavaScript。名字里带“Java”纯粹是营销需要跟Java本身关系不大。但这门语言后来成了浏览器里唯一能跑的脚本语言直接统治了前端世界。JavaScript最早的作用很简单验证表单、弹个提示框、改个图片。但今天它已经能操作DOM、发网络请求、处理音视频、甚至通过Node.js跑在服务器上。你看到的那些热词比如javascript:document.querySelector(video).dispatchEvent(new Event(ended))就是典型的DOM操作——找到视频元素然后手动触发一个“播放结束”事件。我刚开始学JavaScript的时候最头疼的是事件机制。比如你想在视频播完后做点什么不能直接写video.onended function(){}就完事因为有些浏览器对事件触发时机处理不一样。更稳的做法是用addEventListener然后手动dispatchEvent来模拟。这个技巧在自动化测试里特别有用。还有javascript:v document.querySelector(video);v.style.rotate -90deg这种写法是在浏览器地址栏里直接执行脚本。虽然现在很多浏览器限制了这种用法但在调试的时候偶尔还能派上用场。不过正式项目里千万别这么干安全风险太大。2.3 SSL从“可选项”到“强制项”的安全层网景当年发明SSL的时候目的是让浏览器和服务器之间的通信加密。没有SSL你输入的密码、信用卡号在网络上就是明文传输随便一个中间节点都能截获。SSL后来演变成了TLS但大家习惯还是叫SSL。今天你访问任何网站如果地址栏没有那个小锁图标浏览器就会提示“不安全”。Chrome甚至直接把HTTP网站标记为“不安全”。这就是SSL普及的结果。但SSL配置起来坑特别多热词里出现的net::ERR_SSL_PROTOCOL_ERROR、curl: (35) error:0a000126:SSL routines::unexpected eof while reading、MySQL SSL连接错误都是实际开发中高频遇到的问题。我印象最深的一次是帮朋友排查一个本地开发环境的问题。他用https://localhost:8889/img/banner.jpg加载图片结果浏览器报net::ERR_SSL_PROTOCOL_ERROR。原因很简单本地服务根本没有配置SSL证书但他在代码里写了https://。改成http://就好了。但如果是生产环境就必须配证书不能偷懒。提示本地开发用自签名证书是可以的但浏览器会提示不安全。可以用mkcert工具生成本地信任的证书比手动配置OpenSSL命令方便得多。3. 实操环节从零搭一个带SSL的本地图片服务3.1 环境准备与工具选型假设你要在本地跑一个服务用来提供图片资源并且要支持HTTPS。你需要准备这些东西一台开发机Windows、macOS、Linux都行Node.js或者Python用来起服务OpenSSL或者mkcert用来生成证书一个用来测试的HTML页面我选Node.js因为起服务快而且https模块是内置的。如果你用Python也可以用http.server加ssl模块但配置稍微麻烦一点。先检查Node.js版本node -v如果版本低于14建议升级。然后创建一个项目目录mkdir local-ssl-demo cd local-ssl-demo3.2 生成本地自签名证书用mkcert是最省事的。安装mkcert之后执行mkcert -install mkcert localhost 127.0.0.1 ::1这会生成两个文件localhost2.pem和localhost2-key.pem。前者是证书后者是私钥。如果你不想装mkcert用OpenSSL也行openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes -subj /CNlocalhost这条命令会生成key.pem和cert.pem。-nodes表示不加密私钥方便本地开发用。-days 365表示有效期一年。注意自签名证书在浏览器里会显示“不安全”因为浏览器不信任它。但用来本地测试足够了。如果想让浏览器信任还是用mkcert。3.3 写一个最简单的HTTPS图片服务创建一个server.js文件const https require(https); const fs require(fs); const path require(path); const options { key: fs.readFileSync(key.pem), cert: fs.readFileSync(cert.pem) }; const server https.createServer(options, (req, res) { if (req.url /img/banner.jpg) { const imgPath path.join(__dirname, img, banner.jpg); fs.readFile(imgPath, (err, data) { if (err) { res.writeHead(404); res.end(Not Found); return; } res.writeHead(200, { Content-Type: image/jpeg }); res.end(data); }); } else { res.writeHead(200, { Content-Type: text/html }); res.end( !DOCTYPE html html langzh-CN head meta charsetUTF-8 titleSSL图片测试/title /head body h1SSL图片加载测试/h1 img srchttps://localhost:8889/img/banner.jpg altbanner stylewidth:100%; /body /html ); } }); server.listen(8889, () { console.log(HTTPS服务已启动https://localhost:8889); });然后在项目目录下创建img文件夹放一张banner.jpg进去。运行node server.js打开浏览器访问https://localhost:8889你应该能看到图片正常加载。如果报net::ERR_SSL_PROTOCOL_ERROR检查一下证书路径对不对或者端口是不是被占用了。3.4 用curl验证SSL连接有时候浏览器报错信息不够详细可以用curl来测curl -v https://localhost:8889/img/banner.jpg如果证书是自签名的curl会报错curl: (60) SSL certificate problem: self signed certificate加-k参数跳过验证curl -k -v https://localhost:8889/img/banner.jpg如果还是报curl: (35) error:0a000126:SSL routines::unexpected eof while reading那可能是服务端没有正确响应SSL握手。检查一下Node.js的https.createServer有没有传对key和cert。提示unexpected eof while reading这个错误通常意味着服务端在SSL握手过程中断开了连接。可能是证书格式不对或者私钥和证书不匹配。用openssl x509 -noout -modulus -in cert.pem | openssl md5和openssl rsa -noout -modulus -in key.pem | openssl md5对比一下两个MD5值应该一样。4. 常见问题排查SSL、img、JavaScript的那些坑4.1 SSL连接错误速查表错误信息可能原因解决方法net::ERR_SSL_PROTOCOL_ERROR用HTTPS访问了HTTP服务检查服务是否支持HTTPS或改用HTTPcurl: (35) error:0a000126:SSL routines::unexpected eof while reading证书或私钥不匹配重新生成证书确保key和cert配对MySQL SSL连接错误MySQL服务端未启用SSL或证书配置错误检查MySQL的ssl_ca、ssl_cert、ssl_key配置no required SSL certificate was sent客户端未提供证书在客户端配置中指定证书路径SSL/TLS协议信息泄露漏洞(CVE-2016-2183)使用了不安全的加密套件禁用3DES等弱加密算法升级TLS版本warning: disabling truststore since SSL support is missingPython的ssl模块未安装安装openssl-devel后重新编译Python这个表是我在实际运维中慢慢攒出来的基本上覆盖了八成以上的SSL报错。遇到问题先查表能省不少时间。4.2 img标签加载失败的几种情况img标签看起来简单但坑不少。我遇到过的情况包括路径错误src写的是相对路径但页面在另一个目录下。用绝对路径或者/static/img/banner.jpg这种形式。Django静态文件配置问题在Django里img src{% static img/banner.jpg %}需要先{% load static %}并且settings.py里要配好STATIC_URL和STATICFILES_DIRS。SSL混合内容页面是HTTPS但图片是HTTP浏览器会阻止加载。全部改成HTTPS。图片格式不支持有些浏览器不支持WebP或者AVIF用picture标签做降级。CORS问题图片来自另一个域名但没有设置Access-Control-Allow-Origin。这个在Canvas操作图片时会遇到。注意img标签的alt属性不只是给盲人用的图片加载失败时也会显示alt文字。所以别偷懒认真写alt。4.3 JavaScript事件与DOM操作的常见误区热词里有个例子javascript:document.querySelector(video).dispatchEvent(new Event(ended))。这是在手动触发视频结束事件。但实际用的时候要注意querySelector返回的是第一个匹配的元素如果页面上有多个视频得用querySelectorAll然后遍历。dispatchEvent触发的事件是“合成事件”isTrusted属性是false。有些浏览器对合成事件有限制比如不能触发全屏。如果视频还没加载完就触发ended可能会报错。最好先判断readyState。还有javascript:var total50,used[];function refreshTip(){document.getElementById...}这种写法是在地址栏里执行一段脚本。现在Chrome和Firefox都限制了javascript:协议的用法因为容易被用来做XSS攻击。正式项目里千万别这么写。我个人的经验是能用addEventListener就别用onclick属性因为前者可以绑定多个处理函数后者会覆盖。而且addEventListener支持事件捕获和冒泡阶段控制更精细。5. 从网景到今天那些没变的东西网景公司虽然早就没了但它留下的技术栈还在跑。HTML还是那个HTMLJavaScript还是那个JavaScriptSSL变成了TLS但大家还是叫SSL。变的只是工具和框架底层逻辑没怎么动。我有时候会想如果网景当年没有把JavaScript开源没有推动SSL标准化今天的互联网会是什么样。可能每个浏览器厂商都有一套自己的脚本语言开发者得写五份代码。可能网上购物还得打电话确认信用卡号。这些“可能”想想就头疼。所以回到标题那句话“请永远记住网景公司互联网的缔造者。”这话不算夸张。它不只是做了一个浏览器而是把互联网的三大基石——内容、交互、安全——给串起来了。你今天写的每一行HTML、每一个JavaScript函数、每一次SSL握手背后都有网景的影子。最后分享一个小技巧如果你在本地开发时遇到SSL证书问题又不想折腾OpenSSL命令直接用mkcert。装完之后mkcert -install然后mkcert localhost两分钟搞定。比手动生成证书省事多了。我在好几个项目里都这么干实测很稳。