This is post number 10 in my 100 Days To Offload challenge. View all posts or subscribe via RSS.

A few weeks ago, I wrote a post about rendering static tweets. I’ve made a few important improvements, worthy enough of a new post.

In my original post, I described using a proxy to inject Twitter API headers. This is hacky, and leads to flaky builds, because the build server needs to handle a NodeJS proxy running in the background1. I got this working on Vercel and Cloudflare Pages2, but there was no guarantee the build would pass, and I was constantly re-running failed builds. Neither fun nor productive. In the past few weeks I’ve wanted to remove this functionality from my blog because it’s made it more difficult to preview and deploy.

Last week I was reading Nicholas Whittaker’s blog, specifically a post he wrote about static embeds for tweets and videos (sounds familiar, right?). He’s using workers to handle some of the proxying, but notably, he’s not calling Twitter’s private API, which requires authentication, he’s calling a public one.

I had no idea https://cdn.syndication.twimg.com/ existed. It’s a completely open API you can use for fetching tweets, which means it’s perfect for static sites!

Try it out: curl "https://cdn.syndication.twimg.com/tweet?id=1406108535356678145"

Check out the updated shortcode here.

After some further sleuthing on Nicholas’ blog, I realised you can configure caches in Hugo. This means you can cache calls to getJson, and push the cache to git. If a tweet ever gets deleted, as long as it exists in the cache3, I can continue rendering the tweet. Neat!


  1. This sounds more complicated than it is. The Hugo shortcode makes a request to localhost:8080/{tweetId}?auth={authToken}, the proxy then calls https://api.twitter.com/1.1/statuses/show?id=${tweetId} with the same tweet ID and an authentication header. The proxy is on GitHub if you want to take a peek. ↩︎

  2. Here is what the build command looked like: cd twitter-proxy && npm install && node index.js & sleep 5 && echo "Done" && hugo -t hello-friend --baseUrl=$BASE_URL --ignoreCache ↩︎

  3. Provided you’ve set maxAge: -1, so the cache is never purged. My config is here↩︎