{"id":25221616,"url":"https://github.com/monadical-sas/zervice.backblaze","last_synced_at":"2025-07-23T21:38:21.576Z","repository":{"id":51583464,"uuid":"246531740","full_name":"Monadical-SAS/zervice.backblaze","owner":"Monadical-SAS","description":"Upload files to B2 from the browser, and access them via free CloudFlare egress.","archived":false,"fork":false,"pushed_at":"2022-12-12T04:49:15.000Z","size":31,"stargazers_count":6,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-01T18:52:38.649Z","etag":null,"topics":["b2-upload","backblaze","cloudflare","egress","frontend-uploads"],"latest_commit_sha":null,"homepage":"","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Monadical-SAS.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-03-11T09:43:24.000Z","updated_at":"2024-10-18T05:29:07.000Z","dependencies_parsed_at":"2023-01-27T13:46:10.735Z","dependency_job_id":null,"html_url":"https://github.com/Monadical-SAS/zervice.backblaze","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Monadical-SAS/zervice.backblaze","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Monadical-SAS%2Fzervice.backblaze","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Monadical-SAS%2Fzervice.backblaze/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Monadical-SAS%2Fzervice.backblaze/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Monadical-SAS%2Fzervice.backblaze/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Monadical-SAS","download_url":"https://codeload.github.com/Monadical-SAS/zervice.backblaze/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Monadical-SAS%2Fzervice.backblaze/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266753962,"owners_count":23979144,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-23T02:00:09.312Z","response_time":66,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["b2-upload","backblaze","cloudflare","egress","frontend-uploads"],"created_at":"2025-02-10T22:52:54.312Z","updated_at":"2025-07-23T21:38:21.548Z","avatar_url":"https://github.com/Monadical-SAS.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Backblaze B2 Upload from the browser\n\n## Quickstart\n\n```\ngit clone https://github.com/Monadical-SAS/zervice.backblaze.git\ncd zervice.backblaze/\npipenv install\nyarn install\nb2 authorize-account  # use master key for this, not app key\n# edit cors_rules.json contents\nb2 update-bucket bucket-name --corsRules \"$(cat cors_rules.json)\"\nyarn run start\nopen http://localhost:8085\n```\n\n## Manual Setup\n\n1. Set up a BackBlaze account and [create a new B2 bucket](https://secure.backblaze.com/b2_buckets.html) (public)\n\u003ccenter\u003e\u003cimg src=\"https://docs.monadical.com/uploads/upload_1622299a4f50fda59c1a47b1339fa4ac.png\" style=\"width: 200px;\"\u003e\u003cimg src=\"https://docs.monadical.com/uploads/upload_bd0cdef7d94065930436be7e60d256aa.png\" style=\"width: 80px;\"\u003e\u003cimg src=\"https://docs.monadical.com/uploads/upload_ce8dd7c04edf3b9968a170cf0bdd4f49.png\" style=\"width: 180px;\"\u003e\n\u003c/center\u003e\n\n2. Create a new Backblaze [Application Key](https://secure.backblaze.com/app_keys.htm) to authenticate your backend\n\n\u003ccenter\u003e\u003cimg src=\"https://docs.monadical.com/uploads/upload_e7fcb89647fa04c83937558f165021c6.png\" style=\"width: 200px;\"\u003e\u003cimg src=\"https://docs.monadical.com/uploads/upload_13e498f545c73b43f6bb9a9611053ebe.png\" style=\"width: 100px;\"\u003e\u003cimg src=\"https://docs.monadical.com/uploads/upload_ccbaa673584e97669060f3f14bd92714.png\" style=\"width: 180px;\"\u003e\n\u003c/center\u003e\n\u003cbr/\u003e\n\n3. Update the bucket's CORS rules to allow requests from your website\n\n```bash\n\nb2 authorize-account  # use master key for this, not app key\nb2 update-bucket bucket-name --corsRules \"$(cat cors_rules.json)\"\n```\n\n4. Start the webserver\n\n```bash\nyarn install\nyarn run start\nopen http://localhost:8085\n```\n\n## Execution Flow\n\n\n1. user visits `index.html`, selects a file, and `onSelectFile` is triggered\n2. frontend requests a one-time B2 upload url via backend \nfrontend -\u003e\n`localhost:8085/b2/getUploadUrl` -\u003e \nserver -\u003e\n`backblaze.com/api/getUploadUrl`\n5. frontend calculates the file's sha1hash\n6. frontend prepares upload headers for B2 for:\n   - filename\n   - content type\n   - sha1 hash verification\n   - cache control behavior\n   - optional extra metadata\n7. frontend uploads file from browser to server-provided B2 uploadUrl with the prepared headers\n\n\n\n\n## CloudFlare Egress Setup (Optional)\n\nEgress from BackBlaze is free if it goes through CloudFlare's network.  To take advantage of this, you just need to create a domain that's a proxied CNAME to your B2 bucket's friendly URL.\n\n1. Create a CNAME record to point to your BackBlaze b2 bucket host\n    `b2.example.com     CNAME   -\u003e f000.backblazeb2.com (Proxied)`\n2. Create a Page Rule on that domain to enable caching\n    `b2.example.com/file/bukcet-name/*`: `Always cache`\n    \n```bash\n# or do the same thing via the API\ncurl -X PUT \"https://api.cloudflare.com/client/v4/zones/023e105f4ecef8ad9ca31a8372d0c353/dns_records/372e67954025e0ba6aaa6d586b9e0b59\" \\\n     -H \"X-Auth-Email: user@example.com\" \\\n     -H \"X-Auth-Key: c2547eb745079dac9320b638f5e225cf483cc5cfdda41\" \\\n     -H \"Content-Type: application/json\" \\\n     --data '{\"type\":\"CNAME\",\"name\":\"b2.example.com\",\"content\":\"f002.backblazeb2.com\",\"ttl\":{},\"proxied\":true}'\n```\n\n## Notes\n\n### Part of the upload process must take place on a server\n\nB2 does not allow`get_upload_url` and `delete_file_version` to be called directly from browsers due to b2's CORS restrictions.\n\nYou must ask a node backend to make the request for you.\n\n### CORS headers must be allowed with corsRules\n\nThe headers used for in-browser uploads **must* be explicitly marked allowed in your b2 bucket's `corsRules`. If not set properly, uploads will return CORS and 400 errors. The rules can be set up using the b2 CLI.\n\n`b2 update-bucket --corsRules rules.json bucket-name`\n\n### Hashes must match between browser and B2\n\nBrowser's hash needs to match backblaze's hash result exactly or the b2 upload will fail with a 400 error.\n\nFootgun: use ``'do_not_verify'`` as the hash to disable hash verification.\n\n### Content-Type should be set explicitly\n\nAlways explicity pick the expected content type (e.g. 'audio/mpeg', 'application/octet-stream', etc.) to prevent accidentally reflecting user-uploads as public runnable JS/HTML/viruses/etc types to others.\n\nFootgun: use ``'b2/x-auto'`` as the ctype to have b2 autodetect it.\n\n### Filenames in B2 should not be user-defined\n\nYour B2 filenames will be served via public URLs on your domain.\nBeware of using user-provided filenames! Do you really want someont to upload `yourfiles.com/passwod_reset.html`?\n\nTip: you can use the hash as the name instead\n\n### The `Content-Length` header does not need to be set\n\nThe Content-Length header will be automatically set to the correct length by the browser making the upload request.\n\n### Axios dependency\n\nWhile uploading can be done without `axios` to make the requests from the browser -\u003e B2, it's more difficult to get the CORS headers right with `fetch()` or `XMLHttpRequst`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmonadical-sas%2Fzervice.backblaze","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmonadical-sas%2Fzervice.backblaze","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmonadical-sas%2Fzervice.backblaze/lists"}