Skip to content

Skip oversized Link preload header in Optimization Detective#2572

Closed
faisalahammad wants to merge 2 commits into
WordPress:trunkfrom
faisalahammad:fix/2304-oversized-link-header
Closed

Skip oversized Link preload header in Optimization Detective#2572
faisalahammad wants to merge 2 commits into
WordPress:trunkfrom
faisalahammad:fix/2304-oversized-link-header

Conversation

@faisalahammad

Copy link
Copy Markdown

Summary

Fixes #2304

Image Prioritizer (via Optimization Detective) can generate a Link: rel=preload response header that gets too large when responsive images have long or non-ASCII filenames across multiple viewport breakpoints with different LCP images. Some reverse proxies (Nginx) reject the response with "upstream sent too big header", causing intermittent 502s for logged-in users.

Relevant technical choices

od_optimize_template_output_buffer() in optimization.php already sends both a Link response header and equivalent HTML <link> tags for every collected preload link, unconditionally. The HTML tags carry the same preload info, so the fix skips sending the header when it would exceed a length cap and just relies on the HTML tags in that case.

  • Added od_get_maximum_link_response_header_length(), mirroring the existing od_get_maximum_url_metric_size() pattern (filter od_link_response_header_max_length, default 4 KB, _doing_it_wrong() fallback on invalid values).
  • Guard added to the existing header() call in optimization.php; the append_head_html() call stays unconditional so preloading keeps working through HTML tags when the header is skipped.
  • Fix lives in Optimization Detective (not Image Prioritizer) since every extension that adds preload links funnels through this one call site.
  • Added doc entry for the new filter in docs/hooks.md.
  • Added tests for the new getter (default, filter override, invalid value) and for the HTML fallback always being present. Note: headers_sent() is always true in this repo's PHPUnit harness, so the actual header() call itself can't be asserted on in automated tests (this was already true before this change, nothing here regresses that).

Testing

Manual: page with responsive images across several breakpoints resolving to different LCP images with long filenames, logged in, uncached request — confirmed no single Link header exceeds the cap and <link rel="preload"> tags are still present in <head>. Verified the filter raises/lowers the cap as expected.

Automated: npm run test-php:optimization-detective (360 tests pass), composer lint:optimization-detective (clean), npm run phpstan (no errors).

Use of AI Tools

Used an AI coding assistant (Claude Code) to investigate the codebase, draft the implementation, tests, and docs, and to run the test/lint/static-analysis suite. I reviewed the change, ran the tests myself, and take responsibility for the code.

- Add od_get_maximum_link_response_header_length() with filter
  od_link_response_header_max_length (default 4KB)
- Skip sending the Link response header when it would exceed the
  limit, falling back to the existing HTML link tag output
- Add tests covering the new getter and the HTML fallback

Some reverse proxies fail requests with an oversized response header,
which can happen when responsive images have long or non-ASCII
filenames across multiple viewport breakpoints. The HTML link tags
already carry the same preload info, so skipping the header when it
is too large keeps the request from failing without losing preload
behavior.

Fixes WordPress#2304
@github-actions

github-actions Bot commented Jul 5, 2026

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: faisalahammad <faisalahammad@git.wordpress.org>
Co-authored-by: westonruter <westonruter@git.wordpress.org>
Co-authored-by: ttwrpz <sigmarubyz@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@github-actions github-actions Bot added the [Plugin] Optimization Detective Issues for the Optimization Detective plugin label Jul 5, 2026
@codecov

codecov Bot commented Jul 5, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 70.31%. Comparing base (8d70987) to head (381aaf8).
⚠️ Report is 15 commits behind head on trunk.

Additional details and impacted files
@@            Coverage Diff             @@
##            trunk    #2572      +/-   ##
==========================================
- Coverage   70.35%   70.31%   -0.04%     
==========================================
  Files          91       91              
  Lines        7867     7857      -10     
==========================================
- Hits         5535     5525      -10     
  Misses       2332     2332              
Flag Coverage Δ
multisite 70.31% <100.00%> (-0.04%) ⬇️
single 35.21% <0.00%> (+0.04%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@westonruter westonruter added the [Type] Bug An existing feature is broken label Jul 9, 2026

@westonruter westonruter left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay. Please see my comment on the issue.

…ader

Reworks the oversized Link preload header fix per reviewer feedback:
instead of capping/skipping the header when too large, permanently
strip imagesrcset/imagesizes from the Link response header since
those attributes cause the bloat and web.dev now recommends relying
on the HTML link tag for responsive image preloading anyway.

- Remove od_get_maximum_link_response_header_length() and the
  od_link_response_header_max_length filter added previously; the
  header guard is back to its original simple form.
- OD_Link_Collection::get_response_header() now omits imagesrcset
  and imagesizes; get_html() is unchanged and still emits them.
- Update tests and docs accordingly.

Addresses PR feedback.

Refs WordPress#2572
@faisalahammad

Copy link
Copy Markdown
Author

@westonruter Thanks, I read your comment on #2304 and agree, this is a better fix.

Changed approach: instead of capping the Link header length with a new filter, I now strip imagesrcset/imagesizes from the Link response header only. The HTML <link rel="preload" imagesrcset="..." imagesizes="..."> tag is unchanged and still carries the full responsive data, so preloading still works the same for browsers, just through the HTML element instead of the HTTP header, matching the web.dev guidance you linked.

Removed the od_link_response_header_max_length filter and od_get_maximum_link_response_header_length() entirely since they are no longer needed. Updated docs and tests to match.

Ready for another look.

@faisalahammad

Copy link
Copy Markdown
Author

Closing in favor of #2578, which better matches the direction from @westonruter's feedback on #2304 and the web.dev guidance on responsive-image preloads.

#2578 excludes links that have imagesrcset from the Link response header entirely and relies on the HTML <link rel="preload" imagesrcset="..." imagesizes="..."> tag. This PR only stripped those attributes and still emitted a residual header entry (including a useless Link: <about:blank>; … for imagesrcset-only links).

Thanks @westonruter for the review that drove the approach change — continuing review on #2578.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Plugin] Optimization Detective Issues for the Optimization Detective plugin [Type] Bug An existing feature is broken

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Image Prioritizer causes 502 Bad Gateway due to oversized Link response headers (Nginx reverse proxy)

2 participants