Zhanbo Chen
Back to home

Typora + PicGo + Cloudflare R2 Image Hosting Upload Success but Images Broken: A Record of URL Concatenation Pitfalls

Pitfall Notes
2026-07-19
4 min read
0 reads
TyporaPicGoCloudflare R2图床Markdown踩坑记录

In brief

Uploading images to Cloudflare via PicGo in Typora encountered obstacles. This article successfully uploads images and outputs them correctly locally by properly concatenating URLs.

This afternoon while organizing my blog in Typora, I suddenly discovered that all images uploaded to Cloudflare R2 through PicGo had turned into broken images. Accessing the image links directly in the browser returned 403, but PicGo's upload logs showed success. After some troubleshooting, I finally found the culprit in PicGo's outputURLPattern configuration—it wasn't that the image hosting service was down, but that the URL wasn't constructed correctly at all.

Symptom: Upload Successful, but Typora Won't Render

Here's what happened:

  • I had Cloudflare R2 configured in PicGo (via S3-compatible API).
  • Clicked upload, PicGo showed success and returned a link.
  • Pasted the link into Typora, but it only displayed a blue line of text or showed broken images.

I instinctively thought the image hosting service was down, because the returned domain looked like this:

https://xxxassets.hashcode.r2.cloudflarestorage.com/2026/07/xxx.png

But here's the strange part: when I used the public access domain that Cloudflare bound to R2 (https://pub-hashcode.r2.dev) and concatenated the same path, the browser could open it normally.

In other words: The image hosting service wasn't down; PicGo was generating a malformed URL.

Troubleshooting: Which Layer Is Failing

I did step-by-step isolation testing:

Verification ItemResult
Browser directly accessing R2's S3 API domain403, because R2's r2.cloudflarestorage.com is a private endpoint and doesn't allow public access by default
Browser accessing public domain r2.dev + same path200, image displays normally
Changed link in Typora to r2.dev domainStill won't render
Checked PicGo's custom domain configurationFound that only https://pub-xxx.r2.dev was filled in; after upload, the returned URL only had the domain with no file path afterward

At this point, the problem split into two issues:

  1. Wrong domain: Should use the r2.dev public domain, not the cloudflarestorage.com private endpoint.
  2. Incomplete URL concatenation: PicGo only returned the domain root, missing the /2026/07/xxx.png portion.

Root Cause: Misunderstood the Placeholder in outputURLPattern

PicGo's S3 plugin documentation clearly states: the old urlPrefix and urlSuffix are deprecated; use outputURLPattern instead.

My initial outputURLPattern configuration (incorrect) was:

https://pub-hashcode.r2.dev/{md5}.{extName}

And my uploadPath configuration was:

{year}/{month}/{md5}.{extName}

After upload, PicGo returned:

https://pub-hashcode.r2.dev/2026/07/{md5}.png

The {md5} was output as-is without being replaced with the actual hash value.

The core issue: outputURLPattern does not support the {md5} placeholder.

The documentation clearly lists which variables outputURLPattern supports:

PlaceholderDescription
{path}Complete path, equals {dir}/{uploadedFileName}
{uploadedFileName}Uploaded filename (including extension)
{dir}Upload directory
{protocol}, {host}, {port}URL components

{md5}, {year}, {month} and similar are only valid in the upload path uploadPath. Once a file is named as {md5}.{extName} and uploaded to the {year}/{month}/ directory, its identity in the output template has already changed to {uploadedFileName}, and its directory has become {dir}.

So when I wrote {md5} in outputURLPattern, PicGo didn't recognize it and simply output the string {md5} as-is.

Fix: Use {path} and Done

The correct outputURLPattern should be:

https://pub-hashcode.r2.dev/{path}

The complete configuration should be:

Configuration ItemValue
uploadPath{year}/{month}/{md5}.{extName}
endpointhttps://...r2.cloudflarestorage.com
customUrlhttps://pub-hashcode.r2.dev
outputURLPatternhttps://pub-hashcode.r2.dev/{path}

After saving and re-uploading, the link returned by PicGo became:

https://pub-hashcode.r2.dev/2026/07/xxxxxx.png

Typora immediately rendered it correctly.

Below is the configuration page in PicGo:

image-20260719190535169

Lessons Learned

  1. R2's S3 API endpoint ≠ public access endpoint. *.r2.cloudflarestorage.com is for program calls; to show images to visitors, you must bind it to *.r2.dev or use a custom domain, and output the correct public address through outputURLPattern in PicGo.

  2. The placeholders in uploadPath and outputURLPattern are different things. {md5}, {year} are only available when defining upload directory and filename; when outputting URLs, the file has already been stored, so you need to use {path}, {uploadedFileName}, {dir} to reference it.

  3. Broken images don't always mean the image hosting service is down. Try accessing the image URL directly in your browser first to confirm whether it's a permissions issue, path issue, or Markdown syntax issue—this can save you a lot of trouble.

ZB

Zhanbo Chen

Java Backend & AI Agent Developer

Back to home
Comments
Typora + PicGo + Cloudflare R2 Image Hosting Upload Success but Images Broken: A Record of URL Concatenation Pitfalls | Zhanbo