# 小鱼网站制作 SOP

从本地开发到服务器部署 域名注册 ICP备案与上线维护

版本 1.1 · 更新日期 2026年10月14日 · 面向第一次独立建站的同学

## 先读这一页

照这份手册完成一个网站：在Windows上写页面，在Ubuntu服务器上部署，用自己的域名访问，再学会修改、备份和排查故障。

**小鱼的实际路线：本地开发 → 阿里云ECS → Nginx提供页面 → FastAPI处理业务 → SQLite保存数据。** 主站用React/Vite，课表、调酒、建站手册等子页面则可以用独立HTML。它们共享服务器和域名，各自保留功能目录。

附带 `starter/` 是一个可立即运行的练习：先做静态页面，再接一个后端接口。页面做出来后，才有必要扩展账号、记录和管理台。

### 先抓住五件事

1. **功能决定架构。** 页面展示和浏览器内文件转换可以只做前端；多人共享账号、积分和记录需要后端与数据库。
2. **资源决定能否备案。** 内地服务器、公网能力、购买周期和域名实名信息要在付款前核对。
3. **备案与开发并行。** 本地继续开发，首次ICP备案通过后再开放内地网站。参考 [备案期间访问说明](https://help.aliyun.com/zh/icp-filing/the-influence-of-the-record-during-the-site-visit)。
4. **上线只发布运行需要的文件。** Vite发布 `dist/`；数据库独立存放；管理权限在后端检查。
5. **每次更新留得住旧版本。** 代码回滚与数据库恢复分别处理。

### 完成路线

```text
确认网站用途与备案主体
        ↓
本地开发和验收 ←→ 购买域名及合格服务器 → 提交ICP备案
        ↓                                  ↓
准备发布包和服务器配置                 审核通过
        └──────────────────────┬───────────┘
                               ↓
                  DNS解析 → 正式部署 → HTTPS
                               ↓
                  开站验收 → 公安联网备案 → 持续维护
```

每个章节末尾给出验收结果，用它判断这一步是否完成。

## 01 先确定你究竟要做什么

### 1.1 写一张项目卡

先写清第一版的核心功能。例如，课表转换器处理一个文件；联赛助手保存四人的对局结果。再决定需要哪些页面、接口和数据。

| 项目 | 需要填的内容 |
| --- | --- |
| 网站名称 | 页面展示名称及拟申请的备案名称 |
| 使用者 | 面向哪些人 是否需要登录 |
| 核心功能 | 用户进入页面后完成的一件事 |
| 网站主办者 | 个人还是单位 谁持有云账号与域名 |
| 服务内容 | 是否有用户发布内容 注册 上传 投票 支付等 |
| 数据范围 | 收集什么 为什么收集 保存多久 如何删除 |
| 预算 | 首年总额 续费总额 备份及带宽费用 |
| 维护人 | 谁处理故障 续费 内容投诉和数据请求 |

### 1.2 备案手续分别做什么

| 手续 | 作用 | 何时办理 |
| --- | --- | --- |
| 域名注册及实名认证 | 注册地址并确认持有人 | 购买域名后 |
| ICP备案 | 非经营性互联网信息服务备案 | 内地网站开通前 |
| 公安联网备案 | 网站的公安联网备案 | 开通后按要求提交 |
| 经营性ICP许可等资质 | 相应经营性或特定业务的准入 | 业务涉及时先确认办理路径 |

普通非经营性站点走备案流程；收费信息服务或特定行业，应先确认相应许可及前置手续。[互联网信息服务管理办法](https://xzfg.moj.gov.cn/front/law/detail?LawID=1756&Query=%E4%BA%92%E8%81%94%E7%BD%91+1+2)

### 1.3 个人工具站和公开树洞的区别

**备案看实际服务内容，不只看网页名称。** 个人工具展示与面向公众的留言社区，在主体和内容要求上有区别。做树洞时，把留言、评论、上传等功能列清楚，向接入商确认该业务可采用的主体和备案路径；申请中的用途应与上线内容一致。[网站信息填写规范](https://help.aliyun.com/zh/icp-filing/basic-icp-service/user-guide/fill-in-website-information)

备案按主办者所在地省份规则办理；杭州只是小鱼服务器的机房地域。这两项填错，会影响材料准备和审核。

**本阶段验收：** 能用三句话说明网站用途、主办者和所需办理路径；确认拟上线的功能与该路径相符。

## 02 理解网站的基本结构

| 部件 | 通俗理解 | 小鱼式架构中的位置 |
| --- | --- | --- |
| 前端 | 用户看到和点击的页面 | HTML CSS JS 或 React构建后的文件 |
| 后端 | 检查身份 权限及业务规则的服务 | FastAPI接口 |
| 数据库 | 保存账号 记录和业务数据 | SQLite 起步或其他合适数据库 |
| 服务器 | 全天运行网站的远程电脑 | 云服务器 |
| 域名 | 用户容易记住的地址 | 例如 example.com |
| DNS | 告诉浏览器域名对应哪个服务器 | A AAAA CNAME等解析记录 |
| Nginx | 对外提供页面并转发接口请求 | 公网80和443入口 |
| HTTPS证书 | 加密连接并验证访问的域名 | TLS证书及续期机制 |

```text
浏览器 → DNS查找 → example.com上的Nginx
                       ├─ /           静态页面
                       ├─ /tools/     工具子页面
                       └─ /api/       仅转给本机后端 127.0.0.1:8000

管理台 admin.example.com → 后端身份及权限校验
数据库 /var/lib/fishsite/ → 与公开页面目录分开
```

小鱼的课表转换器可以在浏览器内处理文件；调酒记录、联赛积分则由后端存储。先按功能选择架构，静态站只部署前端即可。

### 本手册的两条练习路线

- **必做路线：** 附带的HTML CSS JS练习站。本地无需Node.js，发布时直接上传前端文件。
- **进阶路线：** 附带的FastAPI健康检查接口。再往后才是React/Vite、账号、数据库及管理台。

## 03 准备本地开发环境

### 3.1 安装哪些工具

| 工具 | 必要程度 | 官方入口 |
| --- | --- | --- |
| 代码编辑器 | 必需 可选自己熟悉的编辑器 | https://code.visualstudio.com/ |
| 浏览器及开发者工具 | 必需 | 使用现有Edge或Chrome即可 |
| Python | 本教程本地服务及进阶后端需要 | https://www.python.org/downloads/ |
| Git | 建议 用于源码版本管理 | https://git-scm.com/downloads/ |
| OpenSSH客户端 | 部署时需要 | Windows设置中的可选功能或系统现有工具 |
| Node.js及npm | 只有React/Vite等构建项目需要 | https://nodejs.org/en/download |

Windows可把软件和缓存放D盘、项目源码放独立项目目录。已有工具能用就直接复用。

在Windows PowerShell检查：

```powershell
python --version
git --version
Get-Command ssh, scp
# 只有选择React/Vite路线时才检查下面两项
node --version
npm --version
```

`python`找不到时，检查安装及应用执行别名，也可试 `py --version`。安装后重新打开终端。PowerShell拦截 `npm.ps1` 时，使用 `npm.cmd`运行相同命令即可。

### 3.2 建立清楚的项目目录

```text
fishsite/
├─ frontend/          页面源码或React项目
├─ backend/           后端源码 如有需要
├─ ops/               Nginx及服务配置模板
├─ docs/              规则 使用说明和维护说明
├─ backups/           本地备份 不提交公开仓库
├─ .gitignore
└─ README.md
```

将教学包中的 `starter/` 复制或重命名为 `D:\Projects\fishsite`。源码、部署配置、说明和备份按上面的目录存放。

### 3.3 设置版本管理边界

`.gitignore`负责排除环境文件、数据库、私钥、依赖和备份。提交前用 `git status`查看文件列表，再添加源码。

```powershell
Set-Location "D:\Projects\fishsite"
git init
git status --short
# 添加本次需要保存的源码
git add frontend backend ops README.md .gitignore
git diff --cached --stat
git commit -m "Create first website"
```

`commit`保存本地版本，`push`上传到远程仓库。私钥和生产配置放在项目之外，团队共享源码和示例配置。

**本阶段验收：** 能打开终端、运行工具检查；知道源码、依赖、构建文件和秘密分别在哪里。

## 04 在本地完成第一版网页

### 4.1 运行附带的静态练习站

在练习项目目录运行下列命令。`127.0.0.1`表示只在这台电脑上访问。

```powershell
Set-Location "D:\Projects\fishsite"
python -m http.server 8080 --bind 127.0.0.1 --directory frontend
```

浏览器打开 `http://127.0.0.1:8080`。仍使用教学包原始目录时，先进入 `starter`。按 `Ctrl+C`结束服务。

练习站有主题切换和一个后端检查按钮；仅启动静态服务时，后端检查提示未连接是正常的，不代表网页无法运行。

### 4.2 完成四个小练习

1. 在 `frontend/index.html` 修改标题、简介和三张卡片的文字。
2. 在 `frontend/styles.css` 修改一种主题颜色，观察手机宽度下的排列。
3. 在 `frontend/app.js` 找到主题按钮，理解点击后保存到本机浏览器的逻辑。
4. 使用浏览器开发者工具的Network和Console，确认没有缺失的CSS、JS和图片。

完成这四项后，你就掌握了页面内容、样式、交互和调试入口，再按项目需要增加功能。

### 4.3 借助AI时怎么下达任务

把目标、文件结构和验收方式一起交给AI，效果比“帮我做个网站”更稳定：

```text
请制作一个中文个人工具展示页。
第一版只包含标题 三张工具卡片 手机适配和返回首页入口。
使用HTML CSS JS 先给出文件结构 再实现。
给出本地启动命令。
检查320px和1440px宽度 确认按钮和链接可用。
现有功能保持不变 新增功能完成后说明修改的文件。
```

### 4.4 如果你选择React和Vite

小鱼树洞主站使用React/Vite。选择这条路线时，在新的 `fishsite-react` 目录创建项目：

```powershell
npm create vite@latest fishsite-react -- --template react
Set-Location fishsite-react
npm install
npm run dev -- --host 127.0.0.1
```

根据终端显示打开本地地址。准备发布时：

```powershell
# 已有package-lock.json的项目建议用npm ci复现依赖
npm ci
npm run build
npm run preview -- --host 127.0.0.1
```

**开发看源码，上线发 `dist/`。** `npm run build`生成发布文件；`npm run preview`用于本地检查。当前Vite指南的Node.js要求为20.19+或22.12+。[Vite官方指南](https://vite.dev/guide/)

### 4.5 本地验收清单

- [ ] 320、390、768、1440像素宽度下没有横向滚动，按钮能操作。
- [ ] 页面刷新、空数据、错误输入和文件处理失败有明确反馈。
- [ ] Network没有意外404，Console没有应用错误。
- [ ] 图片不会盖住文字，卡片宽度不会超过屏幕。
- [ ] 页面内容和链接已替换为本项目的实际内容。
- [ ] 保存了源码；React项目额外保存了锁文件并验证了构建产物。

## 05 选择并购买服务器

### 5.1 购买前先做备案可行性检查

按小鱼的路线选择阿里云中国内地ECS，备案也在阿里云办理；域名可以在其他符合备案要求的服务商注册。

付款前检查三项：**内地节点、包年包月、公网带宽。** 当前ECS备案条件还要求累计购买时长大于3个月，选择6个月或12个月比较直接。免费试用和按量实例应先在控制台核验或转换计费方式。[备案前准备](https://help.aliyun.com/zh/icp-filing/basic-icp-service/user-guide/overview)

### 5.2 教学起步配置

小鱼起步采用2核、2GiB内存、3Mbps带宽。这个配置适合轻量页面和小规模API；扩容看实际CPU、内存和网络占用。

| 项目 | 起步选择 | 判断方法 |
| --- | --- | --- |
| 产品 | 可备案的ECS或接入商明确支持的产品 | 付款前验证备案资格 |
| 地域 | 中国内地且靠近主要使用者 | 不等于备案主体所在省份 |
| CPU及内存 | 2核2GiB可作为轻量起点 | 数据库 写入 图片处理多时评估4GiB |
| 系统 | 云平台官方Ubuntu LTS镜像 | 小鱼实例使用Ubuntu22.04 |
| 磁盘 | 如40GiB起步 并计划备份空间 | 看图片 数据库和日志增长 |
| 网络 | 具备公网访问能力 | 核对带宽 流量计费和公网IP |
| 购买周期 | 比较6个月及12个月 | 同时记录首购与续费价格 |

**图多先优化传输。** 3Mbps约等于0.375MB/s理论总速率，所有访问者共享。小鱼调酒有63款配图，采用WebP和延迟加载；建站手册是纯静态页面，无需新增后端。优先压缩图片和减少首屏请求，再根据监控决定加带宽或内存。

### 5.3 填一张费用表

| 费用项 | 首年实付 | 后续年度续费 | 是否自动续费 |
| --- | --- | --- | --- |
| 云服务器及系统盘 | 待填 | 待填 | 待填 |
| 公网带宽或流量 | 待填 | 待填 | 待填 |
| 域名 | 待填 | 待填 | 待填 |
| 备份及快照 | 待填 | 待填 | 待填 |
| 可选付费证书或其他服务 | 待填 | 待填 | 待填 |

ICP备案行政办理本身不收费；接入商提供的付费代办属于可选服务。[通信管理局服务指南](https://hunca.miit.gov.cn/bsfw/bszn/art/2024/art_7ef0d8bd3b0d433ba4b9b277f883f74d.html)

### 5.4 购买后记录什么

1. 网站主办者持有云账号并设置多因素验证。
2. 确认地域、规格、磁盘、公网能力、周期和续费价格。
3. 实例取名，如 `fishsite-prod`，便于区分正式站与测试站。
4. 设置SSH密钥或独立的服务器登录口令。
5. 记录公网IP、实例ID、到期日、账单和维护人。

**本阶段验收：** 实例运行、公网IP已记录、备案资格可用，首年和续费成本已算清。

## 06 注册域名和准备实名资料

域名按年注册和续费。选择容易记和输入的名称，比较首年、续费及转入价格。

### 6.1 操作顺序

1. 在正规的域名注册服务商查询名称，确认可注册及后缀的备案适用性。
2. 创建域名持有人信息模板，按真实主办者完成实名认证。
3. 注册域名，确认域名状态正常、实名认证已通过。
4. 保存域名证书，核对持有人与备案主体的一致性要求。
5. 开启可控的续费提醒或自动续费，确认付款方式仍有效。

注册后查看实名认证状态，等待信息同步后提交备案。[域名注册快速入门](https://help.aliyun.com/zh/dws/getting-started)

### 6.2 准备材料

- 个人准备身份证件、手机号、邮箱和实际地址；单位准备证照及负责人材料。
- 域名实名持有人与备案主体按所在地要求保持一致。
- 写清网站名称、服务内容、域名和服务器信息。
- 在备案系统上传证件；源码包保存操作说明，证件单独存放。

**本阶段验收：** 域名实名通过，材料齐全，审核联系人能接电话和短信。

## 07 提交ICP备案并等待结果

### 7.1 应该在哪提交

域名注册商管域名，服务器接入商协助办理备案。采用阿里云ECS，就进入阿里云ICP备案系统；已有其他接入商备案的站点迁移到阿里云，需要办理相应接入手续。[备案流程](https://help.aliyun.com/zh/icp-filing/basic-icp-service/user-guide/icp-filing-application-overview)

### 7.2 办理步骤

1. 校验主体、域名及云产品资格。同账号按系统引导选择实例，跨账号按服务码流程办理。
2. 填主体信息、网站名称和实际用途。
3. 上传资料，完成真实性核验。
4. 接入商初审，按反馈补齐材料。
5. 工信部短信核验。
6. 管局审核通过，取得备案号。

**短信核验要在收到后24小时内完成。** 接入商的“订单已提交”通知与工信部核验短信是两条信息；以系统显示的核验人员名单逐一完成。[短信核验指引](https://help.aliyun.com/zh/icp-filing/basic-icp-service/user-guide/sms-check)

材料齐全的管局审核有20个工作日办理时限，项目计划还要加上初审、补正和节假日。[备案服务指南](https://hunca.miit.gov.cn/bsfw/bszn/art/2024/art_7ef0d8bd3b0d433ba4b9b277f883f74d.html)

### 7.3 等待期间可以做什么

继续本地开发，并通过SSH配置服务器、在回环地址测试服务。**首次备案等待期间，公网Web入口保持关闭；取得正式结果后开站。**

### 7.4 收到结果后

在 [工信部备案查询](https://beian.miit.gov.cn/) 核对结果，把取得的备案号放在首页底部并链接该入口。

**本阶段验收：** 正式结果已保存、可核对备案号、实际用途与申报一致；接入状态符合当前服务器要求。

## 08 初始化新服务器

新服务器先用云平台提供的初始账号登录；本例用 `root`完成初始化，随后用 `deploy`维护。已有服务器从检查目录和配置开始，跳过已完成的初始化步骤。

### 8.1 换成你自己的参数

| 示例值 | 实际应填什么 |
| --- | --- |
| example.com | 你的域名 |
| 203.0.113.10 | 你的服务器公网IPv4地址 |
| 198.51.100.20 | 你当前可信管理网络的公网IPv4地址 |
| fishsite | 你的独立项目名称 |
| v1 | 本次发布版本目录名 每次使用新名称 |

后续命令使用这张参数表。代码块上方的“Windows PowerShell”和“Ubuntu Bash”标明执行位置。

### 8.2 建立和验证SSH连接

Windows PowerShell：

```powershell
ssh-keygen -t rsa -b 4096 -f "$env:USERPROFILE\.ssh\fishsite_rsa" -C "fishsite-deploy"
# 为本机私钥设置口令
Get-Content "$env:USERPROFILE\.ssh\fishsite_rsa.pub"
```

在ECS控制台导入 `.pub` 公钥，选择同地域实例绑定，按提示处理重启。私钥留在本机。本例用RSA，适配阿里云ECS的公钥导入流程。[ECS登录凭据管理](https://help.aliyun.com/zh/ecs/user-guide/instance-logon-credential-management)

完成绑定后再从Windows连接：

```powershell
ssh -i "$env:USERPROFILE\.ssh\fishsite_rsa" root@203.0.113.10
```

首次连接时，将SSH提示的主机指纹与云控制台中下列命令的输出对照：

```bash
ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub
```

在Ubuntu上初始化：

```bash
sudo apt update
sudo apt upgrade
sudo apt install nginx python3-venv sqlite3 ufw
sudo adduser deploy
sudo usermod -aG sudo deploy
```

给新用户安装你自己的同一份公钥：

```bash
sudo install -d -m 0700 -o deploy -g deploy /home/deploy/.ssh
sudoedit /home/deploy/.ssh/authorized_keys
# 在编辑器里加入本机.pub显示的一整行公钥
sudo chown deploy:deploy /home/deploy/.ssh/authorized_keys
sudo chmod 0600 /home/deploy/.ssh/authorized_keys
```

在Windows第二个终端用同一私钥连接 `deploy@203.0.113.10`，执行 `sudo -v`。验证这条登录通路后，再调整SSH认证设置。

### 8.3 安全组与系统防火墙是两层

一次连接要同时通过云安全组和Ubuntu防火墙。按下面的端口表配置，80/443在正式开站时放行。

| 端口 | 用途 | 允许范围 |
| --- | --- | --- |
| 22 TCP | SSH管理 | 你的可信管理IP 或受控VPN入口 |
| 80 TCP | HTTP及常见证书校验 | 上线后按需对公众开放 |
| 443 TCP | HTTPS | 上线后按需对公众开放 |
| 8000 5173 8080 | 示例后端及开发服务 | 不开放公网 |
| 数据库及管理面板端口 | 内部服务 | 不默认开放公网 |

Ubuntu上先允许你的SSH来源，再启用防火墙；下列管理IP是示例值。动态公网IP变化时要先更新规则，并保留云控制台救援方式。

```bash
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow from 198.51.100.20 to any port 22 proto tcp
sudo ufw enable
sudo ufw status verbose
```

备案通过并准备开站时，再分别在安全组和Ubuntu中允许Web访问：

```bash
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
```

使用IPv6时，同步检查IPv6规则。[ECS安全组](https://help.aliyun.com/zh/ecs/user-guide/start-using-security-groups)、[Ubuntu防火墙](https://ubuntu.com/server/docs/how-to/security/firewalls/)

**本阶段验收：** 第二终端可登录；22限制管理来源，后端监听回环地址。

## 09 在备案通过后配置DNS

### 9.1 主域名和子域名怎么填

到域名的DNS管理控制台添加记录；DNS由NS指定的服务商管理，NS保持现有设置即可。

| 主机记录 | 类型 | 记录值 | 用途 |
| --- | --- | --- | --- |
| @ | A | 服务器公网IPv4 | example.com |
| www | A | 同一公网IPv4 | www.example.com |
| admin | A | 管理台所在公网IPv4 | 只有做独立管理台时才添加 |

**普通工具用子页面，独立管理台用子域名。** 小鱼的课表工具、调酒和本手册使用 `/tools/`、`/cocktails/`、`/sop/`，沿用主站DNS和证书。`admin.example.com`则要单独添加解析、Nginx虚拟主机和证书，登录权限仍由后端校验。

A记录填公网IPv4；服务器具备可用公网IPv6时再添加AAAA。填错AAAA会造成部分设备能访问、部分设备一直超时。[域名指向网站](https://help.aliyun.com/zh/dws/getting-started/configure-domain-name-resolution)

### 9.2 从本机验证

```powershell
Resolve-DnsName example.com -Type A
Resolve-DnsName www.example.com -Type A
Test-NetConnection example.com -Port 80
Test-NetConnection example.com -Port 443
```

DNS返回正确IP后，端口仍不通，就查安全组、系统防火墙和监听服务。443在配置HTTPS后再验收；修改解析后要考虑TTL缓存。

**本阶段验收：** DNS返回正确公网地址，没有遗留不可用AAAA记录，管理台子域名只在需要时配置。

## 10 发布前端并配置Nginx

### 10.1 准备发布文件

静态站发布 `frontend/` 内容，React/Vite发布 `dist/`内容。Nginx的 `root`只指向这些发布文件。

小鱼按三处存放：源码在项目目录，公开页面在 `dist/`，生产数据在 `/var/lib/`。**修改一个子页面时，只同步它自己的目录。** 本手册上线只发布 `/sop/`，树洞首页和数据库保持原样。

新站可按下面的版本目录方式发布：每次建立新目录，用 `current`指向正在使用的版本，上一版本留作回滚。

Ubuntu上准备独立目录：

```bash
sudo install -d -m 0755 -o deploy -g deploy /var/www/fishsite
sudo install -d -m 0755 -o deploy -g deploy /var/www/fishsite/releases
```

Windows PowerShell上传；此时当前目录是练习项目根目录：

```powershell
scp -i "$env:USERPROFILE\.ssh\fishsite_rsa" -r .\frontend deploy@203.0.113.10:~/fishsite-release-v1
```

Ubuntu上检查上传内容，然后发布；`v1`必须是尚未使用的新版本目录：

```bash
( # 用子Shell运行 检查失败即停止 不退出原SSH会话
set -eu
ls -la ~/fishsite-release-v1
test ! -e /var/www/fishsite/releases/v1
mkdir /var/www/fishsite/releases/v1
cp -a ~/fishsite-release-v1/. /var/www/fishsite/releases/v1/
test -f /var/www/fishsite/releases/v1/index.html
find /var/www/fishsite/releases/v1 -type d -exec chmod 0755 {} +
find /var/www/fishsite/releases/v1 -type f -exec chmod 0644 {} +
# current用符号链接指向正在使用的版本
test ! -e /var/www/fishsite/current || test -L /var/www/fishsite/current
ln -sfn /var/www/fishsite/releases/v1 /var/www/fishsite/current.next
mv -Tf /var/www/fishsite/current.next /var/www/fishsite/current
)
```

命令块的 `set -e`使检查失败时停止；文件齐全后才切换 `current`。下一次改用 `v2`等新目录。数据库继续使用独立数据目录。

### 10.2 安装网站配置

将附带 `ops/nginx-static.conf.example` 的域名和路径替换后，用 `sudoedit`保存到 `/etc/nginx/sites-available/fishsite`。

核心配置示意如下，完整安全头配置见模板：

```nginx
server {
    listen 80;
    server_name example.com www.example.com;
    root /var/www/fishsite/current;
    index index.html;

    location /api/ { return 404; }
    location / { try_files $uri $uri/ =404; }
}
```

启用和验收：

```bash
sudo ln -s /etc/nginx/sites-available/fishsite /etc/nginx/sites-enabled/fishsite
sudo nginx -t
# 只有语法检查成功才执行下一行
sudo systemctl reload nginx
curl -I http://example.com/
```

配置已启用时，查看现有链接，修改配置后直接执行语法检查和reload。[Nginx入门指南](https://nginx.org/en/docs/beginners_guide.html)

React前端路由使用 `try_files $uri $uri/ /index.html;`回退到入口页，API与静态资源分别处理。部署到子目录时，构建 `base`应与访问路径一致，否则首页可能有内容、CSS和图片却404。

### 10.3 页面空白与CSP

小鱼管理台曾因CSP拦截内联脚本而空白，解决方式是将CSS和JS拆成独立文件。模板也是这一结构，配合 `script-src 'self'`和 `style-src 'self'`。遇到空白页，先看Console，再按被拦截的资源修复。

**本阶段验收：** 主域名及www展示新页面；不存在的文件返回404；秘密文件不在公开目录；旧版本仍保留。

## 11 配置HTTPS和证书续期

DNS和HTTP访问正常后，用Certbot为域名申请HTTPS证书。命令里的每个域名都要有正确解析；只使用主域名时，删去 `-d www.example.com`。

```bash
# 查看现有Certbot安装
command -v certbot
```

没有现成安装时，按官方当前安装方式使用Snap：

```bash
sudo apt install snapd
sudo snap install --classic certbot
sudo /snap/bin/certbot --nginx -d example.com -d www.example.com
```

填写联系邮箱，配置HTTP到HTTPS跳转，然后检查Nginx和证书。

```bash
sudo nginx -t
curl -I https://example.com/
sudo /snap/bin/certbot renew --dry-run
systemctl list-timers --all
```

**签发一次只是起点，续期演练成功才算完成。** `renew --dry-run`验证续期流程，定时任务负责自动续期；另设到期提醒。其他安装渠道使用对应Certbot路径。[Ubuntu证书教程](https://ubuntu.com/server/docs/how-to/security/obtain-tls-certificates/)、[Certbot安装指引](https://certbot.eff.org/instructions?os=snap&tab=standard&ws=nginx)

页面和接口优先使用 `/api/`等相对路径，随站点使用HTTPS，避免混合内容错误。

**本阶段验收：** HTTPS证书的域名正确、浏览器无证书警告、续期演练成功、有到期提醒。

## 12 可选进阶 增加后端与管理台

需要账号、保存记录或多人同步时，才接入后端。先用附带健康检查接口跑通“页面 → Nginx → FastAPI”，再增加业务表和权限。

### 12.1 本地运行附带FastAPI示例

Windows PowerShell，在练习项目根目录执行：

```powershell
python -m venv .venv
.\.venv\Scripts\python.exe -m pip install -r backend\requirements.txt
.\.venv\Scripts\python.exe -m uvicorn backend.main:app --host 127.0.0.1 --port 8000 --reload
```

访问 `http://127.0.0.1:8000`，点击后端检查；`/api/health`返回JSON。示例同时提供本地页面，页面与接口同源。开发用 `--reload`，上线由systemd维护进程。[FastAPI手动部署](https://fastapi.tiangolo.com/deployment/manually/)

验证后记录实际依赖版本，后续部署按相同版本复现。

### 12.2 服务器部署进阶示例

上传后端源码和依赖文件，服务器创建自己的虚拟环境，业务进程使用 `deploy`用户：

```bash
sudo install -d -m 0755 -o deploy -g deploy /var/www/fishsite/backend
# 将你自己的backend源码上传并检查后放入上述目录
python3 -m venv /var/www/fishsite/venv
/var/www/fishsite/venv/bin/python -m pip install -r /var/www/fishsite/backend/requirements.txt
```

第10章已将 `/var/www/fishsite`归属设为 `deploy`。创建环境遇到权限错误时，检查这个目录的所有者。

将 `ops/fishsite-api.service.example`安装到 `/etc/systemd/system/fishsite-api.service`。它在本机8000端口运行，退出后自动重启：

```bash
sudo systemctl daemon-reload
sudo systemctl enable --now fishsite-api
systemctl is-active fishsite-api
curl http://127.0.0.1:8000/api/health
sudo journalctl -u fishsite-api -n 30 --no-pager
```

将Nginx配置中的 `/api/`块替换为附带 `nginx-with-api.conf.example` 的代理配置；这里 `proxy_pass` 后没有附加斜杠，从而保留 `/api/health` 路径。先执行 `nginx -t`，通过后再reload。

```nginx
location /api/ {
    proxy_pass http://127.0.0.1:8000;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header X-Forwarded-Proto $scheme;
}
```

用 `https://example.com/api/health`验证整条链路：浏览器访问443，Nginx转发到本机8000，后端返回JSON。

### 12.3 小鱼管理台的关键设计

| 场景 | 实际做法 |
| --- | --- |
| 账号密码 | 保存带盐密码哈希 后台提供密码重置 |
| 操作权限 | 后端逐请求检查身份 写操作校验来源 |
| 项目隔离 | 全局管理员与赛事项目管理员分开 |
| 私密手记和照片 | 仅授权用户读取 分享链接可撤销 |
| 图片上传 | 限制大小 验证图像内容 再编码保存 |
| 香草园名单 | 停用和恢复保留历史票数与资料 |
| 管理操作 | 记录目标 时间和变更结果 |

会话Cookie使用Secure、HttpOnly和适合部署方式的SameSite设置。验收权限时直接请求接口：未登录应返回401，无相应权限应返回403。

**本阶段验收：** 关闭SSH窗口后API仍运行，域名接口正常，身份和权限测试通过。

## 13 开站验收和公安联网备案

### 13.1 上线前最后一道检查

- [ ] 备案及接入已通过，站点内容与申报一致。
- [ ] 首页底部显示自己的ICP备案号并链接工信部查询。
- [ ] HTTPS、页面、接口、手机端和不同网络都通过验证。
- [ ] 服务器密码与管理台密码不同；云账号有多因素验证。
- [ ] 后端、数据库和开发端口未对公网开放。
- [ ] 测试账号与演示数据已清理。
- [ ] 隐私说明、数据删除和投诉联系渠道与实际功能匹配。
- [ ] 源码与必要数据已有备份，并完成过一次恢复演练。
- [ ] 已设置服务器、域名、证书及备份失败提醒。

### 13.2 公安联网备案

在开站之日起30日内，通过 [全国互联网安全管理服务平台](https://beian.mps.gov.cn/) 提交公安联网备案：申请主体 → 新办网站 → 填写信息和上传资料 → 审核 → 悬挂编号、图标和查询链接。[办理指引](https://help.aliyun.com/zh/icp-filing/basic-icp-service/quick-start-for-public-security-network-filing-for-personal-websites)

### 13.3 有投稿功能时补齐什么

人物头像和小传等投稿增加审核、投诉与删除入口，素材保存来源和授权信息。AI生成素材按适用规则做声明和标识。[生成合成内容标识办法](https://www.cac.gov.cn/2025-03/14/c_1743654684782215.htm)

**本阶段验收：** 开站清单完成；公安备案已按要求申请，完成后悬挂正确编号；有人负责受理投诉和资料请求。

## 14 每次修改都按同一套流程

```text
确认范围 → 备份 → 本地修改 → 测试 → 发布新版本
                                     ↓
                     线上验收 → 成功留记录 或失败回滚
```

### 14.1 发布前备份什么

- 代码及正在使用的前端版本，Nginx及服务配置，依赖版本记录。
- 业务数据库、上传素材，以及恢复所需的秘密配置；秘密备份加密并严格控制访问。
- 域名及服务器到期信息、备案信息、负责人和恢复步骤。

备份放到网站目录之外，并保留异地副本，覆盖服务器磁盘损坏等情况。

### 14.2 用SQLite在线备份

小鱼多个业务使用SQLite。运行中的数据库采用在线备份，日期或时间戳区分不同副本：

```bash
sudo install -d -m 0700 /var/backups/fishsite
stamp=$(date +%Y%m%d-%H%M%S)
sudo sqlite3 -readonly /var/lib/fishsite/site.db ".backup '/var/backups/fishsite/site-$stamp.db'"
sudo chmod 0600 "/var/backups/fishsite/site-$stamp.db"
sudo sqlite3 "/var/backups/fishsite/site-$stamp.db" "PRAGMA integrity_check;"
```

换成实际数据库路径；`integrity_check`返回 `ok`表示完整性检查通过。WAL模式下只复制 `.db`可能漏掉最近写入，`.backup`会生成一致性快照。[SQLite在线备份](https://www.sqlite.org/backup.html)

### 14.3 出问题时怎样回滚

- 纯静态页面：确认上一版本目录完整，将 `current`重新切换到上一版本，再验证主域名。
- 后端代码：恢复兼容的旧代码及依赖，重启对应服务，并验证接口。
- 数据库结构改变：检查旧代码与新结构的兼容性，按对应迁移方案处理。
- 恢复数据库：暂停写入，保存当前副本，试恢复并核对记录，再切换生产数据。

**代码版本与数据版本分别管理。** 发布记录写清版本、备份位置、测试结果和回滚方案。

### 14.4 建议的维护频率

| 周期 | 要检查的事项 |
| --- | --- |
| 每日 | 可用性及备份任务失败告警 |
| 每周 | 内存 磁盘 带宽 错误日志 投诉与审核队列 |
| 每月 | 恢复演练 依赖更新评估 权限和秘密配置检查 |
| 到期前 | 域名 云服务器及证书提醒 建议提前30天核查 |
| 内容或主体变更时 | 核对备案变更 接入迁移及其他手续 |

## 15 常见问题按层排查

| 现象 | 判断位置 | 处理方法 |
| --- | --- | --- |
| 开全局代理后访问超时 | 客户端出口或分流 | 将域名设为直连 对照直连结果 SSH还要检查管理IP规则 |
| 域名和公网IP都超时 | 网络入口或服务监听 | 检查安全组 UFW和ss输出 |
| DNS返回旧IP | 解析位置及缓存 | 核对NS对应控制台 A记录和TTL |
| 首页正常 子页404 | 文件或路由 | 检查对应目录和构建base |
| 接口502 | 后端未就绪 | 查看systemctl和journalctl 验证本机health |
| 页面空白 Console报CSP | 资源被拦截 | 外置内联CSS和JS 核对允许的资源来源 |
| 证书申请失败 | 域名校验 | 检查每个域名的解析及80端口 |
| 手机卡片超过屏幕 | 布局固有宽度 | 检查viewport min-width与图片尺寸 |
| 页面正常 数据不见了 | 数据目录或环境变量 | 核对实际数据库路径 与备份比较记录 |
| 备案被退回 | 材料或用途 | 按退回理由修改对应字段并重提 |

Ubuntu常用只读检查：

```bash
systemctl status nginx --no-pager
sudo nginx -t
sudo ss -lntp
sudo ufw status verbose
free -h
df -h
# 若部署了本教程后端
sudo journalctl -u fishsite-api -n 50 --no-pager
```

反馈故障时附上现象、发生时间、上述检查结果和脱敏日志。

## 16 教学安排和最终交接

### 16.1 建议分成三次课

1. **第一课 本地做出可运行的页面。** 解释结构、修改示例、做手机适配、看控制台，验收第一版。购买资源前完成用途和主体讨论。
2. **第二课 准备上线所需资源。** 创建账号、比较费用、购买合格资源、注册实名域名、提交备案。等待审核期间继续完善页面。
3. **第三课 审核通过后正式上线。** 做DNS、Nginx、HTTPS及可选后端，完成开站清单、公安备案申请和恢复演练。

学生持有自己的域名和云账号，老师指导操作与验收，后续维护归属清楚。

### 16.2 给维护者的交接包

- 源码、锁文件或依赖版本、README及目录说明。
- 域名和服务器归属、续费安排、负责人及联系办法。
- 部署配置模板、发布记录、备份机制和经过测试的恢复步骤。
- 备案结果、证件及秘密配置的单独存储位置。
- 凭据交接及必要的轮换记录。
- 已知问题、待办功能及权限划分。

### 16.3 最终通过标准

学员能独立完成五件事：解释访问链路，修改页面，发布版本，定位一次故障，恢复一次备份。再核对资源归属与续费安排，完成交接。

## 官方资料入口

规则与操作入口于2026年10月14日核对，办理时查看所对应的最新页面。

- [工信部非经营性互联网信息服务备案管理办法](https://www.miit.gov.cn/zcfg/xxtxl/art/2024/art_7e48434c08c24131b4b7eecfca5b2b6c.html)
- [互联网信息服务管理办法](https://xzfg.moj.gov.cn/front/law/detail?LawID=1756&Query=%E4%BA%92%E8%81%94%E7%BD%91+1+2)
- [湖南省通信管理局备案服务指南](https://hunca.miit.gov.cn/bsfw/bszn/art/2024/art_7ef0d8bd3b0d433ba4b9b277f883f74d.html)
- [阿里云备案流程概述](https://help.aliyun.com/zh/icp-filing/basic-icp-service/user-guide/icp-filing-application-overview)
- [阿里云备案前准备](https://help.aliyun.com/zh/icp-filing/basic-icp-service/user-guide/overview)
- [网站信息填写规范](https://help.aliyun.com/zh/icp-filing/basic-icp-service/user-guide/fill-in-website-information)
- [备案期间网站访问说明](https://help.aliyun.com/zh/icp-filing/the-influence-of-the-record-during-the-site-visit)
- [工信部短信核验](https://help.aliyun.com/zh/icp-filing/basic-icp-service/user-guide/sms-check)
- [域名注册快速入门](https://help.aliyun.com/zh/dws/getting-started)
- [配置域名解析](https://help.aliyun.com/zh/dws/getting-started/configure-domain-name-resolution)
- [ECS安全组](https://help.aliyun.com/zh/ecs/user-guide/start-using-security-groups)
- [ECS登录凭据及密钥对管理](https://help.aliyun.com/zh/ecs/user-guide/instance-logon-credential-management)
- [全国互联网安全管理服务平台](https://beian.mps.gov.cn/)
- [公安联网备案快速入门](https://help.aliyun.com/zh/icp-filing/basic-icp-service/quick-start-for-public-security-network-filing-for-personal-websites)
- [Vite官方指南](https://vite.dev/guide/)
- [FastAPI部署](https://fastapi.tiangolo.com/deployment/manually/)
- [Nginx入门指南](https://nginx.org/en/docs/beginners_guide.html)
- [Ubuntu防火墙](https://ubuntu.com/server/docs/how-to/security/firewalls/)
- [Ubuntu TLS证书配置](https://ubuntu.com/server/docs/how-to/security/obtain-tls-certificates/)
- [Certbot安装指引](https://certbot.eff.org/instructions?os=snap&tab=standard&ws=nginx)
- [SQLite在线备份](https://www.sqlite.org/backup.html)
- [人工智能生成合成内容标识办法](https://www.cac.gov.cn/2025-03/14/c_1743654684782215.htm)
