Skip to main content

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.

Why don’t you need to configure CORS?

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

Health inspection

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:

  1. ** 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".
  2. ** 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

InstructionsRole of
proxy_pass http://127.0.0.1:8787Transmitted to app Containers, Spa and /api Go the same upstream, no need to divide location
proxy_buffering offTurn off the response buffer, and each event of the SSE is immediately transmitted to the browser, otherwise the typewriter effect disappears.
proxy_read_timeout 3600sRead 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.1Upstream 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 100mThe request body ceiling.Nginx is only 1MB by default, and non-conference results in upload files returning directly to 413
proxy_set_header Host $hostTransmit 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-ForTransmitting the real client IP, directly related to the limit flow, see the section below
X-Forwarded-Proto $schemeThe 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
About the flush_interval

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 SceneUpper limit of serviceResponse to environmental variablesrecommended
Upload of ordinary documents/documentsDeposit of 50 MBMAX_UPLOAD_BYTESclient_max_body_size 100m has covered
The administrator imports.by default 20 GbMAX_BACKUP_BYTESSee the two methods below.

Manager in the background. Backup of imports File storage is up to 20 gigabytes. 100m The two practices:

  1. ** Temporary restrictions. ** Give it client_max_body_size substitute to 21gnginx -s reload Replacement after import is completed.The large document scenario suggests adding simultaneously proxy_request_buffering off Let the Nginx side-send redirect, avoiding buffering the entire archive to the local disk first.
  2. ** Direct connection to the Internet (recommended) ** Direct access to the server or internal network http://127.0.0.1:8787 Execute 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-IP and take X-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-For You can’t pretend to be someone else’s IP.

The meaning of this rule for operation:

X-Forwarded-For must be added correctly

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.

We strongly recommend using 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

phenomenonReasonsTreated
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 413The 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 simultaneouslyX-Forwarded-For Incorrectly AddedComplementary as above. proxy_set_header Two lines
Using Cloudflare backstream for CF node IP measurementThe source station does not restore the real IPConfiguration of the real_ip module CF-Connecting-IP See also Cloudflare access