Skip to content

Image parser uses a data: placeholder from data-src as the image URL, so the real src misses srcset enhancement #1186

Description

@selul

Summary

When an image has a lazy-load attribute that holds a data: placeholder before its real src, the image parser takes the placeholder as the image's URL. The tag replacer then works on that placeholder instead of the real image, so the image never gets the page profiler's missing-srcset / sizes entries.

<img data-src="data:image/gif;base64,R0lGODlhAQABAIAAAP///////yH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" src="https://example.com/wp-content/uploads/2026/10/photo.jpg" alt="Test" />

The same image with src first, or without the data-src, is enhanced normally.

Found by the QA agent while testing #1184 / #1185. It also happens on development, so it isn't caused by #1185.

What happens

  1. Optml_Manager::parse_images_from_html() (inc/manager.php) matches the first src or possible_src_attributes() attribute it finds and returns its value as img_url. Here that's data-src, so img_url is data:image/gif.
  2. Optml_Tag_Replacer::process_image_tags() goes ahead with that URL: can_replace_tag( 'data:image/gif' ) returns true.
  3. The "optimized" URL for data:image/gif is the placeholder itself, so add_missing_srcset_attributes() builds every candidate from it. esc_url() turns each one into an empty string, and nothing is added.
  4. The real src is still moved to the CDN by the later URL replacement pass. Only the tag-level work is done against the wrong URL.

The image also gets data-opt-id computed from the placeholder URL (get_id_by_url( 'data:image/gif' )) instead of the real image. So every image with that placeholder shares one profiler ID.

Reproduction

PHPUnit, with profiler data seeded for the image (keyed by the real src ID and by the data-opt-id the browser profiler reports, assets/js/modules/srcset-detector.js:139):

Tag shape development #1185
data-src="data:…" before src not enhanced not enhanced
src before data-src="data:…" enhanced enhanced
no data-src enhanced enhanced

Impact

  • Images whose markup puts a lazy-load data: placeholder attribute before src miss the profiler's responsive srcset / sizes.
  • Other per-tag work that uses img_url (crop and dimension handling, profiler IDs, possibly lazy-load decisions) runs against the placeholder. This needs checking.

Possible fix

When picking an image's URL, skip attributes whose value is a data: URI, so a real src is used. This changes what parse_images_from_html() returns, which feeds every image replacement. It needs its own tests across the replacer and lazy-load suites, and a check against possible_src_attributes() integrations.

Activity

  1. self-assigned this
    on Oct 16, 2026
  2. added theissue type on Oct 16, 2026
  3. removed their assignment
    on Oct 16, 2026
  4. added
    bugThis label could be used to identify issues that are caused by a defect in the product.
    on Oct 19, 2026
  5. self-assigned this
    on Oct 20, 2026
  6. kushh23 commented on Oct 20, 2026

    @kushh23
    Contributor

    🧪 QA steps and test links

    When an image's lazy-load attribute holds a data: placeholder and comes before the real src, Optimole treats the placeholder as the image. Every such image gets the same data-opt-id, so the page profiler can't give it its own width, height or responsive srcset.

    Requires: a site connected to Optimole, for the steps outside the links.

    Link Build Bot check
    Reproduce the bug Optimole 4.2.17 (released) ✅ Reproduced: Images 1 and 2 both have data-opt-id="771715633", Image 3 (same file as Image 1) has a different one
    Verify the fix PR #1190 build a9f9857 ✅ Fixed: Images 1 and 2 have different IDs (443598161, 1527998280), Images 1 and 3 share one

    In the links:

    • Steps 1 to 3 are already done, and the links open on the test page.
    • Optimole is connected with a placeholder account, so the images show as broken. Check the HTML, not the pictures.
    • The PR build still shows Optimole 4.2.17 on the Plugins screen.
    • Tools → QA Logs shows debug.log.

    Reproduce on Optimole 4.2.17:

    1. Pages → Add New Page, title it "Optimole placeholder test".
    2. Add a Custom HTML block with three images, using any two image URLs from your Media Library (A and B) and this placeholder: data:image/gif;base64,R0lGODlhAQABAIAAAP///////yH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
      • Image 1: <img data-src="PLACEHOLDER" src="URL-A" alt="Image 1">
      • Image 2: <img data-src="PLACEHOLDER" src="URL-B" alt="Image 2">
      • Image 3: <img src="URL-A" data-src="PLACEHOLDER" alt="Image 3">
    3. Publish and view the page.
    4. Right-click each image → Inspect, and note its data-opt-id.
    • Bug: Images 1 and 2 have the same data-opt-id (771715633), although they are different files. Image 3 has another ID, although it is the same file as Image 1.
    • Fixed: Images 1 and 2 have different IDs, and Images 1 and 3 have the same ID.
    Edge cases
    • Optimole lazy load on: Images 1 and 2 lazy-load from their real photo, and the data-src placeholder is kept.
    • <img src="PLACEHOLDER" data-lazy-src="URL-A"> (another lazy-load plugin owns the image): same output as on 4.2.17, with no extra <noscript> copy.
    • <img data-src="PLACEHOLDER"> with no src: the page renders normally and the tag is left alone.
    • Upper case DATA-SRC="DATA:image/gif...": behaves like Image 1.
    • Live site, after a few logged-out visits: Image 1 gets the profiler's width, height and srcset, like an image without the placeholder.

    Fix: #1190 (open)

  7. pirate-bot commented on Oct 20, 2026

    @pirate-bot
    Collaborator

    QA agent — 🟢 Passing checks · Placeholder-first images now receive responsive metadata and real-image profiler IDs.

    Placeholder-first images now receive srcset, sizes, dimensions, and IDs based on the real photo. The same pages reproduce the missing attributes and shared IDs on the base build. Lazy-load targets and existing third-party noscript structures also behaved as expected, and all four new tests passed on PHP 7.4 and 8.3. This used seeded connection and profile data; live profiler collection and CDN delivery were not verified.

    Worth a human look

    1. Review whether query-string variants should have distinct profiler IDs: exploratory parsing of photo-a.jpg?version=7&qa=1 returned the base URL while preserving the complete tag. The same src-first capture occurs on the base build; exact query capture is not specified by this
    Checks (5 passed)
    • ✅ On a published page with profiler measurements for the real image, a data: lazy-load placeholder before src does not prevent the image from receiving the real image's responsive srcset and sizes.
    • ✅ Placeholder-first images receive dimensions from the real image's profiler record and a data-opt-id for that real image: different files have different IDs, and the same file has the same ID regardless of attribute order.
    • ✅ With Optimole lazy load enabled, both placeholder-first src and placeholder-first data-lazy-src images are lazy-loaded from their real image URL while the original data-src placeholder is retained.
    • ✅ Images owned by another lazy loader, with a data: src and a real data-lazy-src, retain their placeholder source and existing noscript structure without an extra Optimole noscript copy.
    • ✅ The parser selects the real URL and preserves the whole tag for double quotes, single quotes, data-lazy-src, src-first and uppercase DATA-SRC/DATA: markup and supported source-attribute integrations; a tag containing only data: lazy attributes and no real source is left alone.

    Full report, evidence and session trail

  8. added a commit that references this issue on Oct 21, 2026
    e29c894
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugThis label could be used to identify issues that are caused by a defect in the product.bug-report

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions