CSP内容安全策略:前端XSS防护的终极白名单指南
先泼盆冷水一搜“CSP”中文互联网上先撞出来的大概率是某场算法认证、某个软件的“自动保存路径”甚至可能是 3D 打印切片软件的配置项。这些我都不聊今天只聊安全圈里那个让前端开发者又爱又恨的 CSP——Content Security Policy内容安全策略。它不是什么新框架也不是加密算法而是一组由浏览器执行的规则。页面拿到这个响应头之后会严格按照规则去决定“哪些来源的脚本能执行、哪些图片能加载、哪些页面能嵌套我、form 能不能往陌生域名提交”。说直白一点CSP 是现代前端防 XSS 的最后一道防线也是很多安全评审里必查的响应头之一。这篇文章适合谁如果你是前端开发、安全运维或者正在给老项目做安全加固那这篇能帮你把 CSP 从“看到就头疼”变成“照着抄就行”。我会把策略结构、指令含义、接入方式、踩坑记录一次讲清楚。1. 先弄清楚 CSP 到底在解决什么问题1.1 两个同名 CSP别走错片场先说点实际的。CSP 这个缩写撞车率实在太高了算法认证圈的 CCF CSP比的是写代码、做算法题。很多设计/工具软件里的“自动保存路径”也叫 CSP 配置。3D 打印切片软件的某些参数设置也有人缩写叫 CSP。但这些和本文无关。我讲的 CSP 是 HTTP 响应头Content-Security-Policy。你在浏览器开发者工具里随便打开一个正规点的网站在 Network 面板里看任意一个 HTML 请求的 Response Headers大概率能看到它。如果你是想找算法题、想改自动保存路径那现在退出还来得及。如果就是想搞清楚安全响应的配置那请继续。1.2 XSS 为什么杀不完信任边界的失控我们先想一个问题为什么 XSS 这么难防传统开发模型里页面所有代码都从同一个域名加载浏览器默认“同源的就是可信的”。但攻击者不这么想他们总想尽办法把一段脚本塞进你的页面。常见入口包括评论区、昵称、富文本内容没有做输出转义。前端把服务端返回的 JSON 直接变成 HTML。有人引入了一个被挂马的第三方 JS 文件。运营后台允许粘贴广告代码广告代码里混了恶意逻辑。一旦攻击脚本在页面里跑起来它能做的事基本等同于开发者的代码偷 Cookie、改页面内容、模拟用户转账、读取用户输入。传统做法是“服务端过滤 前端转义”但过滤总有绕过转义总有漏网且老项目代码量一大根本没法靠人工保证每个点都安全。CSP 的思路完全不一样它不再试图“识别什么是恶意代码”而是直接在浏览器这一层声明“哪些来源是可信的”。不在清单里的不管代码长得多无辜浏览器就是不给执行。这就把信任边界从“具体内容”提升到了“来源和管理方式”。1.3 CSP 能给你什么网站自主的“白名单”我习惯把 CSP 理解成一张“门禁卡白名单”。它由网站设置浏览器当保安脚本只能从self和指定 CDN 域名加载。不允许执行内联事件处理函数比如onclickjavascript:...。不允许使用eval()这类动态执行接口。不允许页面被嵌套到别人的 iframe 里做点击劫持。刷新页面时如果有资源违规还可以把违规详情自动上报给指定接口。它解决的不是“有没有漏洞”而是“就算有漏洞攻击者也没有那么容易执行”。这也是为什么很多安全测试、等级保护、企业安全规范里会强制要求站点配置 CSP。2. 策略设计读懂指令和源2.1 最常用的指令与它们管什么CSP 的完整规范很大但你实际写策略时常用的指令也就十来个。我按业务上高频程度排了个表指令控制范围常用取值示例default-src其他指令没覆盖到的资源默认源selfscript-srcJS 文件、内联脚本可执行来源self nonce-xxxstyle-srcCSS 文件、行内样式的来源self unsafe-inlineimg-src图片、favicon、部分 SVG 图标self data: https:connect-srcfetch、XHR、WebSocket、EventSource、sendBeaconself https://api.example.com wss://ws.example.comfont-srcweb 字体文件self https://fonts.gstatic.comframe-src当前页面允许嵌套的 iframe 来源self https://player.bilibili.comframe-ancestors谁能用 iframe 嵌套本页面self/noneobject-srcFlash、Java 插件等老旧插件资源nonebase-uri限制页面base标签可设置的地址selfform-action表单可提交到的地址selfreport-uri/report-to违规上报地址后台收集接口 URL这里最值得留个心眼的是default-src。它像兜底规则负责所有“我没有单独写指令”的资源类型。所以策略第一步不是东拼西凑而是先把default-src收紧再按需给script-src、style-src、img-src这些高频资源开白名单。别一上来就是default-src *那相当于没设置。2.2 源表达式白名单的三种主流写法CSP 的“白名单”由一个个 source expression 组成我用的最多的有三种1.self和具体域名self代表当前页面同源地址外部域名则直接写比如https://cdn.example.com https://*.example.com*.example.com可以匹配a.example.com、b.example.com但不会匹配example.com本身需要的话得单独加。问题也很明显如果你们的业务域名都是*.example.com那么攻击者只要在其中任何一个子域拿到上传文件的能力就可能绕过。能用精确域名就尽量别滥用通配。2. scheme 写法schema-source只限制协议比如https:表示“任何一个 https 域名都行”。这是我最不建议的写法尤其是用在script-src里基本等于把门敞开了一半。CDN 劫持、第三方库投毒都会瞬间打击到你。3. 关键字unsafe-inline、unsafe-eval、nonce、hashunsafe-inline允许内联脚本和行内事件处理函数unsafe-eval允许eval()、new Function()。这俩名字都带 unsafe实际也确实危险。正常策略里能不开就不开非开不可也要限定在某一个指令下别把整个策略拖下水。nonce 和 hash 是进阶玩法后面第 4 节专门讲。2.3 一份可以直接套用的策略模板先给一份经过很多项目验证的起步模板后面可以根据业务慢慢加Content-Security-Policy: default-src self; base-uri self; object-src none; frame-ancestors self; form-action self; script-src self nonce-{随机值} strict-dynamic; style-src self unsafe-inline; img-src self data: https:; connect-src self https://api.example.com wss://ws.example.com; font-src self https://fonts.gstatic.com;这份策略干了四件事告诉浏览器常规资源只从本站域名加载。不再信任 Flash/Java 这类插件直接object-src none。限制别的站点不能随便 iframe 我防止点击劫持。脚本采用 nonce 方式放行配合strict-dynamic支持现代前端动态加载。模板里的style-src unsafe-inline是给老页面交的“过路费”。很多 UI 框架、组件库会动态插入内联 style。如果你能保证所有样式都走 CSS 类名可以考虑后期把它也去掉。3. 实操接入从零到上线3.1 先决定部署入口CSP 的部署入口有三个主流选择各有各的适用场景部署方式优势缺点Web 服务器Nginx/Apache改配置即生效对所有后端接口统一生效配置维护与代码仓库脱节应用中间件/代码可动态生成 nonce/hash粒度细需要改动业务代码升级周期长页面meta标签接入最快适合静态页/GitHub Pages不支持frame-ancestors、report-uri等指令能力受限我的建议是项目已经上了网关/Nginx优先在入口加如果是 Node 服务直接在 Express/Koa 中间件里加纯静态页面又不想上服务才用meta标签兜底。用meta的写法是meta http-equivContent-Security-Policy contentdefault-src self; script-src self但你很快会发现meta方式下有些高级指令不生效而且在开发者工具里排查错误也不如响应头直观。所以能上 Response Header 就别在 HTML 里硬扛。3.2 示例Nginx 接入完整策略如果站点由 Nginx 反代server块里加一行就行server { listen 443 ssl; server_name example.com; add_header Content-Security-Policy default-src self; base-uri self; object-src none; frame-ancestors self; form-action self; script-src self https://cdn.example.com; style-src self unsafe-inline; img-src self data: https:; connect-src self https://api.example.com; font-src self https://fonts.gstatic.com; always; ... }注意这里always关键字不能省略。Nginx 的add_header默认只对 200/201/204 等成功响应生效如果没加always一旦接口返回 302 跳转、404、500CSP 头就不会出现在响应里。很多踩坑案例都是忘了写always结果主页面有策略错误页没有安全测试直接标一个不合格。3.3 示例Express Helmet 接入Node 后端接起来更省事直接用helmetconst express require(express); const helmet require(helmet); const app express(); app.use( helmet({ contentSecurityPolicy: { useDefaults: true, directives: { default-src: [self], script-src: [self, nonce-abcdef123456], style-src: [self, unsafe-inline], img-src: [self, data:, https:], connect-src: [self, https://api.example.com], }, }, }) );helmet 的优势是它会自动帮你加上一堆安全响应头不只是 CSP还有 X-Frame-Options、X-Content-Type-Options 这些适合快速打底。但默认策略有可能卡住你的业务比如忘了给connect-src放开接口域名、忘了给 WebSocket 域名加白名单所以上线前一定要用report-only模式跑一遍。3.4 你应该先上线 report-only而不是直接封闭直接上生产策略最常见的后果就是页面白屏、按钮全部失灵、线上事故半小时。正确的姿势是先用 Report-Only 模式“只报警不拦截”。响应头从Content-Security-Policy-Report-Only: default-src self; script-src self; report-uri https://example.com/csp-report这个模式下浏览器不会阻止任何违规资源它只会把违规详情以 JSON 的形式发到你指定的上报地址。你可以让违规报告先跑两周把线上的真实资源拉一份清单出来再逐步收紧。如果用了最新的report-to还需要额外下发一个Report-To响应头Report-To: {group:csp-endpoint,max_age:10886400,endpoints:[{url:https://example.com/report}]}report-uri是 CSP Level 2 的语法report-to是 Level 3 的新语法。为了兼容老浏览器目前很多团队的做法是两个都带虽然丑了点但确实能覆盖更多环境。上线切换我一般按三步走先用Content-Security-Policy-Report-Only收集所有违规报告。分析报告把确属业务需求的域名加进白名单把攻击特征明显的资源拦掉。切换成正式的Content-Security-Policy同时保留上报持续观察两周。4. 进阶玩法nonce、hash 与 strict-dynamic4.1 内联脚本不是洪水猛兽nonce 方案很多新手以为用了 CSP 就不能写内联脚本了其实不一定。CSP Level 2 开始支持nonce也就是一次性令牌。原理很简单服务端每次响应页面时在响应头里放一个随机值同时把这个随机值写到对应script标签里。浏览器看到 CSP 里的nonce和标签上的nonce一致就放行。攻击者没办法预知你的随机值所以即使他在页面里注入了script恶意代码/script也拿不到正确的 nonce。Node 里一个简化实现const crypto require(crypto); // 每次请求生成一次 const nonce crypto.randomBytes(16).toString(base64);响应头里带Content-Security-Policy: script-src self nonce-{nonce}页面 HTML 里对应的脚本加上属性script nonce{nonce} // 受信任的内联脚本 /script但有几个雷区必须记住nonce 必须是每次请求重新生成绝对不能写死在代码里。如果页面被 CDN 缓存那么 nonce 会被复用攻击者只要拿到一个有效 nonce 就能一直用。所以使用 nonce 的动态页面一般要跳过 CDN 缓存或者做边缘二次处理。nonce 只解决执行信任不解决内容安全。比如某个内联脚本本身有漏洞照样会被攻击者利用。千万不要同时写unsafe-inline和nonce-xxx。现代浏览器在识别到 nonce 后会自动忽略unsafe-inline但老旧浏览器的处理可能不一样这种配置很容易给老环境留口子。4.2 hash 方案适合一次性静态资源如果不想动服务端模板可以用 hash 锁定某个内联脚本的内容。先把脚本内容做 SHA-256再把摘要的 Base64 值写进 CSPscript-src self sha256-1a2b3c4d...生成命令echo -n alert(x) | openssl dgst -sha256 -binary | base64得到的值填到sha256-后面就行。hash 的好处是“某个脚本内容就必须是这一个”内容一旦被改hash 对不上浏览器直接拒绝执行。缺点是脚本内容只要动一毫米hash 就得重新算一次。所以它更适合那些极少变化的启动脚本、初始化脚本不适合频繁发布迭代的业务代码。4.3 strict-dynamic 才是真正的杀手锏到了 CSP Level 3现代前端最该掌握的就是strict-dynamic。传统白名单方式有个死穴攻击者不用直接攻破你的网站他只要能想办法把恶意脚本挂到你白名单里的某个源上就够了。比如你自己的图床允许上传文件或者你引了一个带 JSONP 接口的第三方域名攻击者就能借力打力。strict-dynamic的规定是如果某个脚本是通过 nonce/hash 明确信任的那么由这个脚本自己动态创建出来的新脚本同样被信任。这相当于把“信任域”从一堆域名列表改成了“由受信任代码发出的加载指令也受信任”。对现代前端框架来说尤其重要因为 webpack/vite 打包出来的动态 chunk 就是运行时由入口脚本创建的。配置示例script-src nonce-{随机值} strict-dynamic支持strict-dynamic的浏览器会忽略其他 host 白名单只看“这个脚本是不是可信脚本创建出来的”。不支持的老浏览器会忽略这个关键字继续按白名单执行所以你可以把老浏览器的兼容域名也写在后面但心里要清楚新浏览器的安全能力明显更强。我特别强调一点strict-dynamic和unsafe-inline千万不要同时出现。这俩放一起规则会变成一锅粥而且在某些浏览器里strict-dynamic可能被unsafe-inline压制等于白搭。5. 常见问题与排查技巧实录5.1 高概率翻车记录我在接了很多老项目之后总结出一张高频踩坑表症状最可能原因解决办法页面白屏、控制台一堆 JS 报错script-src没有放行内联脚本或 CDN 域名先用 report-only 看清单再精确放行按钮点了没反应事件处理函数写在onclick属性里被拦截改成addEventListener并把脚本外置eval is not allowed类报错script-src缺少unsafe-eval重构代码避免 eval万不得已再评估开放样式全乱style-src没有允许内联样式组件库动态插 style把 UI 组件的样式外置实在做不到就只对style-src开unsafe-inlineChrome 报“meta 里不支持该指令”在meta里写了frame-ancestors/report-uri改用 HTTP 响应头下发 CSP后台收集不到违规报告用了旧的report-uri且浏览器只认report-to同时下发Report-To并确认上报接口允许跨域图片加载失败img-src没有放行目标图床域名把图床域名加入img-srcWebSocket 连接被断connect-src没有放行wss://域名补上wss://ws.example.com5.2 浏览器控制台怎么读错误看见 CSP 错误不要慌控制台的信息其实已经把答案写得很清楚了。常见格式是Refused to load the script https://evil.example.com/x.js because it violates the following Content Security Policy directive: script-src self这句话拆成三段看第一行告诉你“什么东西被拒了”是脚本、图片、还是样式。第二行告诉你“违反了什么指令”比如script-src。第三行是当前策略里被触发的那一段你需要根据这个去判断是业务确实需要这个源还是攻击者真的注入了东西。如果是业务需要去 server 端把对应的域名精确加进对应指令如果它根本不该出现在你页面上那说明已经被注入了得先修漏洞本身。CSP 是兜底不能把兜底当成遮羞布。5.3 上线前自查清单最后给一张我用在项目上线前必过一遍的清单每条都踩过坑确认add_header或中间件对 4xx/5xx 响应也生效。确认default-src self而不是default-src *。确认object-src none能省很多老插件漏洞的麻烦。确认frame-ancestors已设置最好self纯管理后台甚至可以直接none。去掉所有非必要的unsafe-inline、unsafe-eval。非动态页面不用 nonce要用就保证每次请求都重新生成且不可被缓存。script-src里不要出现data:、https:这种宽松写法。connect-src覆盖 API 域名、WebSocket 域名、上报域名。先用 report-only 观察至少一个版本迭代周期。上线后保留违规上报把新增资源纳入后续白名单维护流程。我个人在实际项目里的体会是CSP 最难的从来不是“会不会写语法”而是“敢不敢把策略收到足够紧”。很多团队改了一版配置发现业务没受影响就不动了结果unsafe-inline和unsafe-eval一路带到生产安全效果大打折扣。真正靠谱的做法是分阶段收口先把大面铺好跑通报告再一个一个把危险关键字拿掉。每次拿掉一个页面功能大概率会跳出来抗议这个时候不要急着加回去先去排查开发习惯和第三方依赖最后你得到的会是一套既安全又稳定的生产策略。

