O96u 438c1d5529 feat: storage path layout, basename URLs, and bidirectional domain isolation
Add storage_use_date_path for flat vs date-key storage layout and emit
basename-only public URLs when hide-folder is enabled. Block image paths
on the site host when domain separation is active so only the image host
serves files. Update settings UI, i18n, tests, and deployment docs.
2026-08-28 09:56:45 +08:00
2026-08-25 16:11:50 +08:00
2026-08-22 14:36:57 +08:00
2026-08-22 14:36:57 +08:00
2026-08-22 14:36:57 +08:00
2026-08-22 14:36:57 +08:00
2026-08-23 17:43:42 +08:00
2026-08-22 14:36:57 +08:00
2026-08-27 15:32:31 +08:00
2026-08-22 14:36:57 +08:00
2026-08-22 14:36:57 +08:00
2026-08-25 16:11:50 +08:00

PicHost

PicHost

Lightweight personal image hosting · Simple uploads, clean management

Self-hosted / Multi-user / Docker / API / Twikoo · Local disk or object storage

version CI Nuxt 4 Docker GPL-3.0

Branches · Quick Start · Screenshots · Features · Tech Stack · Roadmap · 简体中文


Branches

Branch Notes
main (default) v1.1.3 — local disk + multi-backend storage, dual-domain separation, /storage UI, hybrid URLs
cloudflare Cloudflare R2–only line — streamlined for deployments that use R2 exclusively

Use main for the full product and multiple cloud backends.

If you only use Cloudflare R2, check out the dedicated branch:

git clone https://github.com/O96u/PicHost.git
cd PicHost
git checkout cloudflare

Screenshots

Upload Storage
Upload Storage
Stats & gallery Settings
Stats Settings

Features

  • Drag, click, or Ctrl+V paste to upload; admins can use custom folder paths
  • Server-side WebP (sharp), Referer hotlink protection, configurable public base URL
  • Multi-user — username/password login; optional registration; users see only their images
  • Upload preferences (home card flip): client-side compression, auto-copy links, per-user auto-delete
  • Multi-backend storage: local disk + S3-compatible (Cloudflare R2 / Tencent COS / Alibaba OSS / AWS S3); admins manage backends at Storage (/storage)
  • Hybrid URLs: proxy (same-origin via PicHost) or public (302 to CDN / bucket URL)
  • Gallery — browse, search, batch delete; admin sees uploader badges
  • Stats — upload/delete metrics, folder breakdown; admin sees registered user count
  • API — global token (admin) + per-user tokens; in-app cURL snippets
  • Twikoo — POST /api/index.php (image + token)
  • Zero-config Docker — first-run web wizard, no secrets required upfront

Quick Start

docker run -d \
  --name pichost \
  -p 6892:6892 \
  -v ./data:/data \
  --restart unless-stopped \
  muxui/pichost:latest

Default port: 6892. Open http://<host>:6892 in your browser and complete the setup wizard.

Forgot password (requires docker exec / server access):

docker exec pichost reset-password              # admin only, when there is exactly one
docker exec pichost reset-password <username>   # admin or regular user

A random password is printed. Fails if the user does not exist.

If you cloned the repo, you can also use docker compose up -d (see docker-compose.yml).

Local development

npm install
cp .env.example .env   # optional
npm run dev

Open http://localhost:3000/setup to create the admin account. Data defaults to ./data.

Tech Stack

Layer Technology
Frontend Nuxt 4, Vue 3, Nuxt UI 4, Tailwind CSS 4
Backend Nitro (Node.js), SQLite
Images sharp (WebP transcoding & compression)
Auth Session cookie, scrypt password hashing, API tokens
Storage Local disk + S3-compatible backends (R2 / COS / OSS / AWS); images index in SQLite
Deploy Docker (amd64 / arm64), docker compose
Tests Vitest

Users & permissions

Scenario Auth Ownership / visibility
Web upload Session Current user
API upload + user token Auth-Token Token owner
API upload + global token Auth-Token Admin (userId null)
Twikoo /api/index.php form token Same as global token
Gallery list / search / delete Session or token Users: own only; admin: all
Direct link GET /images/... None (Referer rules) Public if URL is known

Regular users are limited to the images/ folder; custom folder requires admin or global token.

Storage layout

/data
├── pichost.db          # SQLite: users, sessions, settings, storage_backends, images index
├── images/             # Default upload folder (when using local backend)
├── blog/               # Example custom folder
└── twikoo/             # Twikoo comment images

Pattern: folder/YYYY/MM/randomId.webp + .meta.json. With cloud backends, blobs live in the bucket; SQLite images tracks backend_id and key. Back up all of /data plus bucket data.

API overview

Method Path Description
POST /api/images/upload Upload image
GET /api/images Paginated list
DELETE /api/images Delete one (key)
POST /api/index.php Twikoo / EasyImage 2.0

Upload example:

curl -X POST "https://pic.example.com/api/images/upload" \
  -H "Auth-Token: YOUR_TOKEN" \
  -F "folder=blog" \
  -F "image=@./demo.png"

Full docs and copyable cURL snippets are on the API page after deployment.

Twikoo

Setting Value
IMAGE_CDN easyimage
IMAGE_CDN_URL https://your-domain/api/index.php
IMAGE_CDN_TOKEN Same as API_UPLOAD_TOKEN

Reverse proxy

  • Proxy to port 6892; HTTPS recommended
  • Nginx: client_max_body_size ≥ 12m
  • Set IMAGE_BASE_URL (single domain) or SITE_BASE_URL + IMAGE_BASE_URL (dual-domain separation)

One Docker instance + two domains, both fully proxied to 6892 (isolation via PicHost middleware):

# admin.example.com — admin + API + images
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;
    client_max_body_size 12m;
  }
}

# pic.example.com — same full proxy; PicHost middleware blocks non-image paths
server {
  server_name pic.example.com;
  location / {
    proxy_pass http://127.0.0.1:6892;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Proto $scheme;
  }
}

FN NAS Lucky reverse proxy (public hostname → backend http://<LAN-IP>:6892):

Site domain (admin) Image domain
Lucky site Lucky image

See docs/domain-separation.en.md for Caddy/NPM notes, env vars, and setup fields.

Development

npm run dev          # Dev server
npm run build        # Production build
npm run start        # Run .output
npm run lint         # ESLint
npm run typecheck    # Type check
npm test             # Unit tests

Roadmap

Version Plan
v1.1.3 Dual-domain separation, optional images/ URL prefix, update check (current main)
v1.1.2 Activity logs, gallery storage filter, upload rate limits
v1.1.1 Session check fixes, storage placeholder cleanup, Docker logging
v1.1.0 Multi-backend object storage, hybrid URLs, storage UI
v1.0.4 Password visibility toggle, legacy ADMIN_SECRET migration
v1.0.3 CI and Docker multi-arch build fixes
v1.0.2 i18n, mobile menus, stats/upload UI, Docker password reset
v1.0.1 Settings version display, mobile upload preferences layout fix
v1.0.0 Local storage, multi-user, API, Twikoo

License

GPL-3.0

LINUX DO

S
Description
No description provided
Readme GPL-3.0
7.8 MiB
0 Stars 1 Watchers 0 Forks
2026-10-18 20:54:03 +08:00
Languages
TypeScript 52.3%
Vue 46%
JavaScript 1.2%
CSS 0.3%
Dockerfile 0.1%