GitHub Release / release (push) Canceled after 0s
Publish Docker image / build (linux/amd64, ubuntu-latest, amd64) (push) Canceled after 0s
Publish Docker image / build (linux/arm64, ubuntu-24.04-arm, arm64) (push) Canceled after 0s
Publish Docker image / merge (push) Canceled after 0s
ci / ci (22, ubuntu-latest) (push) Canceled after 0s
修复设置页误清空管理域、配置与生效 URL 混淆及第三 Host 绕过隔离;加固 Settings API 与中间件,并更新文档站。
237 lines
7.3 KiB
Markdown
237 lines
7.3 KiB
Markdown
# Cloudflare & preferred-edge deployment
|
||
|
||
In dual-domain mode, PicHost distinguishes **site** vs **image** traffic from the request **Host** (and `X-Forwarded-Host` from a trusted reverse proxy). Cloudflare orange cloud, preferred-edge IPs, Workers, and Pages are all fine — as long as the hostname identity reaching PicHost stays correct.
|
||
|
||
> **One-line rule:** The network path may change; the hostname identity must not — `admin → admin`, `img → img`, never `img → admin`.
|
||
|
||
See [Dual-domain separation](./domain-separation.md) for middleware behavior and [Reverse proxy](./reverse-proxy.md) for general proxy requirements.
|
||
|
||
---
|
||
|
||
## Quick checklist
|
||
|
||
| # | Check | Expected |
|
||
| - | ----- | -------- |
|
||
| 1 | Browser hostname | `admin.*` or `img.*`; do not mix IP / `pages.dev` |
|
||
| 2 | Pages / Worker in the middle | Image host must **not** `fetch(admin…)` |
|
||
| 3 | Reverse-proxy upstream | Both hostnames → `http://127.0.0.1:6892` |
|
||
| 4 | Nginx `proxy_set_header Host` | `$host` (preserve client hostname) |
|
||
| 5 | PicHost settings | `SITE_BASE_URL` / `IMAGE_BASE_URL` match `server_name` |
|
||
| 6 | Port `6892` | Reachable only from localhost or internal proxy |
|
||
|
||
---
|
||
|
||
## Recommended layout
|
||
|
||
Both hostnames go through Cloudflare (proxied) and **independently** reverse-proxy to one PicHost instance:
|
||
|
||
```mermaid
|
||
flowchart TB
|
||
internet[Internet]
|
||
cf[Cloudflare proxied]
|
||
admin[admin.example.com]
|
||
img[img.example.com]
|
||
panel[1Panel / Nginx]
|
||
app[PicHost :6892]
|
||
|
||
internet --> cf
|
||
cf --> admin
|
||
cf --> img
|
||
admin --> panel
|
||
img --> panel
|
||
panel --> app
|
||
```
|
||
|
||
```env
|
||
SITE_BASE_URL=https://admin.example.com
|
||
IMAGE_BASE_URL=https://img.example.com
|
||
```
|
||
|
||
```text
|
||
admin.example.com → http://127.0.0.1:6892
|
||
img.example.com → http://127.0.0.1:6892
|
||
```
|
||
|
||
You do **not** need separate ports for “frontend / backend / images” — one PicHost instance on port `6892` is enough.
|
||
|
||
### Site hostname ≠ frontend domain
|
||
|
||
| Hostname | Serves |
|
||
| -------- | ------ |
|
||
| `admin.example.com` | Admin UI, login, settings, gallery, API, upload |
|
||
| `img.example.com` | Image delivery, hotlinks, CDN |
|
||
|
||
Use **site / image hostname**, not “frontend / backend”. PicHost does **not** require `frontend :3000 + backend :6892` in production.
|
||
|
||
---
|
||
|
||
## Orange cloud & preferred edge
|
||
|
||
**DNS Proxied (orange cloud)** is normal and recommended:
|
||
|
||
```text
|
||
admin.example.com, img.example.com
|
||
A/CNAME → origin → Proxied
|
||
```
|
||
|
||
Opening `admin.example.com` should yield `Host: admin.example.com` at PicHost; `img.example.com` should yield `Host: img.example.com`.
|
||
|
||
**Preferred-edge** setups (alternate IPs, entry hostnames) are also fine. Preferred routing may change the path to Cloudflare’s edge, but must **not** rewrite the image hostname’s origin identity to the site hostname:
|
||
|
||
```text
|
||
admin → CF → admin.example.com → origin → PicHost
|
||
img → CF → img.example.com → origin → PicHost
|
||
```
|
||
|
||
---
|
||
|
||
## 6. Typical bad topologies
|
||
|
||
These patterns break Host isolation even when the browser still shows the image hostname:
|
||
|
||
### ❌ Bad 1: Image host fetches site host
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
user[User on img.example.com]
|
||
worker[Pages / Worker]
|
||
admin[fetch admin.example.com]
|
||
app[PicHost]
|
||
|
||
user --> worker --> admin --> app
|
||
```
|
||
|
||
The last hop has `Host: admin.example.com`, so PicHost treats it as site traffic — `https://img.example.com/` may show the admin UI. This is **Host rewrite**, not a cache bug.
|
||
|
||
### ❌ Bad 2: Preferred proxy upstreams to site host
|
||
|
||
```text
|
||
img.example.com → preferred hostname → admin.example.com → origin
|
||
```
|
||
|
||
Even for routing only, using the site hostname as upstream breaks image isolation.
|
||
|
||
### ❌ Bad 3: Public exposure of port 6892
|
||
|
||
```text
|
||
any host / IP:6892 → PicHost
|
||
```
|
||
|
||
Users can bypass Cloudflare and Nginx `server_name` rules. Port `6892` should be reachable only from `127.0.0.1` or an internal reverse proxy.
|
||
|
||
### ✅ Correct pattern
|
||
|
||
```text
|
||
admin → admin → PicHost
|
||
img → img → PicHost
|
||
```
|
||
|
||
Do **not** use Pages / Workers as a **full-site reverse proxy** for PicHost (extra `pages.dev` / `workers.dev` entry points). PicHost needs `node-server`, SQLite, `sharp`, and local `data/` — it **cannot** run as a Node app on Pages/Workers. Keep Docker/VPS for the app; use orange-cloud DNS and optional R2 on CF.
|
||
|
||
---
|
||
|
||
## Workers / Pages guidance
|
||
|
||
If you only need headers, auth, Referer rules, logging, or cache — and PicHost remains your origin:
|
||
|
||
| Prefer | Avoid |
|
||
| ------ | ----- |
|
||
| `img.example.com` → **Worker Route** → existing `img` origin | `img` → Worker → `fetch(admin.example.com)` |
|
||
|
||
Cloudflare recommends [Routes](https://developers.cloudflare.com/workers/configuration/routing/routes/) when an external origin already exists, rather than [Custom Domains](https://developers.cloudflare.com/workers/configuration/routing/custom-domains/) used to chain cross-host `fetch()`.
|
||
|
||
Code like this on the image hostname is a smoking gun:
|
||
|
||
```js
|
||
fetch(`https://admin.example.com${url.pathname}`)
|
||
```
|
||
|
||
---
|
||
|
||
## Nginx / 1Panel layout
|
||
|
||
Two `server` blocks, same upstream. Critical line: `proxy_set_header Host $host;`
|
||
|
||
```nginx
|
||
server {
|
||
server_name admin.example.com;
|
||
location / {
|
||
proxy_pass http://127.0.0.1:6892;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Forwarded-Proto $scheme;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||
client_max_body_size 12m;
|
||
}
|
||
}
|
||
|
||
server {
|
||
server_name img.example.com;
|
||
location / {
|
||
proxy_pass http://127.0.0.1:6892;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Forwarded-Proto $scheme;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||
}
|
||
}
|
||
|
||
# Reject bare IP and unknown hostnames (strongly recommended)
|
||
server {
|
||
listen 80 default_server;
|
||
listen 443 ssl default_server;
|
||
server_name _;
|
||
return 444;
|
||
}
|
||
```
|
||
|
||
With orange cloud, restrict the origin firewall to **[Cloudflare IP ranges](https://www.cloudflare.com/ips/)** only.
|
||
|
||
---
|
||
|
||
## Two-layer Host protection
|
||
|
||
Use reverse proxy and application layers **together**:
|
||
|
||
| Layer | Role |
|
||
| ----- | ---- |
|
||
| **Nginx default server** | Reject bare IP, `pages.dev`, and other undeclared hostnames |
|
||
| **PicHost middleware (v1.2.x+)** | When dual-domain is on, unconfigured hosts get 404 at the app (`localhost` / `127.0.0.1` exempt in development) |
|
||
|
||
Even with app-layer blocking, keep a default server at the proxy to reduce junk traffic to PicHost.
|
||
|
||
---
|
||
|
||
## X-Forwarded-Host
|
||
|
||
PicHost may consult `X-Forwarded-Host` behind a reverse proxy, but that header is **forgeable** by clients. Therefore:
|
||
|
||
- Do not expose port `6892` publicly
|
||
- Only trusted proxies should reach the app
|
||
- Prefer keeping `Host` correct from the first hop
|
||
|
||
---
|
||
|
||
## Troubleshooting: admin UI on image hostname?
|
||
|
||
**Inspect the proxy chain first — do not purge CF cache first.**
|
||
|
||
Cache issues usually mean stale JS/CSS/images or occasional 404s. If `/`, `/login`, and `/settings` work **reliably** on the image host, the Host was almost certainly rewritten.
|
||
|
||
```text
|
||
① Browser hostname?
|
||
② Pages / Worker / preferred proxy in the middle?
|
||
③ What URL does the proxy fetch?
|
||
④ Does it fetch admin.example.com?
|
||
⑤ Which Host does 1Panel / PicHost see?
|
||
```
|
||
|
||
---
|
||
|
||
## Related
|
||
|
||
- [Dual-domain separation](./domain-separation.md)
|
||
- [Reverse proxy](./reverse-proxy.md)
|
||
- [Environment variables](./configuration.md)
|
||
- [FAQ](./faq.md)
|