7. 常见问题

常见问题

最常见的上线阻断项:公开短链按应用实际收到的 Host + 短码 精确查找。DNS、证书、后台和 /health 正常并不能证明 Host 配置正确;反向代理必须传递与后台域名完全一致的 Host,不能只设置 X-Forwarded-Host

本页按部署、使用、API 和开发整理 v2.22.3 的常见问题。命令中的域名、账号和凭据均为示例。

部署与运维

端口被占用,如何改到 8081?

有效环境变量是 HTTP_ADDR

HTTP_ADDR=:8081 ./dwz-server start

或设置 YAML http.addr: ":8081"SERVER_ADDR 是旧文档遗留键,本版本不读取。

安装后又回到安装向导?

检查运行工作目录下是否同时存在:

config/config.yaml
config/install.lock

Docker 必须持久化 /app/config。不要通过手工新建空锁文件掩盖配置或数据库问题。

SQLite 数据在哪里?

DATABASE_FILEPATHdatabase.filepath 决定,安装向导常用 ./config/sqlite.db。相对路径以进程工作目录解析。备份前优先停止服务,避免遗漏 WAL 数据。

容器无法连接数据库或 Redis?

容器内 localhost 指容器本身。Compose 中应使用服务名,例如 mysqlpostgresredis。不要把数据库端口或 Redis 6379 暴露到公网来绕过网络配置。

/health/simple 正常,但业务接口失败?

/health/simple 只验证 HTTP 存活;使用 /health 检查数据库和 Redis。再查看启动日志中的迁移、数据库连接和发号器错误。

多实例应该如何配置?

所有实例共享 MySQL/PostgreSQL、Redis、相同的 JWT_SECRET,并设置:

CACHE_DRIVER=redis
ID_GENERATOR_DRIVER=redis

local 发号器只适合单进程。Redis 发号器不可用时服务会拒绝启动,而不会不安全地降级。

能在线把 SQLite 切换成 MySQL 吗?

没有自动迁移命令。应停机、导出转换数据、在目标库执行迁移、导入并核对,再切换配置。先在数据副本上演练;不要直接修改生产 goose_db_version

域名与短链

后台有短链,访问却是 404?

服务通过应用实际收到的 HTTP Host + 短码 精确查找。后台域名为 s.example.com 时,Host 也必须是 s.example.comwww.s.example.coms.example.com:443127.0.0.1:8080 和容器服务名都不会命中同一记录。

按顺序检查:

  1. 后台域名没有协议、路径、末尾斜杠或错误端口。
  2. 标准 80/443 反向代理使用 proxy_set_header Host $host;
  3. 只有公开 URL 确实带非标准端口时才使用 $http_host,并在后台填写相同端口。
  4. CDN 回源没有把 Host 改成统一的源站域名。
  5. 应用部署在域名根路径,短链本身存在、启用且未过期。

后台页面和 /health 正常并不能排除 Host 问题,因为它们不会用公开短域名查找短链。

如何确认是反向代理问题还是应用配置问题?

使用一条真实测试短链执行两个 GET 请求:

DWZ_SHORT_HOST=s.example.com
DWZ_SHORT_CODE=abc123

curl -sS -o /dev/null -D - \
  -H "Host: ${DWZ_SHORT_HOST}" \
  "http://127.0.0.1:8080/${DWZ_SHORT_CODE}"

curl -sS -o /dev/null -D - \
  "https://${DWZ_SHORT_HOST}/${DWZ_SHORT_CODE}"
  • 两个请求都是 301/302/307/308:链路正常。
  • 直连成功、公开入口失败:检查 DNS、证书、宝塔/Nginx/CDN 和 Host 传递。
  • 两个请求都是 404:检查后台域名和短码记录。
  • 502:代理无法连接应用端口。

不要使用 curl -I;当前短码入口处理的是 GET 请求。

设置 X-Forwarded-Host 可以代替 Host 吗?

不可以。v2.22.3 的短链查找读取真正的 HTTP Host,不读取 X-Forwarded-Host。必须通过反向代理的 Host 请求头传递公开短域名。

域名字段什么时候需要端口?

标准 HTTP/HTTPS 的 80/443 不填写端口。只有用户访问的公开 URL 本身带非标准端口时才填写,例如 s.example.com:8443,同时使用 $http_host 保留该端口。不要填写内部应用端口 8080

