🚀 内网项目踩坑实录:从 HTTP 到 HTTPS 的血泪史

By LayFz on Jul 3, 2025

📋 TL;DR(Too Long; Didn’t Read)-概述

  • 问题: 微信公众号爬虫在内网部署时无法登录
  • 原因: HTTP 环境下无法设置 Secure Cookie + Deno KV 生产环境依赖
  • 解决: 部署自签名 HTTPS + 删除用户存储功能
  • 结果: 功能完美运行 ✨

🎯 项目背景

需求简述

开发一个微信公众号文章爬虫工具,供公司内部使用。由于数据安全要求,必须部署在内网环境。

技术栈

Framework: Nuxt.js 3
Runtime: Node.js
Proxy: Nginx
Panel: 宝塔面板
Environment: Ubuntu 22.04 + 内网 172.19.23.27

预期 vs 现实

环境预期现实
公网测试✅ 完美运行✅ 确实完美
内网部署🤔 应该没问题吧💥 完全不能用

🔥 问题一:Cookie 安全策略地狱

💀 症状描述

部署到内网后,用户扫码登录时:

  • 二维码可以显示 ✅
  • 扫码成功 ✅
  • 点击登录… 毫无反应

宝塔配置如下:

通过反向代理打到本地3000端口,实现内网访问

🔍 错误现场

打开 F12,没有任何报错,甚至无法定位问题,此时的我非常焦虑,无论是多级反向代理还是跨域配置我都尝试了,并且长达两个小时都无法解决,此时我在本机ubantu的firefox才看到具体的报错信息,如图所示:

这真的让我非常难顶!!!排查了这么久,firefox直接就帮我定位了问题。(:

🚫 Cookie "slave_user" has been rejected because a non-HTTPS cookie can't be set as "secure"
🚫 Cookie "master_sid" has been rejected because a non-HTTPS cookie can't be set as "secure"  
🚫 Cookie "bizuin" has been rejected because a non-HTTPS cookie can't be set as "secure"
🚫 Cookie "data_ticket" has been rejected because a non-HTTPS cookie can't be set as "secure"
... (重复 N)

💡 第一反应: 这什么鬼?为什么公网好好的,内网就不行?

🕵️ 深入调查

问题根源

微信公众平台返回的 Cookie 长这样:

Set-Cookie: slave_user=xxx; Path=/; Secure; SameSite=None; HttpOnly
Set-Cookie: master_sid=yyy; Path=/; Secure; SameSite=None; HttpOnly

关键在于 Secure 标志!

浏览器安全策略

if (cookie.hasSecureFlag && location.protocol !== 'https:') {
  console.error('🚫 Cookie rejected: non-HTTPS environment')
  return false
}

环境对比

环境协议Cookie 设置结果
公网https://✅ 允许 Secure Cookie🎉 登录成功
内网http://❌ 拒绝 Secure Cookie💥 登录失败

💡 解决思路

既然浏览器严格要求 HTTPS 才能设置 Secure Cookie,而微信登录又必须依赖这些 Cookie,那只有一个办法:

给内网环境配置 HTTPS!


🛠️ HTTPS 部署实战

Step 1: 生成自签名证书

# 一键生成支持多地址的证书
openssl req -x509 -newkey rsa:2048 \
    -keyout server.key -out server.pem \
    -days 365 -nodes \
    -config <(
    echo '[dn]'
    echo CN=172.19.23.27
    echo '[req]'
    echo distinguished_name = dn
    echo '[EXT]'
    echo subjectAltName=@alt_names
    echo '[alt_names]'
    echo DNS.1=localhost
    echo DNS.2=wechatccc.com
    echo IP.1=172.19.23.27
    ) -extensions EXT

Step 2: 宝塔面板配置

  1. 网站设置 → SSL 选项
  2. 选择”其他证书”
  3. server.pem 内容粘贴到”证书(PEM格式)”
  4. server.key 内容粘贴到”密钥(KEY)”
  5. 保存并启用 HTTPS

Step 3: Nginx 配置

宝塔会自动生成配置,核心部分:

server {
    listen 80;
    listen 443 ssl http2;
    server_name 172.19.23.27 wechatccc.com;

    # 🔒 SSL 证书配置
    ssl_certificate /www/server/panel/vhost/cert/domain/fullchain.pem;
    ssl_certificate_key /www/server/panel/vhost/cert/domain/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;

    # 🔄 反向代理到 Nuxt 应用
    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded-Proto $scheme;
        # ... 其他标准代理配置
    }
}

🎯 问题一解决

访问 https://172.19.23.27

  • ⚠️ 浏览器显示”不安全”警告(自签名证书)
  • ✅ 点击”高级” → “继续访问”
  • ✅ Cookie 设置成功,登录流程恢复正常

🔥 问题二:Deno KV 生产环境炸弹

💣 新的爆炸

刚解决 Cookie 问题,结果点击登录后又炸了:

{
  "statusCode": 500,
  "message": "Could not find a Deno KV for production, make sure to deploy on Deno Deploy."
}

🤔 心理活动: 什么?我明明用的是 Node.js,怎么扯到 Deno 了?

