Skip to content

Frontend URL replacement exhausts PHP memory on large responses #1117

Description

@pirate-bot

Summary

Optimole can exhaust the PHP memory limit while rewriting URLs in a frontend response that contains a large number of distinct eligible URLs. Expected behavior: frontend output optimization completes without terminating the request under supported site content. Actual behavior: the request terminates with an allowed-memory-size fatal error during URL replacement. Impact: affected visitors receive a failed frontend response instead of the rendered page.

Customer context

  • Product / area: Optimole WordPress plugin, frontend output URL replacement
  • Version: 4.2.10
  • Environment: WordPress 7.1; PHP 7.4.33 and PHP 8.1.2-1ubuntu2.25; 128 MB PHP memory limit in reported crashes
  • Integration / third party: Not provided
  • Reported error / symptom: Allowed memory size of 134217728 bytes exhausted (tried to allocate 90112 bytes)
  • Impact: 2 occurrences across 2 distinct production sites between 2026-08-21 and 2026-08-25.

Reproduction notes

  1. Use a frontend request eligible for Optimole output replacement.
  2. Render a sufficiently large response containing many distinct URLs matching Optimole's image or asset extraction rules.
  3. Run with a 128 MB PHP memory limit.
  4. Observed in production: the request can terminate at URL replacement with Allowed memory size of 134217728 bytes exhausted.

A local runtime reproduction was not performed; exact content size and URL cardinality are unknown.

Diagnosis

Conclusion

Production telemetry records a frontend fatal at inc/manager.php:784. The inspected code buffers the full frontend response, extracts all distinct matching URLs, then applies a full-string preg_replace() once for each URL. This creates a direct memory-exhaustion path for sufficiently large content or high unique-URL cardinality. The reported fatal is in Optimole product code, not Themeisle SDK code.

Where this likely occurs

  • inc/manager.phpOptml_Manager::process_template_redirect_content() lines 793-817 starts an output buffer for eligible frontend requests and passes the full response to replace_content().
  • inc/manager.phpOptml_Manager::replace_content() lines 441-533 invokes process_urls_from_content() at lines 525-529 after other whole-content processing.
  • inc/manager.phpOptml_Manager::extract_urls_from_content() and Optml_Manager::normalize_urls() lines 703-737 collect matching URLs and deduplicate them, with no observed bound on input size or number of unique URLs.
  • inc/manager.phpOptml_Manager::do_url_replacement() lines 748-785 loops over each extracted URL and assigns a complete preg_replace() result back to $html at line 784, matching the production crash location.
  • v4.2.10 is release commit 060fd3fe (2026-07-15). The inspected v4.2.9..v4.2.10 diff contains no change to inc/manager.php; blame attributes the replacement loop to b7f67fd7 (2019-12-14). Available history does not establish a regression in 4.2.10.

Engineering notes

The failure surface is the non-partial frontend output-buffer workflow when URL processing reaches do_url_replacement(). The peak memory requirement can grow with both the buffered response size and the count of distinct image or, when CDN processing is active, asset URLs. Two production reports on different PHP versions reached the same fatal location under a 128 MB memory limit. The exact page size, URL count, active Optimole settings, and other plugins on those sites are unavailable.

Test coverage status

tests/test-replacer.php covers functional replacement through test_optimization_url() lines 217-226, test_assets_url() lines 323-358, test_style_replacement() lines 372-379, and test_elementor_data() lines 482-505. These tests assert transformed output for representative content but do not cover large buffered responses, high unique-URL cardinality, peak memory, or the frontend output-buffer hook. No relevant resource-bound coverage was found during inspection.

What to verify or explore next

  • May be worth reproducing with the frontend output-buffer workflow, a constrained 128 MB PHP memory limit, and progressively larger HTML containing distinct eligible image and asset URLs.
  • If reproducible, checking peak memory for replace_content() and do_url_replacement() separately may distinguish extraction cost from repeated replacement cost.
  • May be worth confirming behavior with CDN asset processing enabled and disabled, since extract_urls_from_content() expands the eligible extensions when that setting is active.

Unknowns / follow-up

  • The telemetry report has no structured stack trace beyond the fatal location.
  • The specific response size, number of extracted URLs, and per-site Optimole configuration were not captured.
  • No earlier working release boundary is available from the incident evidence.

Confidence

Confidence: 91/100

Behavior inventory: 1. A frontend Optimole URL-replacement request can exhaust PHP memory. Production telemetry pinpoints the fatal at the per-URL replacement loop, and repository inspection confirms that a complete buffered response is repeatedly processed without a content-size or URL-cardinality bound. The path is product code, not the Themeisle SDK.

Crash telemetry

Occurrences 2
Distinct sites 2
First seen 2026-08-21 04:36 UTC
Last seen 2026-08-25 09:18 UTC
Crash location product:inc/manager.php:784
Request context frontend
Inside Themeisle SDK no
Product versions 4.2.10
WP versions 7.1
PHP versions 7.4.33, 8.1.2-1ubuntu2.25
SDK versions 3.3.58, 3.3.59

Source: automated crash report — optimole-wp, fingerprint e16d0f7a1b6dbfc263cf3fb43ab3af81
Generated by bug-report-triage (ID: bug-report-triage_6a8e81425e50f1.08531822)

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions