Self-hosted HTML file sharing. Upload an HTML file, get a short-lived public URL. https://c81920cc.dropit.patilla.es/
  • Python 48.3%
  • CSS 20.6%
  • JavaScript 18%
  • HTML 10.9%
  • Just 1.6%
  • Other 0.6%
Find a file
2026-05-31 20:30:22 +02:00
.forgejo/workflows ci: add GHCR push to release workflow 2026-05-23 14:14:07 +02:00
app fix: check Content-Length header early and cap chunked read to max_upload_size 2026-05-31 19:58:46 +02:00
tests fix: rename test to clarify it tests body-read rejection, not chunked transfer 2026-05-31 19:59:57 +02:00
.dockerignore chore: project scaffold 2026-05-21 12:52:16 +02:00
.env.example docs: add CONTENT_DOMAIN to .env.example 2026-05-28 17:14:30 +02:00
.gitignore docs: update README layout and ignore playwright-mcp artifacts 2026-05-22 16:05:56 +02:00
compose.yml chore: replace port 52031 with 8000 everywhere 2026-05-22 16:43:35 +02:00
Dockerfile feat: serve user pages on per-page subdomains for origin isolation 2026-05-28 16:42:00 +02:00
justfile wip 2026-05-22 16:13:38 +02:00
pyproject.toml chore: project scaffold 2026-05-21 12:52:16 +02:00
README.md wip 2026-05-31 20:30:22 +02:00
uv.lock chore: project scaffold 2026-05-21 12:52:16 +02:00

dropit logo drop•it

Self-hosted HTML file sharing. Upload an HTML file, get a short-lived public URL.

See it in action: this README is hosted on dropit

Landing page   Admin panel

Quick start

cp .env.example .env        # edit UPLOAD_TOKENS and BASE_URL
just install                # create venv and install deps
just dev                    # run on http://localhost:8000

Open http://localhost:8000 — enter your API token, drop an HTML file, copy the link.

Admin panel

Visit http://localhost:8000/admin with your admin token to list and delete all uploaded pages.

Set ADMIN_TOKEN in your .env to enable it:

# Generate a secure token
just admin-token

# Add to .env
ADMIN_TOKEN=<generated-value>

The admin token also unlocks the forever TTL and bypasses the per-user TTL limit.

Upload via API

curl -X POST http://localhost:8000/upload \
  -H "Authorization: Bearer tok_abc123" \
  -F "file=@page.html"
# {"url": "http://a3f8c1d2.localhost:8000", "expires_at": "..."}

# Custom TTL (1h, 6h, 24h, 48h, 7d — or "forever" with admin token)
curl -X POST "http://localhost:8000/upload?ttl=6h" \
  -H "Authorization: Bearer tok_abc123" \
  -F "file=@page.html"

# Permanent upload (admin token required)
curl -X POST "http://localhost:8000/upload?ttl=forever" \
  -H "Authorization: Bearer <admin-token>" \
  -F "file=@page.html"

Claude Code integration

A Claude Code skill is available for uploading HTML files directly from Claude Code sessions via /dropit. It handles file resolution, TTL selection, and upload in one step.

Self-hosting

Pre-built images are published for amd64 and arm64 (Raspberry Pi 4/5):

Registry Image
GitHub (GHCR) ghcr.io/patillacode/dropit:latest
Forgejo forgejo.patilla.es/patillacode/dropit:latest

Create a compose.yml on your server:

services:
  dropit:
    image: ghcr.io/patillacode/dropit:latest
    ports:
      - "8000:8000"
    volumes:
      - ./data:/data
    environment:
      # At least one upload token — share it with whoever should be able to upload
      UPLOAD_TOKENS: alice:your-token-here
      # Admin token — generate with: openssl rand -hex 32
      ADMIN_TOKEN: your-admin-token-here
      # Your public URL
      BASE_URL: https://dropit.example.com
      # Domain used for per-page share links (each page gets its own subdomain)
      # Requires a wildcard DNS record and SSL cert for *.dropit.example.com
      CONTENT_DOMAIN: dropit.example.com
      # Optional: allow permanent uploads (admin only)
      # ALLOWED_TTLS: 1h,6h,24h,48h,7d,forever
    restart: unless-stopped
docker compose up -d

Open http://your-server-ip:8000.

Behind a reverse proxy (nginx, Caddy, Traefik): remove the ports mapping, set BASE_URL to your public domain (e.g. https://dropit.example.com), set CONTENT_DOMAIN to the same domain (e.g. dropit.example.com), and proxy both dropit.example.com and *.dropit.example.com to the container on port 8000. You will need a wildcard SSL cert for *.dropit.example.com (Let's Encrypt DNS-01 challenge).

To update:

docker compose pull && docker compose up -d

Docker (local build)

# Build
docker build -t dropit .

# Run
docker run -p 8000:8000 \
  -e UPLOAD_TOKENS=alice:tok_abc123 \
  -e BASE_URL=http://localhost:8000 \
  -e ADMIN_TOKEN=your-admin-token \
  -v $(pwd)/data:/data \
  dropit

# Or with compose (uses .env file)
docker compose up

Development

just test           # run test suite
just lint           # check with ruff
just fix            # auto-fix lint + format
just reset-db       # delete dev database (forces fresh schema)
just admin-token    # generate a random admin token

Configuration

All settings via environment variables (see .env.example):

Variable Default Description
UPLOAD_TOKENS required name:token pairs, comma-separated
ADMIN_TOKEN Token for /admin panel; also allows forever TTL and bypasses MAX_USER_TTL
ALLOWED_TTLS 1h,6h,24h,48h,7d Accepted TTL values; add forever to enable permanent uploads
DEFAULT_TTL 24h TTL when not specified in upload request
MAX_USER_TTL 24h Maximum TTL for non-admin tokens
MAX_UPLOAD_SIZE 5242880 Max upload size in bytes (5 MB)
CLEANUP_INTERVAL_HOURS 1 How often expired pages are purged
DATA_DIR ./data Directory for SQLite DB and uploaded files
BASE_URL http://localhost:8000 Base URL for the app (used in OpenAPI docs, health checks)
CONTENT_DOMAIN localhost:8000 Domain for per-page share links — each page is served at {id}.{CONTENT_DOMAIN}; requires wildcard DNS + SSL in production

Cleanup

Expired pages are purged automatically by a background scheduler running inside the FastAPI process (APScheduler). On each run it deletes all pages whose expires_at has passed, removes the corresponding files from disk, and records the result.

The interval is controlled by CLEANUP_INTERVAL_HOURS (default: 1). No cron job or external worker is needed.

The admin panel shows a Cleanup scheduler card with the last run timestamp, how many pages were deleted, whether it was triggered by the scheduler or manually, and the next scheduled run time. You can also trigger a cleanup immediately from the panel without waiting for the next interval.

Release

Tag a version to trigger Docker build and push:

git tag v0.1.0
git push --tags

Requires REGISTRY_TOKEN secret set in Forgejo.