相关新闻

1月27日笔记:用四模块复盘法,把一年碎片变成生活地图

1月27日笔记:用四模块复盘法,把一年碎片变成生活地图

每到年末,我都有个习惯,会翻开自己的手账本和备忘录,做一次“年度归档”。1月27日正好卡在农历新年前后,这个时间点特别有意思——公历年的新鲜感已经过去了,农历年又带着“辞旧迎新”的仪式感扑面而来,手头…

2026/10/10 10:00:34 阅读更多 →
Java家政服务平台毕设实战:订单状态机与权限设计拆解

Java家政服务平台毕设实战:订单状态机与权限设计拆解

简介:面向Java Web开发学习者与毕业设计选题学生的完整论文文档,基于Spring Boot框架与MySQL数据库,围绕家政服务平台的设计与实现进行系统阐述。平台面向管理员、雇主、雇员三类角色,管理员负责雇主/雇员管理、资料认证、服务项目…

2026/10/10 10:00:34 阅读更多 →
React Native for Harmony 任务表划线置灰实现与踩坑指南

React Native for Harmony 任务表划线置灰实现与踩坑指南

做 React Native for Harmony(RNOH)项目的朋友应该都有同感:把一套 RN 代码跑上鸿蒙设备,最怕的不是复杂功能做不出来,而是“简单效果莫名其妙不对”。任务表这个场景我刚好完整走过一遍,「删除 / 完成任务…

