Reverse Proxy and HTTPS
by Aivory app The container is 8787 The front-end SPA serving at the port and /api The backend itself speaks only of HTTP. The production environment suggests placing a reverse proxy layer in front of it responsible for TLS termination. This page gives you the complete configuration of both Nginx and Caddy that can be directly paste, and explains why each is written.
The front end and the API are served by the same process, the same port, the two are always the same source from the browser’s perspective. PUBLIC_ORIGIN There is no need to configure the CORS white list by domain name. ALLOWED_ORIGINS Only when you break the front end with the API ** Different sources ** The deployment form only makes sense, single container deployment cannot be used.
Before starting: Adjusting port mapping
Rapid deployment The compose document is app Map to host 80 ports ("80:8787" When adding Nginx/Caddy on the same machine, 80/443 is to be assigned to the countergenerator, and the app Change to only listening:
# deploy/docker-compose.prod.yml 中 app 服务的 ports 段
ports:
- "127.0.0.1:8787:8787"
Modification of execution docker compose -f docker-compose.prod.yml up -d reconstructed app Containers. after reverse generation unification transferred to http://127.0.0.1:8787。
Route to Route. GET / Returning 200 represents survival and can be used directly as a health inspection detection pathway for countergeneration or cloud load balancing.
Key Prerequisite: Stream exit to SSE
Aivory’s AI response is streamed via SSE (Server-Sent Events). ** Without WebSocket ** So there is no need for anything. Upgrade Related configuration. The server sends a ping heartbeat every 15 seconds to prevent the intermediate agent from crushing the connection due to vacancy. The reverse side does two things:
- ** Closing the response buffer. ** If the buffer responds, the token will save into a large piece and then spit to the browser, the typewriter effect directly disappears, expressed as "cards for a long time and then the whole section pops out".
- ** Reduce reading time. ** A deep research or long tool chain response can take tens of minutes, giving enough time to read.
Both of the following configurations already include these processes.
Filed to:nginx
Complete configuration
Preserved for /etc/nginx/sites-available/aivory.conf and substitute. chat.example.com For your domain name:
# HTTP:仅用于 certbot 验证与跳转 HTTPS
server {
listen 80;
listen [::]:80;
server_name chat.example.com;
# certbot webroot 验证路径(用 --nginx 插件时可省略)
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
# HTTPS 主站
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on; # nginx < 1.25.1 请删除此行,改为在上面两行 listen 末尾追加 http2
server_name chat.example.com;
ssl_certificate /etc/letsencrypt/live/chat.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/chat.example.com/privkey.pem;
# 普通上传默认上限 50MB(MAX_UPLOAD_BYTES),此处留出余量。
# 管理员备份导入可达 20GiB,见下文「大请求体」一节。
client_max_body_size 100m;
location / {
proxy_pass http://127.0.0.1:8787;
# --- SSE 流式输出三件套 ---
proxy_buffering off;
proxy_read_timeout 3600s;
proxy_http_version 1.1;
proxy_set_header Connection "";
# --- 真实客户端 IP 与协议 ---
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Activate and recharge:
sudo ln -s /etc/nginx/sites-available/aivory.conf /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Explanation by item
| Instructions | Role of |
|---|---|
proxy_pass http://127.0.0.1:8787 | Transmitted to app Containers, Spa and /api Go the same upstream, no need to divide location |
proxy_buffering off | Turn off the response buffer, and each event of the SSE is immediately transmitted to the browser, otherwise the typewriter effect disappears. |
proxy_read_timeout 3600s | Read overtime relaxed to 1 hour. A 15 second heartbeat on the service side can cover most free scenes, here’s an extra bottom to avoid over-long answers being stabbed by agents. |
proxy_http_version 1.1 | Upstream uses HTTP/1.1, supports long connections and is a prerequisite for streaming forwarding |
proxy_set_header Connection "" | empty Connection Head, keep upstream keepalive and avoid being downgraded to short connections |
client_max_body_size 100m | The request body ceiling.Nginx is only 1MB by default, and non-conference results in upload files returning directly to 413 |
proxy_set_header Host $host | Transmit the original domain name. Any domain name can be used under a single-container co-source architecture as long as the Host is transmitted correctly (all countergenerations are default) |
X-Real-IP / X-Forwarded-For | Transmitting the real client IP, directly related to the limit flow, see the section below |
X-Forwarded-Proto $scheme | The back end is HTTPS. |
Apply for certificate with certbot
# Debian / Ubuntu
sudo apt install certbot python3-certbot-nginx
# 自动修改 Nginx 配置并签发证书
sudo certbot --nginx -d chat.example.com
# 验证自动续期
sudo certbot renew --dry-run
--nginx The plugin will automatically complete the domain name authentication and write it. ssl_certificate Path and set up renewable timing tasks. If you want complete manual control of your profiles, switch to the webroot mode:
sudo mkdir -p /var/www/certbot
sudo certbot certonly --webroot -w /var/www/certbot -d chat.example.com
Successfully issued certificate. /etc/letsencrypt/live/chat.example.com/ consistent with the route set out above.
Chapter 2: Caddy
Caddy automatically applies and renews the Let's Encrypt certificate, automatically HTTP jumping HTTPS, Caddyfile Just three lines:
chat.example.com {
reverse_proxy 127.0.0.1:8787
}
sudo systemctl reload caddy
Caddy V2 has been detected Content-Type: text/event-stream The response buffer is automatically disabled, so the above three rows are usually opened. If your version is older or overlapped with other middle layers that will introduce the buffer, you can explicitly shut down:
chat.example.com {
reverse_proxy 127.0.0.1:8787 {
flush_interval -1
}
}
flush_interval -1 This means that every byte received is printed to the client immediately, equivalent to the value of Nginx. proxy_buffering off。
Caddy does not limit the request size by default, but also transmits by default. X-Forwarded-For Therefore, file uploads, backup imports and real IPs do not require additional configuration.
Big Request: Upload and Backup Import
Aivory has two types of large request bodies, the anti-generative. client_max_body_size To be treated separately:
| The Scene | Upper limit of service | Response to environmental variables | recommended |
|---|---|---|---|
| Upload of ordinary documents/documents | Deposit of 50 MB | MAX_UPLOAD_BYTES | client_max_body_size 100m has covered |
| The administrator imports. | by default 20 Gb | MAX_BACKUP_BYTES | See the two methods below. |
Manager in the background. Backup of imports File storage is up to 20 gigabytes. 100m The two practices:
- ** Temporary restrictions. ** Give it
client_max_body_sizesubstitute to21g并nginx -s reloadReplacement after import is completed.The large document scenario suggests adding simultaneouslyproxy_request_buffering offLet the Nginx side-send redirect, avoiding buffering the entire archive to the local disk first. - ** Direct connection to the Internet (recommended) ** Direct access to the server or internal network
http://127.0.0.1:8787Execute imports, completely bypassing the volume and overtime limitations of anti-generation.
The service itself is limited by MAX_UPLOAD_BYTES / MAX_BACKUP_BYTES Control and detail. Advanced environmental variables。
Real IP and Limits
Aivory counts stream limits by client IP maintenance (the counter is stored in Redis).
- ** Only when the direct link is the internal network or back-end address. ** (That is, when the request actually comes from your deployment countergeneration) trust only
X-Forwarded-For/X-Real-IPand takeX-Forwarded-For中** Non-internal links on the right. ** As a real IP. - At the end of the address, these headlines. ** All ignored ** TCP-to-end addresses are directly used, so public network attackers are fake.
X-Forwarded-ForYou can’t pretend to be someone else’s IP.
The meaning of this rule for operation:
If the counterpart is not set. proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for All requests seen in the background come from the reverse generation of their own IP. All site users share the same stream limit count: one trigger the stream limit and everyone is limited together. The Nginx configuration above already contains the correct writing, and Caddy’s default behavior is correct.
** The Cloudflare Scene ** The flow path becomes 访客 -> Cloudflare -> 源站 Nginx -> app The opposite side that source station Nginx sees is the Cloudflare node. Requires a real_ip module with Nginx, based on Cloudflare. CF-Connecting-IP First restore the real visitor IP, then go to the referral link above:
# 在 http 或 server 块中声明信任的 Cloudflare 回源网段(节选,完整列表见 Cloudflare 官方发布)
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
# ...其余 Cloudflare 网段...
real_ip_header CF-Connecting-IP;
This is $remote_addr The real visitor IP. $proxy_add_x_forwarded_for Added is also the correct value. The complete section list isined and configured gradually. Cloudflare access。
Can I use HTTPS?
It is possible but not recommended.Aivory remains fully available in a non-secure (pure HTTP) context: the application’s request signature algorithm has a pure JS return implementation, and does not rely on the encrypted interface that the browser only opens under HTTPS.
HTTP means that all of the login credentials and conversation content runs naked in the chain, any intermediate node can be intercepted or manipulated. As long as there is a domain name, you can use any set of configurations for a few minutes to connect to HTTPS.
Common Questions Review
| phenomenon | Reasons | Treated |
|---|---|---|
| Answer no stream, after the whole section appears. | React to buffering. | Nginx confirmed proxy_buffering off Check if there are other buffer layers in the middle (such as certain CDNs). |
| Quit the answer in the middle. | Reading is too short. | confirmed proxy_read_timeout 3600s If you go through the CDN, synchronize checking the CDN's free overtime |
| List of documents 413 | The upper request limit is too small. | Increased client_max_body_size |
| Backup to failure. | Exceeds the upper limit. | Temporarily increased 21g Direct connection to port 8787. |
| All users are restricted simultaneously | X-Forwarded-For Incorrectly Added | Complementary as above. proxy_set_header Two lines |
| Using Cloudflare backstream for CF node IP measurement | The source station does not restore the real IP | Configuration of the real_ip module CF-Connecting-IP See also Cloudflare access |