HTTPS 在哪里配置?

通常由 Nginx、Caddy、Traefik、CDN 或云负载均衡终止 TLS,应用继续监听内网 HTTP。在域名记录中选择 https,并确保外部证书、代理和 Host 与其一致。后台协议选项不会签发证书,也不会自动修改反向代理。

为什么修改域名或协议后,已有短链还是旧地址?

域名和协议会在创建短链时复制到短链记录。修改域名配置不会批量迁移已创建的链接;应先正确配置并测试域名入口,再创建正式短链。需要迁移时先评估旧链接兼容性,不要直接假定修改域名记录会同步更新所有历史链接。

自定义短码有什么限制?

公开跳转分发只匹配 [a-zA-Z0-9._-]+,数据库字段长度为 20;但当前创建 DTO 没有在写入前声明同等正则校验。集成方必须按该字符集限制输入,并避免保留路由名称。否则 API 可能写入无法通过公开路径访问的短码。重复的“域名 + 短码”会被拒绝。

相同目标 URL 为什么有时返回旧短链?

由域名 duplicate_policy 决定:deny 拒绝、allow 总是允许、by_request 再读取请求 find_if_exists。带 custom_code 的请求始终尝试创建专属短链,不复用既有记录。

为什么短链不能删除?

为避免误删正在使用的链接,必须先停用,再删除。域名同样需要先停用。批量删除会分别返回成功 ID 和失败原因。

查询参数会不会传到目标地址?

只有域名启用 pass_query_params 时才透传。不要在短链 Query 中放密码或 Token;它们可能出现在跳转目标、代理日志和点击记录中。

登录、工作区与 API

收到 401 和 403 分别检查什么?

  • 401:Bearer 前缀、JWT/API Token 是否过期或禁用、签名四个请求头和服务器时间。
  • 403:X-Workspace-Id 是否有效、用户是否是启用成员、当前角色能否写该资源。

不传 X-Workspace-Id 可以吗?

可以,服务会选当前用户第一个可用工作区,但集成程序不应依赖顺序。请显式传递工作区 ID,避免数据写入错误空间。

App Secret 或 Bearer Token 忘记保存怎么办?

创建响应只返回一次完整凭据,列表不会回显。删除旧 Token 并创建新 Token,然后在调用方安全轮换。

签名一直失败?

确认方法大写、path 不含域名和 Query、顶层参数按键排序并紧凑 JSON 编码、时间戳单位是秒、时钟误差不超过 300 秒。POST/PUT/PATCH 的 JSON Body 会与 Query 合并参与签名。

OIDC 独占模式会不会把管理员锁在外面?

保存 exclusive=true 时服务要求至少有一条对应提供商绑定,但仍应先用非管理员测试账号完成端到端登录,再开启独占模式。修改前保留数据库和配置备份。

修改 JWT_SECRET 会丢数据吗?

不会删除数据,但所有现有登录 JWT 会失效。多实例滚动更新时必须同步切换,否则用户请求会在实例间随机认证失败。

统计、A/B 与隐私

A/B 转化重复计数怎么办?

每个业务事件使用稳定且唯一的 event_id。相同反馈 Token 和事件 ID 重复提交会返回 duplicate=true。不要为重试生成新的事件 ID。

统计日期为什么少一天或多一天?

接口日期格式是 YYYY-MM-DD,结束日期按包含当天处理;部署时区会影响日界线。调用方、数据库和服务统一时区,并优先显式传 start_dateend_date

点击数据如何归档?

当前版本没有自动归档任务。点击明细包含 IP、User-Agent、Referer 和 Query,部署方应制定留存、脱敏、访问控制和数据库归档策略,并在删除前验证报表影响。

开发与构建

go build 提示找不到 ../../open/go-web

仓库 go.work 用于维护者联调本地 go-web。没有该相对目录时使用模块版本:

GOWORK=off go build ./...
GOWORK=off go test ./...

管理端构建后,后端仍显示旧页面?

前端 dist 必须复制到 static/admin/,然后重新构建 Go 二进制。go:embed 在编译时固定资源,运行中不会自动读取新的 dist。

从哪里提交问题?

提交问题前删除日志中的密码、Token、App Secret、Cookie、个人数据和真实业务 URL。开发细节见开发与架构