Custom Emojis loaded from S3/R2 bypass CDN routing

Overview

when using S3 or cloudflare R2 for uploads alongside a custom CDN URL, custom emoji uploads do not respect the CDN configuration and attempt to load directly from the raw bucket URL.

The issue

when an admin uploads a custom emoji, the uploader creates an upload record and saves the raw bucket URL to the database (e.g., //my-bucket.s3.amazonaws.com/... or //my-bucket.r2.cloudflarestorage.com/...) - this is standard Discourse behavior.

however, when app/models/emoji.rb generates the emoji cache for /site.json, it passes the upload.url directly to the emoji object:

e.url = emoji.upload&.url

because it skips the CDN helper, the frontend receives the raw bucket URL. so depending on how strict the bucket’s access policies are, this results in broken images or forces Discourse to route the emojis through the internal avatar_proxy.

Solution

i have opened a PR that wraps the URL assignments in Discourse.store.cdn_url(), which makes the custom emoji loader align with how standard post images and avatars are routed.

Interim fix

while waiting for the PR to be reviewed and merged, i created a lightweight theme component that swaps the raw bucket URL for the proper CDN URL right before the custom emoji renders in the DOM (works for both posts and chat).

if you are experiencing this bug on your site, you can install this component and configure your S3 strings in the theme admin settings to fix any broken custom emojis:

Note: if you have already uploaded custom emojis that are currently borked, running discourse remap "//my-raw-bucket-url.com" "https://my-cdn.com" in the container will fix the old ones in the database, while the theme component will fix any newly uploaded ones until the PR fix is merged into core.

3 Likes

Testing a default set emoji

:smiley:

Testing a custom emoji

:falco:

yea maybe it’s a cloudflare R2 only thing then. they are breaking on my instance. it only seems to break for new ones uploaded too.

without the theme component fix, i have to run the remap every time i upload a new one. the PR might need a bit of work - i’m not super good with the emoji code.

1 Like

Discourse uses a two CDN setup, one for assets and one proxying the app.

Standard emoji uses one CDN, custom emoji uses the other CDN, but both are protected by a CDN on a properly configured website with a working two CDN setup.

I went over that in the first related topic here

Does your site have both CDNs setup and working?

2 Likes

oh i’ll have to have a look - i didn’t know that. thanks!

edit:

i wrote the section for Cloudflare R2 so assume i have it setup correctly? what might i be missing?