release: v1.2.2 — 双域名配置与 Host 隔离修复
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
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 与中间件,并更新文档站。
This commit is contained in:
1 parent
6c6766221b
commit
c9c34515dd
36 files changed
+2578
-180
No files matched your search
@@ -0,0 +1,113 @@
|
||||
# Changelog
|
||||
|
||||
Version history and notable changes. The full log is also in the repo root [CHANGELOG.md](https://github.com/O96u/PicHost/blob/main/CHANGELOG.md).
|
||||
|
||||
---
|
||||
|
||||
## [1.2.2] — 2026-08-30
|
||||
|
||||
> **Restart the service** after upgrading so middleware and API updates take effect.
|
||||
|
||||
### Dual-domain settings & Host isolation
|
||||
|
||||
#### Fixes
|
||||
|
||||
| Issue | Description |
|
||||
| ----- | ----------- |
|
||||
| Site URL cleared by mistake | Saving Settings could write an empty `site_base_url` when disabling dual-domain or editing only the image URL |
|
||||
| Config vs effective URL mixed | `GET /api/settings` used to blend DB values with `request.origin`, confusing the Settings UI |
|
||||
| Third-host bypass | Unlisted hosts (`pages.dev`, bare IP, old image domains) were not blocked by middleware |
|
||||
|
||||
#### Improvements
|
||||
|
||||
**Settings API (`GET /api/settings`)**
|
||||
|
||||
| Field | Meaning |
|
||||
| ----- | ------- |
|
||||
| `siteBaseUrl` / `imageBaseUrl` | **Configured** values (DB → env), no request fallback |
|
||||
| `effectiveSiteBaseUrl` / `effectiveImageBaseUrl` | **Link generation**, falls back to request origin when unset |
|
||||
| `runtime.currentOrigin` | Detected access URL (display only, not saved) |
|
||||
| `runtime.hostRole` | `site` / `image` / `unknown` / `single` |
|
||||
|
||||
**Save API (`PATCH /api/settings`)**
|
||||
|
||||
- New body field `domainSeparation` (`true` / `false`)
|
||||
- `domainSeparation: true` requires both URLs with different hostnames
|
||||
- `domainSeparation: false` allows clearing the site URL
|
||||
- Without `domainSeparation`, cannot clear `siteBaseUrl` while dual-domain is active
|
||||
|
||||
**Settings / setup UI**
|
||||
|
||||
- Form uses configured values; “Active” line shows `effective*`
|
||||
- Shows detected access URL; **no longer** auto-fills `window.location.origin`
|
||||
- “Use detected URL” button (opt-in)
|
||||
- Confirmation modal when disabling dual-domain
|
||||
- Warning when `hostRole === 'unknown'` with dual-domain on
|
||||
|
||||
**Host middleware**
|
||||
|
||||
- When dual-domain is on, unlisted hosts get **404** at the app layer (all methods)
|
||||
- `localhost` / `127.0.0.1` exempt in development
|
||||
- Complements Nginx `default_server` — use both layers in production
|
||||
|
||||
#### Docs
|
||||
|
||||
- Rewrote [Cloudflare deployment](./cloudflare-deployment.md)
|
||||
- Updated [Dual-domain separation](./domain-separation.md) for third-host blocking
|
||||
- Added this changelog page
|
||||
|
||||
#### Upgrade notes
|
||||
|
||||
- **No** database migration or CLI step
|
||||
- If the site URL was cleared earlier: re-enter it in **Settings**, or set `SITE_BASE_URL` in env and restart
|
||||
- Production: match proxy `server_name` to Settings; keep a default server block
|
||||
|
||||
---
|
||||
|
||||
## [1.2.1] — 2026-08-29
|
||||
|
||||
### Added
|
||||
|
||||
- Slide puzzle login captcha
|
||||
- VitePress docs site on GitHub Pages
|
||||
- `npm run docs:dev` / `docs:build` / `docs:preview`
|
||||
|
||||
### Fixed
|
||||
|
||||
- Gallery not refreshing after delete on stats page
|
||||
- Pagination jump with `type="number"` input
|
||||
- Copy-link format preference not persisting off home route
|
||||
- Captcha UI alignment
|
||||
|
||||
### Improved
|
||||
|
||||
- README trimmed; details moved to docs
|
||||
- Dual-domain docs: unknown Host risk, Cloudflare guide
|
||||
|
||||
---
|
||||
|
||||
## [1.2.0] — 2026-08-28
|
||||
|
||||
### Changed
|
||||
|
||||
- All images under `data/images/`; legacy `blog/` / `twikoo/` need migration
|
||||
|
||||
### Added
|
||||
|
||||
- CLI: `docker exec pichost migrate` / `migrate --apply`
|
||||
- Startup index sync (disk scan, legacy key normalization, orphan cleanup)
|
||||
|
||||
See [v1.2 migration](./migration.md).
|
||||
|
||||
---
|
||||
|
||||
## [1.1.x] summary
|
||||
|
||||
| Version | Highlights |
|
||||
| ------- | ---------- |
|
||||
| 1.1.5 | API upload preview fix; file existence check before serve |
|
||||
| 1.1.4 | Date-based storage paths; site host blocks image URLs |
|
||||
| 1.1.3 | Dual-domain (`SITE_BASE_URL` / `IMAGE_BASE_URL`); Host middleware |
|
||||
| 1.1.0 | Multi storage backends (S3 / R2) |
|
||||
|
||||
Older entries: [GitHub CHANGELOG](https://github.com/O96u/PicHost/blob/main/CHANGELOG.md).
|
||||
@@ -0,0 +1,236 @@
|
||||
# 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)
|
||||
@@ -9,8 +9,8 @@ Configure PicHost via environment variables (Docker / `.env`) or the in-app **Se
|
||||
| API upload token | Env → SQLite | **Yes** (cannot regenerate in UI) |
|
||||
| WebP quality | SQLite → env → default 80 | No |
|
||||
| Referer whitelist | SQLite → env | No |
|
||||
| Site / image base URL | SQLite → env → request origin | No |
|
||||
| Hide `images/` prefix | SQLite → env → default false | No |
|
||||
| Site / image base URL | SQLite → env (configured in API); links use `effective*` or request origin | No |
|
||||
| Short image links | SQLite → env → default false | No |
|
||||
| Date-based paths | SQLite → env → default true | No |
|
||||
| Global auto-delete days | SQLite → env → default 0 | No |
|
||||
|
||||
@@ -33,11 +33,13 @@ Comma-separated Referer whitelist. Own hostnames are allowed automatically. Opti
|
||||
- **Site URL**: admin UI, API, Twikoo upload
|
||||
- **Image URL**: copied links and public image URLs
|
||||
|
||||
Single-domain: set `IMAGE_BASE_URL` only or leave empty (request origin). Dual-domain: hostnames must differ — see [Dual-domain separation](./domain-separation.md). Values must match proxy `server_name`; do not use `localhost`, IP, or unlisted hostnames for production admin access.
|
||||
Single-domain: set `IMAGE_BASE_URL` only or leave empty (request origin). Dual-domain: hostnames must differ — see [Dual-domain separation](./domain-separation.md) and [Cloudflare deployment](./cloudflare-deployment.md). Values must match proxy `server_name`; do not use `localhost`, IP, or unlisted hostnames for production admin access.
|
||||
|
||||
In **Settings**, enable dual-domain explicitly and fill both URLs; disabling clears the site URL (with confirmation). Configured values vs effective link URLs are separate in the API — see [Changelog](./changelog.md#1-2-2-2026-08-30).
|
||||
|
||||
### `HIDE_FOLDER_IN_URL`
|
||||
|
||||
`true` hides the `images/` prefix in public URLs.
|
||||
`true`: short links (domain + filename only), e.g. `img.example.com/a8K3xP.webp`. `false`: keep date or custom folders, e.g. `img.example.com/2026/08/a8K3xP.webp`. Public URLs never expose the internal `images/` prefix; legacy `/images/...` paths still work.
|
||||
|
||||
### `STORAGE_USE_DATE_PATH` / `STORAGE_LAYOUT`
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ Set during `/setup` or in **Settings**:
|
||||
- **Site URL**: `https://admin.example.com`
|
||||
- **Image URL**: `https://pic.example.com`
|
||||
|
||||
Setup and Settings **do not** auto-fill the site URL from `window.location.origin`; use manual input or “Use detected URL”. Disabling dual-domain requires confirmation and clears the site URL. Saves include `domainSeparation: true/false`. See [Changelog](./changelog.md#1-2-2-2026-08-30).
|
||||
|
||||
## Nginx example
|
||||
|
||||
```nginx
|
||||
@@ -53,36 +55,42 @@ server {
|
||||
|
||||
## Security notes
|
||||
|
||||
### Middleware only splits the two configured hostnames
|
||||
### Middleware isolation and third hosts
|
||||
|
||||
PicHost uses the request **Host** (and `X-Forwarded-Host` when proxied) to decide site vs image domain:
|
||||
|
||||
- **Image host**: non-image paths (e.g. `/`, `/settings`) → 404
|
||||
- **Site host**: image URL paths (e.g. `/images/...`) → 404
|
||||
- **Unconfigured third hosts** (e.g. `*.pages.dev`, bare IP, old image domains): **from v1.2.x the app returns 404 for all methods** (`localhost` / `127.0.0.1` are exempt in development for `npm run dev`)
|
||||
|
||||
If the Host is **neither** the configured site nor image hostname (e.g. `localhost`, the server **public IP**, Cloudflare **Pages** `*.pages.dev`, **Workers** `*.workers.dev`, or any other unlisted name), **the app does not block the request today** — admin and API may still be reachable. This is not about hyphenated or long domain names; it is an extra entry point outside your dual-domain pair.
|
||||
This is independent of hyphenated or long domain names. Older versions did not block third hosts; rely on a reverse-proxy default server as a complement in production.
|
||||
|
||||
**Production recommendations:**
|
||||
|
||||
1. Configure `server_name` only for the site and image hosts; add a **default server** to reject other Host values and bare IP access (see Nginx example above)
|
||||
1. Configure `server_name` only for the site and image hosts; add a **default server** to reject other Host values and bare IP access (see Nginx example above; complements app-layer blocking)
|
||||
2. With Cloudflare **orange-cloud** DNS, restrict the origin firewall to **[Cloudflare IP ranges](https://www.cloudflare.com/ips/)** so traffic cannot bypass the CDN
|
||||
3. Sign in only on the **site hostname** from Settings; do not mix `localhost`, IP, or unlisted domains
|
||||
4. Do **not** put Cloudflare Pages / Workers in front of PicHost as a **full-site reverse proxy** (extra `pages.dev` / `workers.dev` entry points worsen this issue)
|
||||
5. PicHost **cannot** run as-is on Pages/Workers (needs `node-server`, SQLite, `sharp`, local `data/`). Keep Docker/VPS; use orange-cloud DNS and optional R2 storage on CF
|
||||
4. Do **not** put Cloudflare Pages / Workers in front of PicHost as a **full-site reverse proxy** (`IMAGE_BASE_URL` may point at a CDN URL, but the app itself should run on Docker/VPS)
|
||||
5. If you must proxy at the edge, preserve the client **`Host` or `X-Forwarded-Host`** or host isolation will fail
|
||||
6. PicHost **cannot** run as-is on Pages/Workers (needs `node-server`, SQLite, `sharp`, local `data/`). Keep Docker/VPS; use orange-cloud DNS and optional R2 storage on CF
|
||||
|
||||
### Cloudflare orange cloud (incl. preferred edge IPs)
|
||||
|
||||
**Recommended:** both `admin.example.com` and `pic.example.com` **DNS Proxied** → origin Nginx / 1Panel → port `6892`, SSL **Full (strict)** (origin cert or Cloudflare Origin Certificate).
|
||||
Full guide: [Cloudflare deployment](./cloudflare-deployment.md).
|
||||
|
||||
**Recommended:** both `admin.example.com` and `pic.example.com` **DNS Proxied** → origin Nginx / 1Panel → port `6892`, SSL **Full (strict)**.
|
||||
|
||||
| Feature | Notes |
|
||||
| ------- | ----- |
|
||||
| Orange-cloud CDN | Compatible with dual-domain; preserve `Host` and `X-Forwarded-Proto` on origin |
|
||||
| DNS “preferred IP” setups | Only changes which CF edge IP clients hit; Host stays your real domain |
|
||||
| DNS “preferred IP” setups | Only changes which CF edge IP clients hit; origin identity must stay **admin → admin**, **img → img** |
|
||||
| R2 storage | Add an R2 backend under **Storage**; independent of orange cloud |
|
||||
| `cloudflare` Git branch | R2-focused deployment line; still Docker, **not** Workers hosting |
|
||||
|
||||
**Do not confuse** “Create Worker” in the CF dashboard (Git + `wrangler deploy`) with deploying PicHost — the repo uses `nitro.preset: 'node-server'` and is not Workers-ready.
|
||||
|
||||
**Do not** chain the image hostname to `fetch(admin…)` via Pages/Workers — that rewrites Host and breaks isolation ([bad topologies](./cloudflare-deployment.md#6-typical-bad-topologies)).
|
||||
|
||||
See [Reverse proxy](./reverse-proxy.md#cloudflare-orange-cloud) for origin hardening.
|
||||
|
||||
## FN NAS · Lucky reverse proxy
|
||||
@@ -152,7 +160,7 @@ Always open admin on the **site hostname**, not `localhost` (cookies and Host mu
|
||||
| `http://admin.pichost.test:3000/images/...` or `/2026/08/xxx.webp` | **404** (use image host) |
|
||||
| `http://pic.pichost.test:3000/` | **404** (image host has no admin) |
|
||||
| `http://pic.pichost.test:3000/images/...` | Image served |
|
||||
| `http://localhost:3000/` or `http://127.0.0.1:3000/` | **Admin still loads** (third Host — isolation skipped; OK for dev only; block at proxy in production) |
|
||||
| `http://localhost:3000/` or `http://127.0.0.1:3000/` | **Admin still loads** (development exception; block unconfigured hosts at proxy in production) |
|
||||
|
||||
After upload, copied links should use `pic.pichost.test`; gallery thumbnails should load.
|
||||
|
||||
|
||||
@@ -21,11 +21,15 @@ Check the **Referer whitelist** (`ALLOWED_REFERER_HOSTS` or Settings). Referring
|
||||
|
||||
## Why can admin load on pages.dev / workers.dev / IP with dual-domain?
|
||||
|
||||
Dual-domain isolation only splits paths on the **configured site and image hostnames**. Other Host values (`localhost`, public IP, Cloudflare **Pages** `*.pages.dev`, **Workers** `*.workers.dev`, etc.) are **not blocked today**, so admin may appear on unexpected URLs.
|
||||
**From v1.2.2:** when dual-domain is on, PicHost returns **404** for Host values **other than** the configured site and image hostnames (`localhost` / `127.0.0.1` exempt in development). See [Changelog](./changelog.md#1-2-2-2026-08-30).
|
||||
|
||||
Common cause: **full-site reverse proxy** via Pages/Workers on top of orange-cloud DNS, or an origin that accepts any Host / bare IP.
|
||||
If admin still loads:
|
||||
|
||||
Fix: use only the two configured domains; add a default `server_name` block; with orange cloud, allow only CF IPs on the origin. See [Security notes](./domain-separation.md#security-notes).
|
||||
1. **Dual-domain not active** (image URL set but site URL empty) — neither path split nor third-host blocking applies
|
||||
2. **Proxy rewrote Host to site hostname** — e.g. Pages/Worker `fetch(admin…)` on the image hostname
|
||||
3. **Port `6892` exposed publicly** — bypasses Cloudflare and Nginx `server_name`
|
||||
|
||||
Fix: configure both URLs in Settings; add a default server block; with orange cloud, allow only CF IPs on origin; do not full-proxy through Pages/Workers. See [Dual-domain separation](./domain-separation.md#middleware-isolation-and-third-hosts), [Cloudflare deployment](./cloudflare-deployment.md), and [Changelog](./changelog.md#1-2-2-2026-08-30).
|
||||
|
||||
## Can I deploy PicHost on Cloudflare Workers / Pages?
|
||||
|
||||
@@ -63,8 +67,6 @@ Use `data/mapping.json` from `migrate` preview. Keys are **storage paths**, not
|
||||
|
||||
This **VitePress site** is the full guide; README is a short overview. Online: <https://o96u.github.io/PicHost/>
|
||||
|
||||
If you get 404, GitHub Pages may not be deployed yet: set **Settings → Pages → Source** to **GitHub Actions** and ensure the `docs` workflow `deploy` job succeeded. See [Local dev · Publish to GitHub Pages](./local-dev.md#publish-to-github-pages).
|
||||
|
||||
## What should the GitHub repo About say?
|
||||
|
||||
For **main**:
|
||||
|
||||
@@ -6,8 +6,9 @@ Welcome to the PicHost user guide. This site is the single source of truth; the
|
||||
|
||||
1. [Quick start](./getting-started.md) — Docker and first-time setup
|
||||
2. [Environment variables](./configuration.md) — Production config vs in-app settings
|
||||
3. [Storage](./storage.md) — Backends and URL modes
|
||||
4. [API](./api.md) — REST endpoints and tokens
|
||||
3. [Changelog](./changelog.md) — Version history and upgrade notes
|
||||
4. [Storage](./storage.md) — Backends and URL modes
|
||||
5. [API](./api.md) — REST endpoints and tokens
|
||||
|
||||
## Screenshots
|
||||
|
||||
|
||||
@@ -51,16 +51,4 @@ npm run docs:build
|
||||
npm run docs:preview
|
||||
```
|
||||
|
||||
Sources live in `docs-site/`; screenshot sources are in `docs/screenshots/` (including `login.png`), copied to `docs-site/public/screenshots/` before build.
|
||||
|
||||
## Publish to GitHub Pages
|
||||
|
||||
Live docs: <https://o96u.github.io/PicHost/>
|
||||
|
||||
**First-time setup** (if deploy fails or the site 404s):
|
||||
|
||||
1. Repo **Settings → Pages**
|
||||
2. **Build and deployment → Source** → **GitHub Actions** (not “Deploy from a branch”)
|
||||
3. Push to `main` or run the `docs` workflow manually from Actions
|
||||
|
||||
Allow 1–2 minutes after a successful deploy. If the `deploy` job fails immediately, Pages is usually not enabled yet.
|
||||
Sources live in `docs-site/`; screenshot sources are in `docs/screenshots/` (including `login.png`), copied to `docs-site/public/screenshots/` before build. Live docs: <https://o96u.github.io/PicHost/>
|
||||
@@ -41,18 +41,18 @@ See [Dual-domain separation](./domain-separation.md).
|
||||
|
||||
## Cloudflare orange cloud
|
||||
|
||||
Run PicHost on **your server / Docker** (port `6892`). On Cloudflare:
|
||||
See [Cloudflare deployment](./cloudflare-deployment.md).
|
||||
|
||||
| Practice | Notes |
|
||||
| -------- | ----- |
|
||||
| **Proxied DNS** | Both admin and image hostnames → origin proxy to `6892`, SSL **Full (strict)** |
|
||||
| Preferred edge IPs | Compatible; Host remains your real domain |
|
||||
| Preferred edge IPs | Compatible; keep **admin → admin**, **img → img** on origin |
|
||||
| **R2** | Object storage backend in **Storage**; not the same as hosting the app on Pages |
|
||||
| Origin firewall | Allow only [Cloudflare IPs](https://www.cloudflare.com/ips/); do not expose `6892` publicly |
|
||||
|
||||
**Avoid:**
|
||||
|
||||
- **Cloudflare Pages** (`*.pages.dev`) or **Workers** (`*.workers.dev`) as a **full-site reverse proxy** to PicHost — extra hostnames can bypass dual-domain isolation ([Security notes](./domain-separation.md#security-notes))
|
||||
- **Cloudflare Pages** (`*.pages.dev`) or **Workers** (`*.workers.dev`) as a **full-site reverse proxy**, or chaining the image host to `fetch(admin…)` — rewrites Host and bypasses dual-domain isolation ([guide](./cloudflare-deployment.md))
|
||||
- “Create Worker” from Git on this repo — `node-server` preset is not Workers-ready
|
||||
|
||||
## Origin hardening (dual-domain)
|
||||
|
||||
Reference in new issue
Block a user