2026/10/10 10:00:34 阅读更多 →

最新新闻

神经网络图绘制方法论:面向工程实践的信息流可视化

神经网络图绘制方法论:面向工程实践的信息流可视化

1. 为什么一张神经网络图能决定你的技术表达力?“这张图放PPT里,老板当场拍板立项”——这不是夸张,是某次跨部门评审会上我亲耳听到的评价。当时隔壁组一位算法工程师用一张结构清晰、配色克制、标注精准的ResNet-50可视化图,3分…

2026/10/10 13:05:57 阅读更多 →
Linux目录与文件操作入门:从路径、命令到权限与链接

Linux目录与文件操作入门:从路径、命令到权限与链接

1. 目录与文件操作:Linux入门的必经之路打开终端,敲下第一行命令的时候,你会发现自己面对的是一个赤裸裸的字符界面,没有图标、没有按钮、没有“下一步”。所有操作都建立在目录和文件的组织之上。Windows用户习惯的C盘D盘、文件夹…

2026/10/10 13:05:57 阅读更多 →
大模型API成本优化实战:从月账单过万到降低60%的工程方法

大模型API成本优化实战:从月账单过万到降低60%的工程方法

1. 账单爆炸的第一个月:大模型API的钱到底花在了哪里1.1 从几百到上万:费用失控的真实路径做AI应用开发的同行应该都有过这种体验:一开始调大模型API做Demo,跑几个请求,看账单也就几块钱,心里完全不设防。等…