🔍 代码考古

发现用户存储模块有这样的逻辑:

export async function createUser(user: UserEntry): Promise<boolean> {
    if (process.dev) {  // 🟢 开发环境:直接跳过
        return Promise.resolve(true)
    }

    // 🔴 生产环境:尝试连接 Deno KV
    const kv = await useKv()  // 💥 爆炸点
    // ... 复杂的用户存储逻辑
}

📊 环境差异分析

环境类型NODE_ENVprocess.dev执行路径结果
开发环境developmenttrue跳过 KV✅ 正常
生产环境productionfalse连接 KV💥 报错

原来如此! 开发环境下代码会跳过用户存储,所以没问题。但生产环境会尝试连接 Deno KV 数据库,而我的部署环境是 Node.js,当然找不到!

🤔 功能必要性评估

仔细思考用户存储功能:

  • 作用: 用户信息持久化、多账号管理、登录状态保持
  • 对爬虫的必要性: 🤷‍♂️ 不是核心功能
  • 复杂度: 🔴 增加部署复杂性

决策: 直接删除用户存储功能,专注核心爬虫能力!

💡 简单粗暴的解决方案

直接让所有用户存储函数返回成功/空值:

export async function createUser(user: UserEntry): Promise<boolean> {
    // 🚀 直接返回成功,不存储任何用户信息
    console.log(`📝 跳过用户创建: ${user.nickname}`)
    return Promise.resolve(true)
}

export async function getUser(originalID: string): Promise<UserEntry | null> {
    // 🚀 直接返回 null,表示"新用户"
    return null
}

export async function getUserByUUID(uuid: string): Promise<UserEntry | null> {
    return null
}

export async function getUserByFakeID(fakeid: string): Promise<UserEntry | null> {
    return null
}

🎉 最终效果

✅ 功能验证

# 🔗 HTTPS 访问
curl -I https://172.19.23.27
# HTTP/2 200 ✅

# 🍪 Cookie 设置
# F12 → Application → Cookies → 看到完整的微信认证 Cookie ✅

# 🔐 登录流程
# 扫码 → 点击登录 → 跳转成功 ✅

# 🕷️ 爬虫功能
# 数据爬取完全正常 ✅

📈 性能表现

响应时间: 200-500ms
稳定性: 24h 无异常
资源占用: 
  CPU: < 5%
  Memory: < 200MB
用户体验: 🌟🌟🌟🌟🌟

🎓 经验总结

💎 核心洞察

1. 安全策略进化论

现代浏览器对 Cookie 安全要求越来越严格:

  • Secure 标志强制要求 HTTPS
  • SameSite=None 必须配合 HTTPS 使用
  • 即使内网环境也无法例外

2. 环境一致性的重要性

- 开发环境: HTTP + 跳过复杂逻辑
+ 生产环境: HTTPS + 完整功能实现
= 隐藏问题,部署时爆炸 💥

3. Keep It Simple, Stupid!

当遇到复杂的依赖问题时,问问自己:

  • 这个功能真的必要吗?
  • 能不能用更简单的方式解决?
  • 核心业务需求是什么?

🛡️ 避坑指南

  • ✅ 优先检查浏览器控制台 Cookie 错误
  • ✅ 确认协议匹配(HTTP vs HTTPS)
  • ✅ 理解 Secure/SameSite 属性含义

数据库依赖管理

  • ✅ 区分开发和生产环境的差异
  • ✅ 评估功能的实际必要性
  • ✅ 优先解决核心问题,再考虑完善功能

内网部署特殊性

  • ✅ 不要以为内网就可以降低安全标准
  • ✅ 自签名证书是内网 HTTPS 的好选择
  • ✅ 简化架构,减少外部依赖

🚀 总结

这次踩坑经历的核心收获:

🔧 技术层面

  1. HTTPS 不是可选项:即使内网环境,现代 Web 应用也需要 HTTPS
  2. 自签名证书很实用:快速、简单、满足功能需求
  3. 功能删减有时比优化更有效:不必要的复杂性就是技术债务

🎯 工程思维

  1. 问题导向:专注解决实际问题,而不是实现所有功能
  2. 渐进式开发:先让核心功能工作,再考虑完善
  3. 简化优于复杂:能删就删,能简化就简化

💡 部署经验

  1. 环境一致性很重要:开发和生产环境的差异会隐藏问题
  2. 错误信息是最好的老师:仔细阅读错误信息,往往直接指向解决方案
  3. 内网不等于简单:现代 Web 标准不会因为内网而降低要求

🎬 结语

从 HTTP 到 HTTPS,从复杂的用户管理到简化的核心功能,这次部署经历让我深刻理解了:

🌟 工程不是艺术,而是在约束条件下找到最优解的过程。

有时候,删除代码比写代码更有价值。有时候,简单粗暴的方案比精巧复杂的设计更实用

技术服务于业务,而不是相反。

评论

订阅我的博客

通过RSS订阅获取最新文章更新,不错过任何一篇技术分享

推荐使用 FeedlyInoreader 等RSS阅读器订阅