Configuration of Cloudflare
Hanging Aivory behind Cloudflare can get three things at a time: ** Hidden source IP ** (Visitors can only see the addresses of the Cloudflare edge nodes) ** Free and automatic renewal of edge certificates ** (Browser to Cloudflare this section of HTTPS does not need to worry you), and Cloudflare ** DDoS mitigation and basic WAF ** Additional advantages include global Anycast near-access and static resource edge caching.
This page offers two complete routes:
- ** Solution A: orange-cloud proxy ** Source stations have a public IP, DNS records open the Cloudflare proxy (orange cloud), and traffic path is “Visitors → Cloudflare → Source station Nginx → app”.
- ** Project B: Cloudflare Tunnel ** The source station does not have a public network IP or does not want to open any input port.
cloudflaredThe container actively builds outbound tunnels for Cloudflare.
Both routes are based on the same fact: Aivory’s app Single container single source serving SPA and /api Which domain name can be used. ** not need ** Changes to Cloudflare ALLOWED_ORIGINS Or any domain name configuration (this variable only makes sense when it is deployed separately from the front and back end).
Prerequisite: Domain Name Access to Cloudflare
- Register a Cloudflare account, click on the panel Add a domain Enter your domain name (such as
example.comChoose a free package. - Cloudflare scans existing DNS records and gives two NS server addresses to your domain name registrar to change Nameserver to both.
- Waiting to change. Active (usually minutes to hours), then the domain name’s DNS and agents are managed in the Cloudflare panel.
At the same time, confirm the basic deployment on the Aivory side has been completed: Deployment of Docker Compose It has been completed, and Plan A has been completed. Reverse Proxy and HTTPS Install Nginx at the source station.
Solution A: orange-cloud proxy
Step 1: Open the DNS record for the orange-cloud proxy
In the Cloudflare panel. DNS → Records Add (or modify) the record pointing to the source station and Proxy status Established as Proxied The Orange Cloud:
| Type | Name | Content | Proxy status |
|---|---|---|---|
| A | chat | The source IPv4 address | Proxied (orange cloud proxy) |
| AAAA | chat | Source station IPv6 address (if available) | Proxied (orange cloud proxy) |
Open the Orange-Cloud Proxy. dig chat.example.com Cloudflare’s edge IP is resolved, and the real address of the source station is no longer exposed.
The orange-cloud proxy just keeps DNS from leaking the source station’s IP. If you want the source station firewall level to accept only back source traffic from Cloudflare, you can only allow access to the 443 port in the firewall (ufw/cloud security group) that is officially announced by Cloudflare (https://www.cloudflare.com/ips/). This step is optionally enhanced and does not affect functionality.
Step 2: Select the SSL/TLS encryption mode
在 SSL/TLS → Overview Choose the encryption mode.The three modes differ in how the “Cloudflare to source station” section goes:
| The model | Installed by Cloudflare | Cloudflare → Source Station | Source Requirements | is recommended |
|---|---|---|---|---|
| Flexible | HTTPS | ** Clearly HTTP ** | The source station only needs to listen to 80, no certificate is required | Only internal network/trusted links |
| Full | HTTPS | HTTPS is ** No school certificate. ** | Optional certificate (including self-signature) | transitional use |
| Full (strict) | HTTPS | HTTPS, ** School certificate validity ** | True and trusted certificate. | ** Recommended ** |
** Recommended full (strict) ** The source station Reverse Proxy and HTTPS With Let's Encrypt certificate, you can meet the requirements. If you don't want to run a certbot, you can also SSL/TLS → Origin Server Issuing a Cloudflare Origin CA certificate (valid for up to 15 years, trusted only by Cloudflare back source) on the source station Nginx is also compliant with Full (strict).
** The advantages and disadvantages of flexible when using HTTP only **:
- Advantages: Aivory supports HTTP explicit deployment in the production environment (the application's request signature algorithm has a pure JS return, and does not rely on the encrypted API for the browser's security context).
https://The address, the page is secure, instead. ** Avoid various browser restrictions under pure text HTTP deployments ** The source station does not have a certificate configuration at all, and is the fastest path to zero certificate maintenance costs. - Disadvantages: Cloudflare to the source station this paragraph is clear, any intermediate network can see traffic content (including login credentials and conversation content). only when this link is reliable (such as in-room network, cloud provider private network) is recommended to use; go public network back source must be upgraded to Full (strict).
Reverse Proxy and HTTPS For example, the Nginx configuration translates requests from 80 ports to 301 to HTTPS. In Flexible mode, Cloudflare always uses the HTTP source and the source station always 301 times. https:// With Flexible, the source station 80 port must serve the business directly without HTTPS jumping.
Source stations Nginx in flexible mode (pure HTTP, still retaining SSE with real IP processing):
server {
listen 80;
listen [::]:80;
server_name chat.example.com;
# 普通上传默认上限 50MB(MAX_UPLOAD_BYTES),留出余量
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(配合下一步的 real_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;
}
}
The source station link in the Flexible mode is HTTP, if you have configured OAuth logins, select OAUTH_CALLBACK_BASE_URL Obviously external. https://chat.example.com Avoid returning addresses. http:// and synchronize the https referral address on the OAuth provider side. FAQ。
Step 3: Source Station Nginx Restores Real Visitor IP
This step ** Not to omit. ** Aivory counts the stream limit according to the client IP maintenance, judging the rule is: only trusted when the direct link is an internal network or back-end address. X-Forwarded-For / X-Real-IP (Take the non-internal entry on the right side of it), and ignore these heads at the same time as the public network.
When the orange-cloud proxy is turned on, the TCP peer that the source station Nginx sees is the Cloudflare edge node. If it is not restored, the “real IP” that Nginx sends to the app is the Cloudflare node IP: Visitors worldwide are folded into a few Cloudflare node addresses, shared traffic limit count, and a single trigger limit will connect a large number of users.
Cloudflare will place real IPs of visitors CF-Connecting-IP Restore the real_ip module with Nginx: New /etc/nginx/conf.d/cloudflare-realip.conf The content is as follows:
# /etc/nginx/conf.d/cloudflare-realip.conf
# 信任的 Cloudflare 回源网段,官方列表:https://www.cloudflare.com/ips/
# IPv4
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 104.16.0.0/13;
set_real_ip_from 104.24.0.0/14;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 131.0.72.0/22;
# IPv6
set_real_ip_from 2400:cb00::/32;
set_real_ip_from 2606:4700::/32;
set_real_ip_from 2803:f800::/32;
set_real_ip_from 2405:b500::/32;
set_real_ip_from 2405:8100::/32;
set_real_ip_from 2a06:98c0::/29;
set_real_ip_from 2c0f:f248::/32;
# 从 Cloudflare 注入的头里取访客真实 IP
real_ip_header CF-Connecting-IP;
sudo nginx -t
sudo systemctl reload nginx
The mainstream distribution. nginx.conf Default includes include /etc/nginx/conf.d/*.conf; (within HTTP blocks), the above file will automatically take effect; if your distribution does not, put this content in any HTTP blocks level configuration.
The working principle is a complete chain of trust:
- The real_ip module can be seen at the opposite end.
set_real_ip_fromIn the white list.CF-Connecting-IPThe value * is written$remote_addr* at this time$remote_addrThe real IP of the visitor. - The original server.
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;No need to change, it adds the real IP after restoration.X-Forwarded-ForTransfer to App. - The app is directly linked to the end of the app.
127.0.0.1:8787), meeting the "internal / back-end" condition, so trust the head and take the most right-hand non-internal entry, which gets exactly the real IP of the visitor.
Verification: access to the site after re-load, tail -f /var/log/nginx/access.log The source IP should be your own public network IP, not the address of a Cloudflare web segment.
Cloudflare’s back source network segment has remained stable for many years, but it is still recommended to check the official pairs list from time to time.
{ curl -s https://www.cloudflare.com/ips-v4; echo; curl -s https://www.cloudflare.com/ips-v6; } \
| sed '/^$/d; s/^/set_real_ip_from /; s/$/;/'
SSE Long Connection: Open the box, but do not cache the API
Aivory’s AI response is streaming through SSE (Server-Sent Events) without WebSocket.Cloudflare’s proxy transmission to SSE is open-box for two reasons:
- ** Free overtime ** Cloudflare proxy connections have a free overtime of about 100 seconds, while Aivory server sends a ping heartbeat every 15 seconds, the connection will never be judged as empty, and long answers and Deep Research tasks will not be interrupted. ** No additional configuration needed. **。
- ** by BUSH ** Cloudflare does not do a total buffer on responses that do not enter the cache, so long as the API response is not cached, the token reaches the browser in real time.
Therefore, the only thing to guarantee on the Cloudflare side is:** /api/* Do not cache** (Cache Rule processing in the next section). proxy_buffering off With relaxed reading overtime must still be retained, see Reverse Proxy and HTTPS。
Cache Rules: API bypass, static resources safely cache
Cloudflare is no cache. text/html Dynamic responses such as those with APIs can also work without rules in theory, but explicit statements can prevent global rules such as "Cache Everything" from being mistakenly opened later. Caching → Cache Rules There are two rules:
| The Rules | matching conditions | Movement is | Explained |
|---|---|---|---|
| Fire is around. | URI Path starts with /api/ | Bypass cache | Ensure SSE is never cached with all interfaces |
| Static resources | URI Path starts with /assets/ | Eligible for cache, Edge TTL extension (for example 1 month) | Construction of product file name with the content of the fingerprint, the content of the file name change, long cache is absolutely safe. |
Rules Order: Around the API is placed in front (Cache Rules match in order).
Panel switches: what to turn off and what to open
| switched | Located | recommended | Reasons |
|---|---|---|---|
| Rocket Loader | Speed → Optimization | ** closed ** | It rearranges script loading order, making it easy for SPA to create white screen or functional abnormalities. |
| Always Use HTTPS | SSL/TLS → Edge Certificates | ** Opened ** | All of http:// Visit to Edge 301 https:// |
| Auto Minify | 无 | No need to treat. | This feature has been downloaded by Cloudflare, no need to find it. |
| Brotli Compression | Speed | Keep in mind (open) | Default is enabled. |
| HTTP/2 / HTTP/3 | Network | Keep in mind (open) | Default is enabled. |
| WebSockets | Network | not related | Aivory streaming goes SSE, not WebSocket, this switch does not affect the switch and shutdown. |
Bypass the request body upper limit and “backup import”
Cloudflare limits the size of individual requests by package:
| The package | Request a maximum limit |
|---|---|
| Free / Pro | 100MB |
| Business | 200MB |
| Enterprise | 500MB |
Two categories of large requests for Aivory:
| The Scene | The service limit. | orange-cloud proxy is affected |
|---|---|---|
General documents/documents uploaded (MAX_UPLOAD_BYTES) | 50MB | Not affected, free package can be covered |
Registration of administrators (MAX_BACKUP_BYTES) | 20GiB | ** Rejected by Cloudflare ** No package is enough. |
Executive Administrator Backup of imports When archiving up to 20GiB, you have to bypass the Cloudflare agent and select one:
- ** Direct connection to the Internet (recommended) ** Direct access to the server or internal network
http://127.0.0.1:8787The admin console executes imports without any volume restrictions for Cloudflare and source Nginx. - ** Gray-Cloud (DNS only) Subdomain ** Create a DNS record.
direct.chat.example.comProxy status is set. DNS only (grey cloud), direct source station imports. source station Nginx needs to prepare a server block for the domain name andclient_max_body_sizeMove to21gReference to Reverse Proxy and HTTPS The large request section).
DNS only records resolve directly to the real address of the source station, conflicting with the "hidden source station" target. It is recommended to delete the record after importation is completed, or use an internal connection from the start.
Project B: Cloudflare Tunnel
Tunnel from the source station. cloudflared The process ** Proactively out. ** Connect to the Cloudflare edge and get traffic back through the tunnel. Suitable for situations where there is no public network IP (home width NAT, internal server) or you don’t want to open any ports: a firewall can completely block the station, the source station IP is not exposed naturally, and no longer requires the source station Nginx and certificates.
Step 1: Create a Tunnel in the Panel
- Enter the Cloudflare panel. Zero Trust (First use requires initialization, free plan is available.)
- Networks → Tunnels → Create a tunnel Select the type of connector. Cloudflared with a name (such as
aivory)。 - After creation, the page will display the installation command, which contains a long string of tokens (
eyJh...Only copy the token itself, and the next step is to deliver it to the container as an environmental variable.
Step 2: Adding cloudflared services
在 deploy/docker-compose.prod.yml 的 services Additional (and app The same level):
cloudflared:
image: cloudflare/cloudflared:latest
restart: unless-stopped
command: tunnel run
environment:
TUNNEL_TOKEN: ${TUNNEL_TOKEN:?请在 .env 中设置 Cloudflare Tunnel token}
networks:
- internal
depends_on:
app:
condition: service_healthy
在 .env Joining in :
TUNNEL_TOKEN=eyJh...你的token...
The point:
cloudflaredJoin an existing private network.internaland withappThe container is directly interconnected, the return source does not leave the host.- Tunnel is a pure exit connection.
cloudflaredNo need of anything.portsThe Mapping. appof service.portscan be admitted."80:8787"Stretch to"127.0.0.1:8787:8787"All public network traffic goes through the tunnel, and the 8787 direct link is reserved for administrator backup imports (see below).
docker compose -f docker-compose.prod.yml up -d
docker compose -f docker-compose.prod.yml logs -f cloudflared # 看到 Registered tunnel connection 即连通
Step 3: Configuring a Public Hostname (ingress)
Return to the Tunnel. Public Hostname The label page, adding:
| The field | 值 |
|---|---|
| Subdomain | chat |
| Domain | example.com |
| Type | HTTP |
| URL | app:8787 |
The ingress direction. http://app:8787: cloudflared 与 app Together with internal Cloudflare automatically creates the corresponding orange-cloud proxy DNS record, which is accessed by the browser. https://chat.example.com Open the Aivory.
Real IPs and Stream Limits in Tunnel Mode
Tunnel mode does not require Nginx, real IP links are automatically established: Cloudflare edge writes the visitor IP X-Forwarded-For 与 CF-Connecting-IP, cloudflared transmitted to app; app The opposite end is cloudflared The private network address of the container, which meets the conditions of trust, so take X-Forwarded-For The most right-hand non-intranet entry, i.e. the real IP of the visitor, can be verified once by the end list after deployment is completed.
Backup imports in tunnel mode
Tunnel traffic also passes through the edge of Cloudflare. ** The request body ceiling is completely consistent with the orange-cloud proxy. ** (Free/Pro from 100MB).20GiB-level backup imports still need to be bypassed: with the previously reserved 127.0.0.1:8787 It is imported directly on the server native, which is why compose recommends keeping the port mapping.
Comparison of two programs
| Dimensions | Solution A: orange-cloud proxy | Project B: Cloudflare Tunnel |
|---|---|---|
| Public network IP / open port | Required at least 443 | No need for entry port. |
| Components of Source Station | Nginx + Certificate (Full strict) | Cloudflared containers |
| Certificate operations | The source station requires a true certificate (certbot or Origin CA) | No, edge certificates are managed by Cloudflare |
| Hidden source | orange-cloud proxy hides DNS, recommended with a firewall white list | Natural hiding, firewalls can be completely sealed into the station |
| Real IP is restored. | Requires the Nginx real_ip module | Automated, no configuration required |
| The SSE flow. | Normal (15s heartbeat overtime covering the edge) | Normal (with the left) |
| Request a maximum limit | By CF package (Free 100MB) | the same |
| Backup of imports | Direct Internet or grey-cloud (DNS only) subdomains | Directly connected 127.0.0.1:8787 |
| Faulty side | Nginx, Certificate Renewal, CF Three | Cloudflared Process and CF |
One suggestion: having a public network IP and already running Nginx, option A; new deployment, home width or not wanting to touch certificate, option B.
Finish checking the list.
Repeat after completion:
- ** The SSL/TLS model ** Solution A is Full (strict) (or explicitly accepts Flexible's acceptance and the backlink is reliable); Solution B does not need to be set.
- Always Use HTTPS Opened and visited.
http://chat.example.com301 to HTTPS. - Rocket Loader has been closed.
- Cache Rules:
/api/*The bypass cache rule exists and ranks first. - ** Reviews of OAuth ** (If OAuth login is enabled):
OAUTH_CALLBACK_BASE_URLSet for external.https://Domain names, OAuth provider-side referral addresses have been synchronized and updated. FAQ。 - ** SSE flow is normal. **: Send a message and reply should be written by word.If the card pops out after a long time, first check if the source station Nginx is missed.
proxy_buffering offCheck if there is a cache rule./api/*。 - ** The limitation is effective on the real IP. ** Solution A View Nginx access log, source should be the visitor’s public network IP instead of the Cloudflare segment; Solution B Access from two different networks (such as mobile traffic and home broadband) confirms that there is no influence on each other’s flow limit count. If all users find a shared stream limit, review the real_ip configuration or
X-Forwarded-Forand transmitted. - ** Introduction to the passage. **: Confirmed to reserve direct access to the Internet (
127.0.0.1:8787If you are familiar with the grey-cloud (DNS only) subdomain practice, do not find a 100MB ceiling when data needs to be recovered. - ** Health inspection ** (If external distribution is used): the root path
GET /Return 200 represents survival.
A common idea in case of disruption: first bypass the Cloudflare direct source station (native curl http://127.0.0.1:8787/ Confirm that the application itself is normal, then layer by layer examine the Nginx, Cloudflare panel configuration.