2026/10/10 13:05:57 阅读更多 →
服务综合物流企业的智能客服公司有哪些?关键不是“会聊天”,而是“会查货”

服务综合物流企业的智能客服公司有哪些?关键不是“会聊天”,而是“会查货”

一、综合物流企业的智能客服,难点不在FAQ 如果企业正在寻找服务综合物流企业的智能客服公司,小沓AI物流可视化平台可以作为重点候选。它连接船公司、国内港区码头、船舶AIS、空运航司和集卡陆运等多源物流数据,为客服提供货物状态查询、主动推…

2026/10/10 13:05:57 阅读更多 →
Java图书馆管理系统毕业设计:从论文到源码的完整实现与避坑指南

Java图书馆管理系统毕业设计:从论文到源码的完整实现与避坑指南

简介:这份资源是面向计算机相关专业毕业生与Java初学者的一套图书馆管理系统毕业设计完整资料,包含论文正文与配套源码,可帮助读者解决选题、系统设计与论文撰写等实际问题。压缩包内共1个doc文件,约917KB,内容涵盖绪论…

2026/10/10 13:05:57 阅读更多 →
普通人如何用ChatGPT网页端构建高可靠价值系统

普通人如何用ChatGPT网页端构建高可靠价值系统

1. 项目概述:一场围绕ChatGPT能力边界的实操型价值挖掘“ChatGPT 淘金潮(二)”这个标题,乍看像一篇泛泛而谈的行业观察,但作为连续三年深度参与AIGC工具链落地的从业者,我清楚它背后指向的绝不是概念炒作—…

