域名、HTTPS 与反向代理
使用 compose 文件中的 Caddy 配置文件或您自己的反向代理,将 Antimatter 部署到带有 HTTPS 的域名上,并正确设置站点 URL 和客户端地址。
站点 URL
站点 URL(ServiceSettings.SiteURL)是用户访问 Antimatter 的地址,例如 https://chat.example.com。服务器会在电子邮件和通知中的链接、登录重定向(OAuth、OpenID Connect、SAML)、移动和桌面应用,以及验证请求来源时使用它。请将其设置为与浏览器地址栏中完全一致的地址:包括协议、域名,以及非默认端口(如有),末尾不要加斜杠。
- 使用站点的 compose 文件时:在
.env中设置SITE_URL。 - 其他位置:
AM_SERVICESETTINGS_SITEURL,或 系统控制台 › 环境 › 网页服务器 › 站点 URL。
如果站点 URL 设置错误,使用身份提供商登录将会失败,电子邮件中的链接会指向错误的位置,且应用可能无法连接。更改域名后,请在此处以及所有身份提供商的重定向 URL 中更新该设置。
使用内置的 Caddy 配置文件实现 HTTPS
站点的 compose.yaml 包含一个位于 https 配置文件中的 Caddy 服务。它将从 Let's Encrypt 获取证书并自动续期,将 HTTP 重定向到 HTTPS,并将所有请求(包括 WebSocket)转发到 Antimatter。
- 将您域名的 DNS 记录(A 记录,以及用于 IPv6 的 AAAA 记录)指向服务器。
- 在防火墙中开放 80/tcp、443/tcp 和 443/udp(HTTP/3)端口。Let's Encrypt 必须能从互联网访问端口 80 或 443。
- 将域名添加到
.env并设置站点 URL:echo "DOMAIN=chat.example.com" >> .env sed -i 's|^SITE_URL=.*|SITE_URL=https://chat.example.com|' .env - 使用配置文件启动:
docker compose --profile https up -d
在后续每次执行 docker compose up 时添加 --profile https,或者在 .env 中设置 COMPOSE_PROFILES=https,以便普通的 docker compose up -d 也能包含该配置文件。Caddy 将其证书保存在 https 卷中。
Antimatter 自身的端口仍在主机上发布(HTTP_PORT,默认为 8065)。当 Caddy 位于前端时,请仅保留该端口供服务器自身使用:
echo "HTTP_PORT=127.0.0.1:8065" >> .env
docker compose --profile https up -d
Caddy 通过内部 edge 网络访问 Antimatter,而不是通过该端口,因此这对您的用户没有任何影响。
使用您自己的反向代理(nginx)
如果服务器已经运行 nginx 或其他代理,请关闭 https 配置文件,并将代理指向 Antimatter 的 8065 端口。代理必须:
- 支持 WebSocket 升级(Web 应用和移动应用会在
/api/v4/websocket上保持 WebSocket 连接,某些应用还会建立自己的连接); - 接受至少与最大上传大小(最大文件大小,默认为 100 MB)一样大的请求体;
- 保持长连接打开(WebSocket、大文件上传和下载);
- 传递
Host请求头和客户端地址。
以下是从 antimatter-docker 生产模板改编的完整服务器配置块。请替换域名和证书路径:
upstream antimatter {
server 127.0.0.1:8065;
keepalive 64;
}
map $http_upgrade $connection_upgrade {
default upgrade;
'' '';
}
proxy_cache_path /var/cache/nginx/antimatter levels=1:2 keys_zone=antimatter_cache:10m
max_size=3g inactive=120m use_temp_path=off;
server {
listen 80;
listen [::]:80;
server_name chat.example.com;
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name chat.example.com;
ssl_certificate /etc/ssl/chat.example.com/fullchain.pem;
ssl_certificate_key /etc/ssl/chat.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_timeout 1d;
ssl_session_cache shared:AntimatterSSL:10m;
ssl_session_tickets off;
add_header Strict-Transport-Security "max-age=31536000" always;
# At least FileSettings.MaxFileSize (100 MB by default).
client_max_body_size 100M;
proxy_http_version 1.1;
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;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_buffers 256 16k;
proxy_buffer_size 16k;
location ~ ^/api/v[0-9]+/(users/)?websocket$ {
client_body_timeout 60s;
send_timeout 300s;
lingering_timeout 5s;
proxy_connect_timeout 90s;
proxy_send_timeout 300s;
proxy_read_timeout 90s;
proxy_pass http://antimatter;
}
location / {
proxy_read_timeout 600s;
proxy_cache antimatter_cache;
proxy_cache_revalidate on;
proxy_cache_min_uses 2;
proxy_cache_use_stale timeout;
proxy_cache_lock on;
proxy_pass http://antimatter;
}
}
使用 nginx -t 检查文件,并使用 systemctl reload nginx 应用配置。服务器本身会发送 X-Content-Type-Options、Referrer-Policy、Permissions-Policy 和 X-Frame-Options;只有 HSTS 由代理负责。待 HTTPS 正常工作后,请提高其 max-age 值,并且仅当所有子域名都使用 HTTPS 时才添加 includeSubDomains。
如果使用 Docker Compose 并在主机上运行 nginx,上述 upstream 指向已发布的端口 8065。请仅将其发布到 127.0.0.1(HTTP_PORT=127.0.0.1:8065),以防止任何人绕过代理。
antimatter-docker 仓库提供了一个完整的设置,包括容器内的 nginx 以及通过 certbot 使用 Let's Encrypt,位于 deploy/compose 中。
其他代理
任何满足上述四个要求的代理均可使用。语音和视频通话从不经过代理:它们使用自己的端口,请参阅通话和语音。
代理后的客户端地址
在代理后面,所有请求似乎都来自代理。为了记录真实的客户端地址、按客户端应用速率限制并使用 IP 过滤,请告诉服务器哪个请求头携带地址,以及它可以信任哪些代理。这两个设置都没有系统控制台字段:请在 config.json 中或通过变量进行设置。
| 键与变量 | 默认值 | 功能说明 |
|---|---|---|
ServiceSettings.TrustedProxyIPHeader, AM_SERVICESETTINGS_TRUSTEDPROXYIPHEADER | 空值:服务器使用连接的地址 | 携带客户端地址的请求头,例如 X-Forwarded-For。将使用请求中存在的第一个请求头。 |
ServiceSettings.TrustedProxyIPs, AM_SERVICESETTINGS_TRUSTEDPROXYIPS | 空 | 您的代理的地址或 CIDR 范围,例如 172.29.71.0/28。请求头仅对来自这些地址之一的请求有效。 |
在变量中,使用空格分隔多个值:AM_SERVICESETTINGS_TRUSTEDPROXYIPHEADER="X-Forwarded-For X-Real-IP"。如果 TrustedProxyIPs 中的条目既不是 IP 地址也不是 CIDR 范围,服务器将拒绝该配置。
服务器如何查找客户端地址
每个代理都会将其收到请求的来源地址添加到 X-Forwarded-For 的右侧。客户端在请求头中发送的任何内容都保留在左侧。因此,服务器从右侧读取请求头,跳过您信任的代理的地址,并将第一个其他地址视为客户端地址。客户端无法通过发送自己的请求头来选择其地址。
- 在单个代理后面,仅设置
TrustedProxyIPHeader即可获得正确的地址:服务器会采用请求头中最右侧的地址。在您同时设置TrustedProxyIPs之前,服务器会在启动时记录一条警告。如果不设置,任何能够直接访问端口 8065 的人都可以设置该请求头,因此请仅将127.0.0.1或私有网络上的该端口发布出去。 - 在多个代理后面(例如,在您自己的代理前面有一个 CDN 或负载均衡器),请在
TrustedProxyIPs中列出所有代理。否则,服务器会将最外层代理的地址误认为是客户端地址。 - 代理可以用其看到的地址替换请求头(如上例中的 nginx 配置
X-Forwarded-For $remote_addr),也可以追加到请求头中(nginx 的$proxy_add_x_forwarded_for)。两种方式均可。
应列出哪些地址
列出代理请求的来源地址,即 Antimatter 看到的地址。
- 使用站点的 compose 文件和 Caddy 时,这已经配置好了:Caddy 通过
edge网络访问 Antimatter,compose 文件将TrustedProxyIPHeader设置为X-Forwarded-For,并将TrustedProxyIPs设置为该网络的范围EDGE_SUBNET(除非您在.env中更改,否则默认为172.29.71.0/28)。如果EDGE_SUBNET与您的某个网络重叠,请更改它。 - antimatter-docker 中的生产环境配置对其 nginx 容器执行相同的操作,使用
FRONTEND_SUBNET。 - 如果 nginx 位于宿主机上,而 Antimatter 在 Docker 中,请求将通过发布的端口到达 Antimatter,通常来自 compose 项目网络的网关地址(
docker network inspect会显示该地址)。请在compose.override.yaml中将AM_SERVICESETTINGS_TRUSTEDPROXYIPS设置为该地址。 - 如果没有使用 Docker,且 nginx 位于同一主机上,则应为
127.0.0.1(如果 nginx 通过 IPv6 连接,则为::1)。
不使用代理的 TLS
服务器也可以自行提供 HTTPS 服务,在 系统控制台 › 环境 › 网页服务器 中:连接安全 设置为 TLS(ServiceSettings.ConnectionSecurity),并配置 TLS 证书文件 和 TLS 密钥文件(ServiceSettings.TLSCertFile、ServiceSettings.TLSKeyFile),或 使用 Let's Encrypt(ServiceSettings.UseLetsEncrypt)。此时服务器必须监听 443 端口(监听地址,ServiceSettings.ListenAddress,默认为 :8065),并且 ServiceSettings.Forward80To443 会将 80 端口重定向过去。反向代理更易于运行和续期,本指南也假设使用反向代理。
服务器超时和请求大小
服务器自身网页服务器的设置,位于 系统控制台 › 环境 › 网页服务器:
| 设置项 | 键 | 默认值 |
|---|---|---|
| 监听地址 | ServiceSettings.ListenAddress | :8065 |
| 读取超时 | ServiceSettings.ReadTimeout | 300 秒 |
| 写入超时 | ServiceSettings.WriteTimeout | 300 秒 |
| 空闲超时(系统控制台中无对应字段) | ServiceSettings.IdleTimeout | 60 秒 |
| 最大有效负载大小(字节) | ServiceSettings.MaximumPayloadSizeBytes | 参见 安全性 |
请确保代理自身的超时时间至少与这些值一样长,其请求体大小至少为 最大文件大小。