Avatars take a long time to load after moving to S3-compatible R2

Hello,

I just migrated to R2 and everything went perfectly. All images have the S3 CDN URL link. However, I noticed an issue: avatars are taking a long time to load. On average, it takes about 3 to 4 seconds, whether I’m clicking on a user’s avatar or looking inside a post. Is this expected?

hmmm, i suspect it could be one of 3 different issues, but the most likely to me is on-the-fly resizing.

1. on-the-fly avatar resizing

when you migrated your uploads to R2, it moved the original images; however, discourse uses many different sizes of avatars (eg 45px for posts, 120px for the user card).

if those specific optimized sizes didn’t migrate perfectly, or haven’t been generated yet, dscourse has to generate them synchronously at the moment the user clicks them:

  1. discourse downloads the original avatar from R2 to the local server
  2. resizes it using imagemagick
  3. uploads the new size back to R2
  4. redirects the browser to the new URL, and this process takes 3 to 4 seconds

to verify: hard-refresh the page - if the avatar takes 3-4 seconds the first time, but loads instantly the second time, this is exactly what is happening.

to fix: this will naturally fix itself as users browse and the sizes are generated. but you can fix it instantly, by forcing the server to pre-generate all avatars in the background by ssh’ing into your server and running:

./launcher enter app
rake avatars:refresh

2. the 3 second IPv6 timeout

if the avatars are taking 3-4 seconds every time even after multiple refreshes, they are likely hitting a networking timeout.

cloudflare R2 api endpoints are dual-stack in that they use both ipv4 and ipv6. if your server droplet has an ipv6 address assigned to it, but the host’s ipv6 gateway isn’t routed properly, ruby’s internal connection to the R2 bucket will attempt ipv6 first, hang for 3 second (this is the default linux TCP timeout), fail and then instantly succeed using ipv4.

to verify: ssh into the server and run:

curl -I -6 https://cloudflare.com

if it hang for a few seconds and fails, the server’s ipv6 is broken, causing every internal S3 api check to suffer a 3-second delay.

to fix: you will need to either fix the ipv6 routing in in your host control panel or maybe even disable ipv6 on the droplet entirely

3. gravatar delays

if your site is configured to check for gravatar updates it may be pinging gravatar’s external servers before rendering the avatar. if the server has a slow outbound connection (also often related to DNS or ipv6) it will likely block the avatar render.

to verify: run this on your server
curl -I -6 https://gravatar.com
if it hangs for 3 seconds the ipv6 is broken (see above)

gravatar-related fix: in your discourse settings go to automatically download gravatars, turn it off temporarily, and see if that fixes it - i don’t think this is the problem but if it is, i you can leave the setting off, or fix the ipv6 routing as in 2 above, or maybe change the DNS resolver.

Thank you for your quick response. I think I had already tried the ‘rake avatars:refresh’ before, but I’m not absolutely sure.

What used to work for me to see the avatar open immediately was clicking on it a first time; on the second time, it would open instantly. But that’s probably due to caching. Also, I just tested your second tip and it returns a “HTTP/2 301”, with several other lines’. Same thing for tip 3. I’ll run avatars:refresh again in a few days, as I needed to restore a snapshot. Thanks again!

Gravatar

server: nginx
date: Mon, 22 Jun 2026 19:29:00 GMT
content-type: text/html; charset=utf-8
content-length: 0
content-language: en
expires: Wed, 11 Jan 1984 05:00:00 GMT
cache-control: no-cache, must-revalidate, max-age=0
x-redirect-by: Gravatar
location: https://en.gravatar.com/
alt-svc: h3=":443"; ma=86400
strict-transport-security: max-age=31536000; includeSubdomains; preload

CF

HTTP/2 301
date: Mon, 22 Jun 2026 19:27:00 GMT
content-type: text/html
content-length: 167
location: https://www.cloudflare.com/
cache-control: max-age=3600
expires: Mon, 22 Jun 2026 20:26:59 GMT
set-cookie: __cf_bm=eBP2aJ7Eg30nHPuvMMNxxKrgNtcNwKs0WDgnYyONeus-1782156420-1.0.1.1-sXpW27iuhGDF615cOfwNFybH4IMxgvZy3uA_3X_o..402T_3KSgT7CSymipL5RjdpGe3raWEqsVxQFFLPKRoDjfoT7B.0rqyDt.osbkOF98; path=/; expires=Mon, 22-Jun-26 19:57:00 GMT; domain=.cloudflare.com; HttpOnly; Secure; SameSite=None
report-to: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=QfYqSekEDPJHC2k%2BMjHN0cGjz172tmUWe2GSR8EgwNLh3TGjFYkQ0vwPxlzY1NcBcKFOMaAi4FlgjqjhETOOtHf%2BH9KdQSvqN3OME2Uh1i4nHIw%2Fy1qkvSpf4jxDchM7CaDW80tJkjBV4OqF"}],"group":"cf-nel","max_age":604800}
nel: {"success_fraction":0,"report_to":"cf-nel","max_age":604800}
strict-transport-security: max-age=15780000; includeSubDomains
server: cloudflare
cf-ray: a0fda5d8ecd6b26d-LAX
alt-svc: h3=":443"; ma=86400

yea given your reply, i am almost certain it is issue #1 because the curl command results for cloudflare and gravatar appear to be as expected. give rake avatars:refresh a try whenever it is convenient for you and let me know if it works.