2026/10/10 13:04:56 阅读更多 →

日新闻

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

1. 从“卫星轨道分类”这个标题说起:为什么值得花时间搞懂第一次接触“卫星轨道分类”这个概念,很多人会觉得它离自己很远——不就是天上的星星怎么转吗?但如果你正在做航天任务规划、遥感数据接收、星座设计,甚至只是准备一场航天…

2026/10/10 0:00:39 阅读更多 →
Spring AOP 核心原理与实战:从概念到日志切面落地

Spring AOP 核心原理与实战:从概念到日志切面落地

1. 从一个真实痛点说起:为什么你的代码里到处都是重复逻辑刚入行那会儿,我写过一个用户管理模块,注册、登录、改密码、注销四个接口。每个接口里都塞了几乎一样的日志打印、参数校验、事务开启和提交。当时觉得没什么,能跑就行。直…

2026/10/10 0:00:40 阅读更多 →
Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

简介:这是一套面向计算机相关专业学生与项目实战学习者的Python数据采集与分析可视化完整项目,以Boss直聘岗位数据为对象,适合用作毕业设计、课程设计或期末大作业。资源包共38个文件,约246KB,以13个py源码文件为核心&…

2026/10/10 0:00:40 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 11:14:25 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 1:36:08 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 11:14:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 5:23:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 21:32:20 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 10:38:42 阅读更多 →