Source & license
Upstream license: MIT
License TL;DR
You can use it, change it, self-host it and sell it. Keep the original copyright and license notice with copies of the code. You don’t have to publish your changes. The authors don’t promise it will work.
Explain MIT in plain English →Summary of the main license. Separate packages and assets can have different terms.
Inspect repository ↗Read this project’s actual license ↗Repository owner
See the upstream repository for the original creator and contributors.
Maintain this project? Maintainer verification →Cloudflare hosting
Free tier eligible within limits
The documented Pastebin Worker deployment can use Cloudflare Free allowances for a small workload under the request, CPU and service-specific quotas below. This is conditional eligibility, not a measured zero-cost deployment; optional features, domains and external providers can add costs.
Hosting requirements
- Workers Free allows 100,000 requests per day shared across the account and 10 ms CPU per invocation; measure CPU-heavy authentication, parsing and rendering before assuming it fits.
- Keep KV below 100,000 reads/day, 1,000 writes, deletes and list operations/day each, and 1 GB; cache refreshes and backups consume writes.
- Use R2 Standard storage, at most 10 GB-month, 1 million Class A operations and 10 million Class B operations/month; provision an eligible billing-enabled R2 account.
- Use a small personal or team workload; domain registration and optional third-party providers are separate costs. Provision your own IDs, secrets and migrations.
Sources checked 01/10/2026
Repository snapshot: 0835cac. Hosting eligibility reflects the deployment documentation and listed assumptions.
- pastebin ↗
This is a pastebin running on Cloudflare workers. Try it on [shz.al](https://shz.al).
- wetransfer ↗
This is a pastebin running on Cloudflare workers. Try it on [shz.al](https://shz.al).
- workers ↗
name = "pb" compatibility_date = "2025-04-24" workers_dev = false main = "worker/index.ts" [[rules]] type = "Text" globs = [ "**/*.html", "**/*.md", "**/*.css" ] fallthrough = true [assets] directory = "dist/frontend" run_worker_first = true binding = "ASSETS" [triggers] # clean r2 garbage every day crons = ["0 0 * * *"] [observability] # enable to collect logs enabled = true #-----------------------------------
- kv ↗
fer to https://developers.cloudflare.com/workers/wrangler/configuration/#routes pattern = "shz.al" custom_domain = true [[kv_namespaces]] binding = "PB" # do not touch this id = "435f8959b9de485ea48751ba557d90f5" # id of your KV namespace [[r2_buckets]] binding = "R2" # do not touch this bucket_name = "pb-shz-al" # bucket name of your R2 bucket [vars] # must be consistent with your routes DEPLOY_URL = "https://shz.al" # url to repo, displayed in the index page REPO = "https://github.com/Sh
- r2 ↗
[[kv_namespaces]] binding = "PB" # do not touch this id = "435f8959b9de485ea48751ba557d90f5" # id of your KV namespace [[r2_buckets]] binding = "R2" # do not touch this bucket_name = "pb-shz-al" # bucket name of your R2 bucket [vars] # must be consistent with your routes DEPLOY_URL = "https://shz.al" # url to repo, displayed in the index page REPO = "https://github.com/SharzyL/pastebin-worker" # the page title displayed in index page INDEX_PAGE_TITLE = "Pastebin Worker" # the name displ
- free-tier-eligible ↗
name = "pb" compatibility_date = "2025-04-24" workers_dev = false main = "worker/index.ts" [[rules]] type = "Text" globs = [ "**/*.html", "**/*.md", "**/*.css" ] fallthrough = true [assets] directory = "dist/frontend" run_worker_first = true binding = "ASSETS" [triggers] # clean r2 garbage every day crons = ["0 0 * * *"] [observability] # enable to collect logs enabled = true #-----------------------------------
- free-tier-eligible ↗
fer to https://developers.cloudflare.com/workers/wrangler/configuration/#routes pattern = "shz.al" custom_domain = true [[kv_namespaces]] binding = "PB" # do not touch this id = "435f8959b9de485ea48751ba557d90f5" # id of your KV namespace [[r2_buckets]] binding = "R2" # do not touch this bucket_name = "pb-shz-al" # bucket name of your R2 bucket [vars] # must be consistent with your routes DEPLOY_URL = "https://shz.al" # url to repo, displayed in the index page REPO = "https://github.com/Sh
- free-tier-eligible ↗
[[kv_namespaces]] binding = "PB" # do not touch this id = "435f8959b9de485ea48751ba557d90f5" # id of your KV namespace [[r2_buckets]] binding = "R2" # do not touch this bucket_name = "pb-shz-al" # bucket name of your R2 bucket [vars] # must be consistent with your routes DEPLOY_URL = "https://shz.al" # url to repo, displayed in the index page REPO = "https://github.com/SharzyL/pastebin-worker" # the page title displayed in index page INDEX_PAGE_TITLE = "Pastebin Worker" # the name displ
- free-tier-eligible ↗
up>1, 2, 3, 4</sup> | Duration | CPU time | | --- | --- | --- | --- | | **Free** | 100,000 per day | No charge for duration | 10 milliseconds of CPU time per invocation | | **Standard** | 10 million included per month <br> +$0.30 per additional million | No charge or limit for duration | 30 million CPU milliseconds included per month<br> +$0.02 per additional million CPU milliseconds<br><br> Max of [5 minutes of CPU time](https://developers.cloudflare.com/workers/platform/limits/#account-plan-limits) per invocation (default: 30 seconds)<br> Max of 15 minutes of CPU time per [Cron Trigger](https://developers.cloudflare.com/workers/configuration/cron-triggers/) or [Queue Consumer](https://developers.cloudflare.co
- free-tier-eligible ↗
cing/). | | Free plan<sup>1</sup> | Paid plan | | --- | --- | --- | | Keys read | 100,000 / day | 10 million/month, + $0.50/million | | Keys written | 1,000 / day | 1 million/month, + $5.00/million | | Keys deleted | 1,000 / day | 1 million/month, + $5.00/million | | List requests | 1,000 / day | 1 million/month, + $5.00/million | | Stored data | 1 GB | 1 GB, + $0.50/ GB-month | <sup>1</sup> The Workers Free plan includes limited Workers KV usage. All limits reset daily at 00:00 UTC. If you exceed any one of these limits, further operations of that type will fail with an error. Note Workers KV pricing for read, write and delete operations is on a per-key basis. Bulk read operations are billed by the amount
- free-tier-eligible ↗
infrequent access storage) for 1.1 GB, you will be billed for 2 GB. ### Free tier You can use the following amount of storage and operations each month for free. | | Free | | --- | --- | | Storage | 10 GB-month / month | | Class A Operations | 1 million requests / month | | Class B Operations | 10 million requests / month | | Egress (data transfer to Internet) | Free <sup>[1](#user-content-fn-1)</sup> | Caution The free tier only applies to Standard storage, and does not apply to Infrequent Access storage. ### Storage usage Storage is billed using gigabyte-month (GB-month) as the billing metric. A GB-month is calculated by averaging the *peak* storage per day over a billing period (30 days). For examp
- MIT ↗
Copyright (c) 2021 Sharzy L <me@sharzy.in> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRIN
- architecture ↗
name = "pb" compatibility_date = "2025-04-24" workers_dev = false main = "worker/index.ts" [[rules]] type = "Text" globs = [ "**/*.html", "**/*.md", "**/*.css" ] fallthrough = true [assets] directory = "dist/frontend" run_worker_first = true binding = "ASSETS" [triggers] # clean r2 garbage every day crons = ["0 0 * * *"] [observability] # enable to collect logs enabled = true #-----------------------------------
- architecture ↗
fer to https://developers.cloudflare.com/workers/wrangler/configuration/#routes pattern = "shz.al" custom_domain = true [[kv_namespaces]] binding = "PB" # do not touch this id = "435f8959b9de485ea48751ba557d90f5" # id of your KV namespace [[r2_buckets]] binding = "R2" # do not touch this bucket_name = "pb-shz-al" # bucket name of your R2 bucket [vars] # must be consistent with your routes DEPLOY_URL = "https://shz.al" # url to repo, displayed in the index page REPO = "https://github.com/Sh
- architecture ↗
[[kv_namespaces]] binding = "PB" # do not touch this id = "435f8959b9de485ea48751ba557d90f5" # id of your KV namespace [[r2_buckets]] binding = "R2" # do not touch this bucket_name = "pb-shz-al" # bucket name of your R2 bucket [vars] # must be consistent with your routes DEPLOY_URL = "https://shz.al" # url to repo, displayed in the index page REPO = "https://github.com/SharzyL/pastebin-worker" # the page title displayed in index page INDEX_PAGE_TITLE = "Pastebin Worker" # the name displ
Documented public demo screenshot · SharzyL/pastebin-worker repository contributors; screenshot captured by Cloudsteading ↗. Depicts the upstream project. We have not deployed and tested a fresh installation here.
What it can replace
Compare the workflow you need. These mappings describe overlap; full feature parity requires a separate comparison.
Publishing text pastes and sharing uploaded files by download URL; filesystem sync, email transfer delivery and managed large-transfer guarantees are excluded.
See supporting source ↗Publishing text pastes and sharing uploaded files by download URL; filesystem sync, email transfer delivery and managed large-transfer guarantees are excluded.
See supporting source ↗How it works
The shape of Pastebin Worker on Cloudflare, and how it stacks up against the rented tools it replaces.
Architecture
Diagram of deployment declarations at the reviewed commit. Each app has its own entrypoint; declared resources do not prove runtime calls. Follow file and line sources below.
View upstream source ↗Configuration and workflow sources
Reviewed commit 0835cac4ab8f. Files were read as data; upstream applications and CI jobs were not executed.
Deployment configuration · 1 files
Cloudflare Workers · compatibility 2025-04-24
pb · default
Entrypoint: worker/index.ts
Static assets: dist/frontend · Worker first: true
Cron triggers (UTC): 0 0 * * *
Configured route patterns: shz.al
PB→ KVR2→ R2ASSETS→ Static assets
Named environments are separate deployments. Bindings are shown only where declared. Configured routes are URL patterns, not verified application endpoints.
Runtime source · handlers, binding usage and workflow steps
Observed TypeScript/JavaScript declarations from Worker entrypoints and resolved relative imports. Calls and workflow steps may run conditionally; their listed order is not a proven end-to-end request flow. Router declarations may be mounted under a prefix or may not be registered. This shows code wiring, not a successful deployment or runtime test. Dynamic wiring, aliases and generated code may not resolve.
- L11 · fetch handler exported · calls handleRequest
- L16 · scheduled handler exported · calls ctx.waitUntil, cleanExpiredInR2
- L21 · handleRequest calls (conditional paths may differ): handleOptions, handleNormalRequest, response.headers.set, corsWrapResponse, console.error
- L50 · handleNormalRequest calls (conditional paths may differ): handlePostOrPut, handleGet, handleDelete
- L3 · decode calls (conditional paths may differ): decode
- L7 · btoa_utf8 calls (conditional paths may differ): btoa, String.fromCharCode, encode
- L11 · atob_utf8 calls (conditional paths may differ): atob, decode, Uint8Array.from, value_latin1.charCodeAt
- L18 · escapeHtml calls (conditional paths may differ): str.replace
- L36 · dateToUnix calls (conditional paths may differ): Math.floor, date.getTime
- L40 · genRandStr calls (conditional paths may differ): CHAR_GEN.charAt, Math.floor, Math.random
- L60 · timingSafeEqual calls (conditional paths may differ): encoder.encode, crypto.subtle.timingSafeEqual
- L9 · parseSize calls (conditional paths may differ): sizeStr.trim, SIZE_REGEX.test, parseFloat
- L24 · parseExpiration calls (conditional paths may differ): expirationStr.trim, EXPIRE_REGEX.test, parseFloat
- L39 · parseExpirationReadable calls (conditional paths may differ): expirationStr.trim, EXPIRE_REGEX.test, parseFloat
- L62 · parsePath calls (conditional paths may differ): pathname.slice, pathname.lastIndexOf, decodeURIComponent, filename.indexOf, filename.slice, pathname.indexOf
- L114 · parseFilenameFromContentDisposition calls (conditional paths may differ): filenameStarRegex.exec, decodeURIComponent, filenameRegex.exec
- L30 · multipartToMap calls (conditional paths may differ): parseMultipartRequest, parseSize, partsMap.set, decode, console.warn
- L67 · handlePostOrPut calls (conditional paths may differ): verifyAuth, handleMPUCreate, handleMPUCreateUpdate, handleMPUResume, handleMPUAbort, url.pathname.startsWith, request.headers.get, contentType.includes, multipartToMap, parts.has, parts.get, JSON.parse, contentAsString, parseExpiration, verifyPassword, verifyName, JSON.stringify, parsePath, url.searchParams.get, handleMPUComplete
- L146 · makeResponse calls (conditional paths may differ): JSON.stringify
Environment references: env.R2_MAX_ALLOWED · env.DEFAULT_EXPIRATION · env.MAX_EXPIRATION · env.DEPLOY_URL
- L16 · decodeMaybeStream calls (conditional paths may differ): decode, getReader, content.pipeThrough, reader.read
- L43 · lastModifiedHeader calls (conditional paths may differ): toUTCString
- L48 · isCurlAgent calls (conditional paths may differ): request.headers.get, startsWith, ua.toLowerCase
- L53 · handleStaticPages calls (conditional paths may differ): isCurlAgent, verifyAuth, getCurlIndexMarkdown, staticPageCacheHeader, path.endsWith, path.lastIndexOf, path.indexOf, renderIndexPage, console.error, getAssetPaths, escapeHtml, renderCssLinks, JSON.stringify, path.startsWith, env.ASSETS.fetch, mime.getType, resp.blob, url.pathname.startsWith, url.pathname.endsWith, url.pathname.slice
- L175 · getPasteWithoutContent calls (conditional paths may differ): getPasteMetadata
- L180 · handleGet calls (conditional paths may differ): handleStaticPages, parsePath, url.searchParams.has, getPaste, getPasteWithoutContent, disallowedMimes.includes, url.searchParams.get, mime.getType, sanitize, request.headers.get, Date.parse, lastModifiedHeader, decodeMaybeStream, isLegalUrl, Response.redirect, makeMarkdown, pasteCacheHeader, metaResponseFromMetadata, JSON.stringify, renderDisplayPage
Environment references: env.CACHE_STATIC_PAGE_AGE · env.CACHE_PASTE_AGE · env.INDEX_PAGE_TITLE · env.ASSETS · env.DISALLOWED_MIME_FOR_PASTE
- L5 · handleDelete calls (conditional paths may differ): parsePath, getPasteMetadata, timingSafeEqual, deletePaste
- L42 · metaResponseFromMetadata calls (conditional paths may differ): toISOString
- L79 · updateAccessCounter calls (conditional paths may differ): Math.random, env.PB.put, message.includes
- L97 · getPaste calls (conditional paths may differ): env.PB.getWithMetadata, workerAssert, migratePasteMetadata, getTime, ctx.waitUntil, deletePaste, updateAccessCounter, env.R2.get
- L136 · getPasteMetadata calls (conditional paths may differ): env.PB.getWithMetadata, getTime, migratePasteMetadata
- L164 · updatePaste calls (conditional paths may differ): dateToUnix, Math.max, parseSize, env.R2.put, String, env.PB.put
- L210 · createPaste calls (conditional paths may differ): dateToUnix, Math.max, parseSize, env.R2.put, String, env.PB.put
- L251 · pasteNameAvailable calls (conditional paths may differ): env.PB.getWithMetadata, getTime
- L262 · deletePaste calls (conditional paths may differ): env.R2.delete, env.PB.delete
- L269 · cleanExpiredInR2 calls (conditional paths may differ): env.R2.list, Number, toDelete.push, needKvLookup.push, Promise.all, needKvLookup.map, getPasteMetadata, toDelete.slice, env.R2.delete, console.log
Environment references: env.PB · env.R2 · env.R2_THRESHOLD
- L5 · encodeBasicAuth calls (conditional paths may differ): btoa_utf8
- L11 · decodeBasicAuth calls (conditional paths may differ): encodedString.split, atob_utf8, credentials.split
- L28 · verifyAuth calls (conditional paths may differ): Object.entries, request.headers.has, decodeBasicAuth, request.headers.get, passwdMap.has, compareSync, passwdMap.get
Environment references: env.BASIC_AUTH
- L6 · isLegalUrl calls (conditional paths may differ): URL.canParse
- L10 · verifyPassword calls (conditional paths may differ): password.includes
- L23 · verifyName calls (conditional paths may differ): NAME_REGEX.test
- L32 · verifyExpiration calls (conditional paths may differ): parseExpiration, parseExpirationReadable
- L7 · mpuExpireMetadata calls (conditional paths may differ): url.searchParams.get, parseExpiration, Math.min, dateToUnix, String
- L18 · handleMPUCreate calls (conditional paths may differ): url.searchParams.get, NAME_REGEX.test, pasteNameAvailable, genRandStr, env.R2.createMultipartUpload, mpuExpireMetadata, JSON.stringify
- L49 · handleMPUCreateUpdate calls (conditional paths may differ): url.searchParams.get, getPasteMetadata, timingSafeEqual, env.R2.createMultipartUpload, mpuExpireMetadata, JSON.stringify
- L78 · handleMPUResume calls (conditional paths may differ): url.searchParams.get, parseInt, env.R2.resumeMultipartUpload, multipartUpload.uploadPart, console.warn, String, JSON.stringify
- L111 · handleMPUAbort calls (conditional paths may differ): url.searchParams.get, abort, env.R2.resumeMultipartUpload, console.warn, String
- L130 · handleMPUComplete calls (conditional paths may differ): url.searchParams.get, env.R2.resumeMultipartUpload, multipartUpload.complete, console.warn, String, parseSize, env.R2.delete
Environment references: env.MAX_EXPIRATION · env.R2 · env.R2_MAX_ALLOWED
- L9 · renderTemplate calls (conditional paths may differ): replaceAll, template.replaceAll
- L20 · getDocMarkdown calls (conditional paths may differ): renderTemplate
- L34 · getCurlIndexMarkdown calls (conditional paths may differ): renderTemplate
- L38 · renderDocAsHtml calls (conditional paths may differ): makeMarkdown, md.replace
Environment references: env.DEPLOY_URL · env.REPO · env.TOS_MAINTAINER · env.TOS_MAIL · env.DEFAULT_EXPIRATION · env.MAX_EXPIRATION · env.R2_MAX_ALLOWED
- L95 · forceNoopener calls (conditional paths may differ): html.replace, test
- L102 · sanitizeHtml calls (conditional paths may differ): forceNoopener, filterXSS
- L126 · tokenText calls (conditional paths may differ): join, list.items.map, Array.isArray, token.tokens.map
- L138 · firstContentToken calls (conditional paths may differ): tokens.find
- L142 · extractMetadata calls (conditional paths may differ): firstContentToken, escapeHtml, tokenText, tokens.slice, tokens.indexOf, slice
- L155 · renderToc calls (conditional paths may differ): toc.filter, openDepths.pop, openDepths.push, escapeHtml
- L305 · makeMarkdown calls (conditional paths may differ): marked.use, parser.parseInline, join, tokens.map, slugger.slug, metadata.toc.push, escapeHtml, text.replace, body.match, repeat, exec, marked.lexer, extractMetadata, sanitizeHtml, marked.parser, renderToc, getAssetPaths, renderCssLinks
Build and deployment pipeline · 2 GitHub Actions workflows
Repository CI declarations, separate from runtime request processing. Job dependencies and conditions are shown as written; long commands are shortened with an ellipsis; a workflow file does not prove a recent successful run.
Triggers: push
deploy · no job dependencies declared
- actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 - Install Node
actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 - Install pnpm
pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 - Setup
pnpm install --frozen-lockfile - Build Frontend
pnpm build:frontend - Test
pnpm fmt pnpm lint pnpm typecheck pnpm test --testTimeout 15000 - Deploy
pnpm wrangler deploy
Triggers: pull_request
coverage-goshujin · no job dependencies declared
- actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 - Install Node
actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 - Install pnpm
pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 - Run Test with Coverage
pnpm install --frozen-lockfile pnpm build:frontend pnpm coverage --testTimeout 15000 - Upload Coverage
actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
test · no job dependencies declared
- actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 - Install Node
actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 - Install pnpm
pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 - Install Deps
pnpm install --frozen-lockfile - Build Frontend
pnpm build:frontend - Lint
pnpm prettier -c . pnpm lint - Type Check
pnpm typecheck - Run Test with Coverage
pnpm coverage --testTimeout 15000 - Upload Coverage
actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
report-coverage · after test
- actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 - Download HEAD coverage artifacts
actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c - Download goshujin coverage artifacts
actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c - Report Coverage
davelosert/vitest-coverage-report-action@8b157684c6a6b259b97d45e72b44242865c0f6a5
deploy: wrangler deploybuild:frontend: vite build frontend --outDir ../dist/frontend --emptyOutDirbuild:frontend:dev: vite build frontend --mode development --outDir ../dist/frontend --emptyOutDirbuild: wrangler deploy --dry-run --outdir=dist
Repository README
View original on GitHub ↗Full upstream document by @SharzyL · README.md · snapshot 0835cac
Pastebin Worker
This is a pastebin running on Cloudflare workers. Try it on shz.al.
Philosophy: effortless deployment, friendly CLI usage, rich functionality.
Features:
- Share your paste with as short as 4 characters, or even customized URL.
- Syntax highlighting powered by highlight.js.
- Client-side encryption.
- Share markdown file with rendered HTML.
- URL shortener.
- Smart and tweakable handling for
Content-TypeandContent-Disposition.
Usage
You can post, update, delete your paste directly on the website (such as shz.al).
It also provides a convenient HTTP API to use. See API reference for details. You can easily call API via command line (using
curlor similar tools). Note that a single request body is capped at 100 MB by Cloudflare (the platform returns HTTP413for larger bodies before the worker runs) — for larger files, use the website or thepbCLI below, which transparently chunk the upload.pb is a Python script (requires Python 3.9+ with the
requestspackage) to make it easier to use on command line; it automatically switches to multipart upload above 5 MiB and shows a progress bar.doc/skill.md is a concise, AI-agent-oriented packaging of the API. Make it available to your coding agent so it can upload, fetch, and manage pastes via this service.
Deploy
You are free to deploy the pastebin on your own domain if you host your domain on Cloudflare.
Install
nodeandpnpm.Clone the repository and enter the directory.
Create a KV namespace and R2 bucket, fill the KV namespace ID and R2 bucket name in
wrangler.toml.
$ pnpm wrangler kv namespace create PB
$ pnpm wrangler r2 bucket create <name>
Modify entries in
wrangler.toml. Its comments will tell you how.Login to Cloudflare and deploy with the following steps:
$ pnpm install
$ pnpm wrangler login
$ pnpm build:frontend
$ pnpm deploy
- Enjoy!
Cost
The service runs on Cloudflare Workers, Workers KV, and R2. Each has a free tier; beyond it you pay only for what you use. Figures below are accurate as of writing — prices change, so confirm against the official pricing pages before relying on them:
- Workers — request routing and execution. Egress is free.
- Free plan: 100 k requests/day, 10 ms CPU per invocation.
- Paid plan ($5/mo base): 10 M requests/month + 30 M ms CPU/month included, then $0.30 per additional M requests and $0.02 per additional M CPU-ms. Also unlocks the higher KV limits below (KV has no separate paid plan).
- Workers KV — small pastes and per-paste metadata.
- Free plan (daily, resets 00:00 UTC): 100 k reads, 1 k writes, 1 k deletes, 1 k list ops, 1 GB storage.
- Paid plan (monthly + overage): 10 M reads ($0.50/M extra), 1 M writes ($5/M), 1 M deletes ($5/M), 1 M list ops ($5/M), 1 GB storage ($0.50/GB-month extra).
- R2 — paste content above
R2_THRESHOLD. Egress is free. Class A op = upload (PutObject); Class B op = fetch (GetObject). Cloudflare rounds storage up to the next GB-month.- Free: 10 GB-month storage, 1 M Class A ops/month, 10 M Class B ops/month.
- Standard paid: $0.015/GB-month storage, $4.50/M Class A ops, $0.36/M Class B ops.
- Workers Logs — optional, off unless enabled in
wrangler.toml.- Free: 200 k events/day, 3-day retention.
- Paid: 20 M events/month included + $0.60 per additional million, 7-day retention.
Costs scale primarily with: large file traffic (R2 ops + storage), high-volume reads (Workers requests + KV reads), and verbose logging (Workers Logs events).
Bottom line — what each tier comfortably handles:
- Free tier — a personal pastebin. Binding limits are KV writes (1 k uploads/day) and KV/Workers reads (~100 k fetches/day), with 1 GB small-paste storage and 10 GB large-paste storage on R2. Plenty for individual or small-team use.
- $5/month Paid — a small public or community service. Roughly ~33 k uploads/day and ~333 k fetches/day stay within the included monthly KV allotment; Workers requests included to ~10 M/month (~333 k/day). R2 storage and ops come out of R2's own free tier first, then a few cents per GB-month and per million ops — adding only a few dollars even at moderate traffic.
[!NOTE] Small pastes go to KV (not R2) to keep garbage collection cheap. KV honors per-key expiration natively, so expired pastes vanish on their own. R2 has no built-in expiration, so cleaning up expired objects would require periodically listing and scanning every object in the bucket — costly in Class A/B ops as the bucket grows.
Auth
If you want a private deployment (only you can upload paste, but everyone can read the paste), add the following entry to your wrangler.toml.
[vars.BASIC_AUTH]
user1 = "$2b$08$i/yH1TSIGWUNQVsxPrcVUeR0hsGioFNf3.OeHdYzxwjzLH/hzoY.i"
user2 = "$2b$08$KeVnmXoMuRjNHKQjDHppEeXAf5lTLv9HMJCTlKW5uvRcEG5LOdBpO"
Passwords here are hashed by bcrypt2 algorithm. You can generate the hashed password by running ./scripts/bcrypt.js.
Now every access to POST request, and every access to static pages, requires an HTTP basic auth with the user-password pair listed above. For example:
$ curl example-pb.com
HTTP basic auth is required
$ curl -Fc=@/path/to/file example-pb.com
HTTP basic auth is required
$ curl -u admin1:wrong-passwd -Fc=@/path/to/file example-pb.com
Error 401: incorrect passwd for basic auth
$ curl -u admin1:this-is-passwd-1 -Fc=@/path/to/file example-pb.com
{
"url": "https://example-pb.com/YCDX",
"admin": "https://example-pb.com/YCDX:Sij23HwbMjeZwKznY3K5trG8",
"isPrivate": false
}
Administration
Delete a paste:
$ pnpm delete-paste <name-of-paste>
List pastes:
$ pnpm -s wrangler kv key list --binding PB > kv_list.json
Development
Note that the frontend and worker code are built separatedly. To start a Vite development server of the frontend,
$ pnpm dev:frontend
To develop the backend worker, we must build a develop version of frontend,
$ pnpm build:frontend:dev
Then starts a local worker,
$ pnpm dev
The difference between build:frontend:dev and build:frontend is that the former will points the API endpoint to your deployment URL, while the later points to http://localhost:8787, the address of a local worker.
Run tests:
$ pnpm test
Run tests with coverage report:
$ pnpm coverage
Remember to run eslint checks and prettier before commiting your code.
$ pnpm fmt
$ pnpm lint
$ pnpm typecheck
Frequently asked about Pastebin Worker
What is Pastebin Worker?+
Pastebin Worker is a self-hosted Pastebin/WeTransfer alternative built on the Cloudflare developer platform. Share text pastes and downloadable files through Workers, KV and R2.
What does Pastebin Worker replace?+
Pastebin Worker is listed as an alternative to Pastebin, WeTransfer. Compare the features and tradeoffs before migrating.
What Cloudflare primitives does Pastebin Worker use?+
Pastebin Worker is built on KV, R2, Workers.
How much does Pastebin Worker cost to run?+
The documented Pastebin Worker deployment can use Cloudflare Free allowances for a small workload under the request, CPU and service-specific quotas below. This is conditional eligibility, not a measured zero-cost deployment; optional features, domains and external providers can add costs. Workers Free allows 100,000 requests per day shared across the account and 10 ms CPU per invocation; measure CPU-heavy authentication, parsing and rendering before assuming it fits. Keep KV below 100,000 reads/day, 1,000 writes, deletes and list operations/day each, and 1 GB; cache refreshes and backups consume writes. Use R2 Standard storage, at most 10 GB-month, 1 million Class A operations and 10 million Class B operations/month; provision an eligible billing-enabled R2 account. Use a small personal or team workload; domain registration and optional third-party providers are separate costs. Provision your own IDs, secrets and migrations. Check current Cloudflare pricing before deploying.
Is Pastebin Worker open source?+
The upstream repository declares the MIT license. Read its terms at https://raw.githubusercontent.com/SharzyL/pastebin-worker/0835cac4ab8f974035d31845f5c2b93b0c85b5c6/LICENSE. Source code and contributor credit are available at https://github.com/SharzyL/pastebin-worker.



Discussion · 0
sign in to comment →