606 words
3 minutes
PicImpact Upload Failures with Cloudflare R2? Here’s the Fix.

PicImpact is a self-hosted photography showcase platform built on Next.js and Hono.js. Its interface is elegant and functional—perfect for those who love customizing their tech stack but don’t want to build a frontend from scratch. 🙋‍♂️

📌 Project Resources#

🪄 Why Choose PicImpact?#

I was searching for a sleek image hosting template for my photography site, Moments. PicImpact stood out for its clean UI and Docker support—a perfect “one-and-done” solution.

🚫 Why not local deployment?
Don’t ask. I just didn’t want to deal with the overhead of a local Node.js environment… 💀

🐳 Docker Deployment Experience#

The deployment is incredibly smooth. As long as your PostgreSQL instance is ready (either local or a managed cloud DB), you just run the following command, and you’re airborne:

Terminal window
docker run -d --name picimpact \
-p 3000:3000 \
-e DATABASE_URL="postgresql://[USER]:[PASSWORD]@[HOST]:[PORT]/[DB_NAME]" \
-e BETTER_AUTH_SECRET="npx auth secret or a random string" \
besscroft/picimpact:latest

🧨 The Obstacle…#

Initially, I used Alist as the storage backend, but I was bottlenecked by my home broadband’s limited upload speed 🥲. So, I pivoted to Cloudflare’s R2—the generous “Saint” of free object storage.

However, after configuration, the upload process went haywire:

  • The UI popup showed a “Success” checkmark ☑️, but the image status below displayed Upload.failed

  • The Browser Console was a gallery of errors:

    1. ERR_NAME_NOT_RESOLVED
    2. ERR_SSL_VERSION_OR_CIPHER_MISMATCH
    3. ERR_FAILED
    4. has been blocked by CORS policy: … No ‘Access-Control-Allow-Origin’ header

Is it successful? The R2 dashboard was empty. Is it a failure? The frontend insisted it was “Success.” I felt like I had triggered every possible error in the PicImpact library 🤦‍♂️. Eventually, I narrowed it down to a CORS configuration issue. Upload Error

🛠️ Setting Up Cloudflare R2#

Cloudflare R2 offers 10GB of free monthly storage with no egress fees. Note that CF nodes are primarily overseas, so latency may vary. For higher performance in China, consider Tencent COS or Alibaba OSS.

  1. Access R2 Storage R2 Object Storage

  2. Create a Bucket. Create Bucket

  3. Bucket Parameters

You can choose any location, as latency is similar. However, ensure you select the Standard storage class to stay within the free tier.😯

Bucket Config

  1. Custom Domain

You must use a domain whose Nameservers (NS) point to Cloudflare.

Custom Domain

  1. CRITICAL: Configure CORS Policy

This is the dealbreaker. If you miss this, you’ll end up wasting hours like I did.~

CORS

CORS Policy Sample: Note that AllowedHeaders is mandatory; R2’s default generator often omits it.

Terminal window
[
{
"AllowedOrigins": [
"http://localhost:3000"
],
"AllowedMethods": [
"GET",
"HEAD",
"PUT",
"POST",
"DELETE"
],
"AllowedHeaders": [
"*"
]
}
]
  1. Create API Tokens Create API

As a security professional, I strongly advocate for the Principle of Least Privilege.

Create User token

Avoid creating “Account API Tokens” if possible; a specific User API Token scoped only to the required bucket is much safer.

API

  1. Save API Credentials

These credentials (Access Key ID, Secret Access Key, and Account ID) appear only once. You will need the S3 API info for PicImpact.

API Info

🔗 Integrating PicImpact with CF-R2#

  1. Navigate to the PicImpact dashboard: Storage Config -> Cloudflare R2 -> Edit. Cloudfalre R2

  2. Fill in the Details:

  • r2_accesskey_id: Your Access Key ID.
  • r2_accesskey_secret: Your Secret Access Key.
  • r2_account_id: The prefix of your S3 endpoint (e.g., if the endpoint is https://123456.r2.cloudflarestorage.com, use 123456).
  • r2_bucket: Your bucket name.
  • r2_storage_folder: Path prefix. If using the root, leave blank. Do not start with a slash (/).
  • r2_public_domain: Your custom domain from step 4. Must include the protocol (e.g., https://abc.com).
  • r2_direct_download: Usually set to false.

R2

  1. Success: You’re now ready to showcase your work!

Moments

✅ Final Thoughts#

PicImpact is a fantastic project with an elegant UI. While the documentation doesn’t provide a “step-by-step” for every storage provider, that’s the beauty of open-source—the joy of tinkering.

The “False Success” caused by CORS is particularly misleading. If you’re struggling with PicImpact + CF-R2, remember:

  • Check the Console errors first.
  • Verify your CORS headers (especially AllowedHeaders).
  • Confirm that files are actually landing in the bucket dashboard.

Hopefully, this guide saves you some hair-pulling! 🤯

PicImpact Upload Failures with Cloudflare R2? Here’s the Fix.
https://fuwari.vercel.app/posts/02139ee4-d1ee-496a-94a2-e1f68c1a2982/
Author
Ryan Zhang
Published at
2025-07-02
License
CC BY-NC-SA 4.0
This content has been translated with the assistance of AI tools, including ChatGPT, Gemini, and Qwen. While efforts have been made to ensure accuracy and clarity, minor discrepancies may exist. Please refer to the original text for authoritative interpretation if needed.