You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(seo): name JPEG in the post card image URL - #2065
The post card image (og:image, twitter:image, oEmbed thumbnail_url) now names its format: the same proxy URL with format=jpeg instead of format=match.
Why
format=match is negotiated from the request's Accept header, but the CDN keeps one cached copy per URL. Once a browser fetched the card URL as AVIF, every link-preview crawler got that AVIF as well, and some networks (Reddit in the reported case) then show no thumbnail. A format in the URL is part of the cache key, so the card image is always a JPEG. It is also a fraction of the size of a full PNG cover, which keeps it under the size limits some networks apply. Animated sources are passed through by the proxy whatever the format.
Only the card image changes. Images on the page itself keep negotiating AVIF/WebP.
Depends on
ecency/imagehoster#77 (explicit JPEG output flattens transparency onto white instead of black) should be deployed before this reaches production, or covers with transparent areas would show a black background in previews.
Tests
New: the card image for a metadata cover, a body image and an already proxied cover is a /p/ URL with format=jpeg, mode=fit, width=1200, height=630; a post without an image has none. Failed before the change.
Full web suite: 6,370 passing. Typecheck and lint clean.
Summary by CodeRabbit
Improvements
Cover images now use JPEG formatting in entry cards when supported, while other image URLs and missing images remain unchanged.
The og:image, twitter:image and oEmbed thumbnail were Accept-negotiated
proxy URLs. The CDN keeps one copy per URL, so after a browser fetched
one as AVIF, link-preview crawlers were served AVIF too and showed no
thumbnail. A format in the URL is part of the cache key.
Closes#2064
No actionable comments were generated in the recent review.
Walkthrough
The entry card image helper now changes format=match to format=jpeg for valid proxied post image URLs. The specs cover JPEG card URLs, missing images, and unchanged unproxied or non-URL values.
Changes: Entry card image formatting
Layer / File(s)
Summary
Format and test card images apps/web/src/app/(dynamicPages)/entry/_helpers/entry-card-fields.ts, apps/web/src/specs/app/entry-card-fields.spec.ts
The helper changes format=match to format=jpeg for valid URLs with paths starting /p/. The specs cover missing images, metadata and body images, already-proxied URLs, and unchanged unproxied or non-URL values.
Social preview card images will use a fixed JPEG format instead of a negotiated one. No concrete merge-blocking risk was found. Deploy the imagehoster transparency fix first, as the PR description notes, to avoid black backgrounds on transparent images.
Pre-merge checks: 4
Check name
Status
Explanation
Description Check
Check skipped - CodeRabbit’s high-level summary is enabled.
Title check
The title names the change: the post card image URL now names the JPEG format. It matches the main change in the changeset, which switches the card image from format=match to format=jpeg in buildEntry…
Linked Issues check
Issue #2064 requires the post card image to be a proxied URL with format=jpeg, mode=fit, width=1200, height=630; no card URL with format=match; no card image for posts without an image; and the same U…
Out of Scope Changes check
The whole-PR diff (merge base fb0dd66 to head b0afce2) changes two files as summarized: entry-card-fields.ts and entry-card-fields.spec.ts. The helper change adds cardImageFormat and updates the Ent…
Full details: Title check
Explanation
The title names the change: the post card image URL now names the JPEG format. It matches the main change in the changeset, which switches the card image from format=match to format=jpeg in buildEntryCardFields and updates the spec. The title is specific and uses a conventional fix(seo) prefix. The changeset also covers the spec update and the no-image and pass-through cases, which the title does not need to list.
Full details: Linked Issues check
Explanation
Issue #2064 requires the post card image to be a proxied URL with format=jpeg, mode=fit, width=1200, height=630; no card URL with format=match; no card image for posts without an image; and the same URL for og:image, twitter:image, and oEmbed thumbnail_url.
Evidence at head: in apps/web/src/app/(dynamicPages)/entry/_helpers/entry-card-fields.ts, buildEntryCardFields now returns image: cardImageFormat(image). cardImageFormat rewrites format=match to format=jpeg when the URL path starts with /p/. It keeps mode, width, and height unchanged, and it returns null for a missing image. Animated sources pass through the proxy, as the issue states. The spec file apps/web/src/specs/app/entry-card-fields.spec.ts covers metadata, body, and already-proxied covers, and a post without an image. Per the PR description, the new tests failed before the change.
Limits of this review: the diff shows the helper change. The summary does not show each consumer (og:image, twitter:image, oEmbed) directly. The consumer wiring is inferred from the PR description and the helper's role, and I did not inspect those routes.
The PR description's deployment dependency on ecency/imagehoster#77 is an operational note, not a coding requirement of #2064.
Full details: Out of Scope Changes check
Explanation
The whole-PR diff (merge base fb0dd66 to head b0afce2) changes two files as summarized: entry-card-fields.ts and entry-card-fields.spec.ts. The helper change adds cardImageFormat and updates the EntryCardFields.image doc comment. The spec change updates the expected proxied-cover URL and adds image test cases. Every change maps to #2064 requirements. The PR description states that body images keep AVIF/WebP negotiation. The helper applies the rewrite only to /p/ URLs with format=match, so this matches the stated scope. No unrelated change was found.
Finishing Touches
Generate unit tests (beta)
Commit to this branch
Create a new PR
Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Warning
Some tools did not complete. Review the errors below.
ESLint
If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.
I’m a rabbit with a link to share, A JPEG card image waits there. No cover? The field stays bare, Other URLs pass unchanged with care. I hop away, pleased with the pair.
Recent review info
Run configuration
Source mode: Auto
Repository: Unavailable
Branch: Unavailable
File: No configuration file from this repository applied
Commit: Unavailable
Layers: Organization UI
Review profile: CHILL
Plan: Advanced
Run ID: 3c14ce98-41e5-4702-82a4-cb52ba007d52
Commits
Reviewing files that changed from the base of the PR and between fb0dd66 and b0afce2.
Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.
Fix post card thumbnails by naming JPEG in proxy URLs
🐞 Bug fix🧪 Tests🕐 10-20 Minutes
AI Description
• Use JPEG card URLs so CDN caching cannot serve browser-negotiated AVIF to link-preview crawlers.
• Keep on-page image negotiation unchanged; cover metadata, body images, and missing images in
tests.
• Deploy imagehoster#77 first so transparent covers receive a white JPEG background.
Diagram
graph TD
E["Post entry"] --> I["Image lookup"] --> B["Card fields"] --> M["OG and Twitter"] --> C[("CDN cache")]
B --> O["oEmbed response"] --> C
Loading
High-Level Assessment
The following are alternative approaches to this PR:
1. Request JPEG directly from image lookup
➕ Expresses the desired format at image generation rather than rewriting the resulting URL.
➖ Requires confirming the helper handles already-proxied images and preserves existing lookup behavior.
Recommendation: Keep the scoped URL transformation for this fix: it changes only eligible card URLs and leaves page images untouched. A direct JPEG lookup would be worth considering if its behavior for every image source is verified. Deploy imagehoster#77 before production to avoid black backgrounds on transparent covers.
Files changed (2) +49 / -7
Bug fix (1) +23 / -2
entry-card-fields.tsName JPEG in shared card image URLs+23/-2
Name JPEG in shared card image URLs
• Changes format=match to format=jpeg for parsed /p/ proxy URLs returned as card images. Null, unparseable, and other URLs remain unchanged; the shared result feeds Open Graph, Twitter, and oEmbed.
entry-card-fields.spec.tsVerify JPEG card URLs across image sources+26/-5
Verify JPEG card URLs across image sources
• Updates the expected card image format and checks metadata covers, body images, and already-proxied covers for JPEG and 1200×630 fit parameters. Also verifies that posts without images retain a null card image.
1. New spec casts test entries through as any✓ Resolved
Description
The new it.each case in the card image spec casts the entry fixture with entry(overrides) as any
before passing it to buildEntryCardFields. This adds a fresh use of any in a TypeScript file the
PR is changing, which the rule prohibits in new or modified lines. The cast is confined to a test,
so the impact is limited to weaker type checking of that fixture, but it sets a pattern a later
reader may copy.
2. Card image fallback branches have no test coverage✓ Resolved
Description
cardImageFormat has a catch branch for inputs that are not parseable URLs and a
startsWith("/p/") guard that returns the image unchanged for non-proxy URLs. The new tests only
exercise the rewrite path, the null-image path, and proxied /p/ URLs with format=match, so
neither fallback is asserted. A regression that swallows a malformed image URL or rewrites the wrong
image would pass the suite unnoticed.
3. Transparent covers show black previews if shipped early 🐞 Bug☼ Reliability
Description
cardImageFormat rewrites every /p/ card URL to format=jpeg, so any cover with an alpha channel
is flattened by the proxy. The PR says the card should not reach production before
ecency/imagehoster#77 (which changes the flattening from black to white) is deployed, but nothing in
this repo enforces that ordering, so a merge-and-deploy would ship the black-background regression
to link previews for transparent covers.
+ const url = new URL(image);+ if (url.pathname.startsWith("/p/") && url.searchParams.get("format") === "match") {+ url.searchParams.set("format", "jpeg");
Evidence
The PR introduces the format change that depends on the external imagehoster fix, as the PR
description states. buildEntryCardFields calls cardImageFormat, and both
generate-entry-metadata.ts (line 64) and the oEmbed route (line 54) consume its output, so
og:image, twitter:image and thumbnail_url all change together.
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
## Issue description
The card image is rewritten to `format=jpeg` before the imagehoster fix that keeps transparency (black instead of white flattening) is deployed. Transparent covers will then show a black background in link previews.
## Fix Focus Areas
- apps/web/src/app/(dynamicPages)/entry/_helpers/entry-card-fields.ts[133-145]
## Recommended Fix
Either merge this change only after ecency/imagehoster#77 is live in production, or put the `format=jpeg` rewrite behind a feature flag or config value that is turned on after the imagehoster deploy. Record the ordering requirement in the PR and in a code comment next to `cardImageFormat`.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
The image field comment now says the value is a 1200×630 JPEG cover image, and the PR text says
the card image is always a JPEG. cardImageFormat only rewrites the query string, so an animated
GIF cover is sent under a format=jpeg URL and the proxy returns it as a GIF, a case the spec does
not cover.
Tip of the day
💡 Did you know, you can open the rule or relationship behind a finding in one click
- An unproxied URL and a non-URL string from the image lookup come back
unchanged.
- The new cases pass the fixture as Entry instead of any.
- The field comment says animated covers stay animated.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #2064
What
The post card image (og:image, twitter:image, oEmbed
thumbnail_url) now names its format: the same proxy URL withformat=jpeginstead offormat=match.Why
format=matchis negotiated from the request's Accept header, but the CDN keeps one cached copy per URL. Once a browser fetched the card URL as AVIF, every link-preview crawler got that AVIF as well, and some networks (Reddit in the reported case) then show no thumbnail. A format in the URL is part of the cache key, so the card image is always a JPEG. It is also a fraction of the size of a full PNG cover, which keeps it under the size limits some networks apply. Animated sources are passed through by the proxy whatever the format.Only the card image changes. Images on the page itself keep negotiating AVIF/WebP.
Depends on
ecency/imagehoster#77 (explicit JPEG output flattens transparency onto white instead of black) should be deployed before this reaches production, or covers with transparent areas would show a black background in previews.
Tests
/p/URL withformat=jpeg,mode=fit,width=1200,height=630; a post without an image has none. Failed before the change.Summary by CodeRabbit