{"data":{"id":"magento2-40349","source":"github-pr","sourceRef":"magento/magento2#40349","title":"Marketing params added back into query used for built-in FPC identifier creation","pr":{"number":40349,"url":"https://github.com/magento/magento2/pull/40349","author":"NateSwanson7","mergedAt":"2026-05-12T11:41:36Z","mergeCommit":"9b0a30bca39add8b8e35d7939dccabcdb720bc15","headCommit":"d127632528a875ded6fa05f7e84e68367191ce0a","baseRef":"2.4-develop","diffSha256":"a5f8020eaed6146a9180163223e1f8b5024d1ead62f9b8348a3affe615749df4"},"issues":[{"number":40350,"url":"https://github.com/magento/magento2/issues/40350","title":"Marketing params added back into query used for built-in FPC identifier creation","labels":["Area: Framework","Component: PageCache","Issue: Confirmed","Priority: P2","Progress: PR Created","Progress: done","Reported on 2.4.x","Reproduced on 2.4.x","Triage: Dev.Experience"],"kind":"human"}],"fixedIn":null,"containingTags":[],"reportedOn":null,"codeMatch":{"2.4.6":"file-missing","2.4.6-p1":"file-missing","2.4.6-p2":"file-missing","2.4.6-p3":"file-missing","2.4.6-p4":"file-missing","2.4.6-p5":"file-missing","2.4.6-p6":"file-missing","2.4.6-p7":"file-missing","2.4.6-p8":"file-missing","2.4.6-p9":"file-missing","2.4.6-p10":"file-missing","2.4.6-p11":"file-missing","2.4.6-p12":"file-missing","2.4.6-p13":"file-missing","2.4.6-p14":"file-missing","2.4.6-p15":"file-missing","2.4.7":"conflict","2.4.7-p1":"conflict","2.4.7-p2":"conflict","2.4.7-p3":"conflict","2.4.7-p4":"conflict","2.4.7-p5":"conflict","2.4.7-p6":"conflict","2.4.7-p7":"conflict","2.4.7-p8":"conflict","2.4.7-p9":"conflict","2.4.7-p10":"conflict","2.4.8":"conflict","2.4.8-p1":"conflict","2.4.8-p2":"conflict","2.4.8-p3":"conflict","2.4.8-p4":"conflict","2.4.8-p5":"conflict","2.4.9":"clean"},"affectedVersions":["2.4.9"],"components":["magento/framework","magento/module-page-cache"],"files":[{"path":"app/code/Magento/PageCache/Model/App/Request/Http/IdentifierForSave.php","change":"modified","package":"magento/module-page-cache"},{"path":"lib/internal/Magento/Framework/App/PageCache/Identifier.php","change":"modified","package":"magento/framework"}],"stripped":{"tests":["app/code/Magento/PageCache/Test/Unit/Model/App/Request/Http/IdentifierForSaveTest.php","lib/internal/Magento/Framework/App/Test/Unit/PageCache/IdentifierTest.php"],"docs":[],"outsideCode":[]},"linesChanged":42,"mergeBatched":false,"excluded":null,"sections":{"description":"- Marketing parameters (e.g., utm_*, gclid, fbclid, etc.) are stripped by regex \n  into a sanitized $url, but then reintroduced during cache identifier generation.","stepsToReproduce":"1. Enable built-in Full Page Cache mode.\n2. Visit any storefront page with marketing/tracking parameters included in the URL, e.g.:\n\n   https://example.com/?utm_source=test&utm_medium=cpc&gclid=TEST123&foo=bar\n\n3. Observe that Magento applies regex stripping patterns inside\n   Magento\\Framework\\App\\PageCache\\Identifier::getValue() to remove marketing parameters.\n\n4. However, inspect the final cache key (e.g. via debugging `Identifier::getValue()`\n   or enabling cache debug mode):\n\n   - The sanitized URL (with tracking params removed) is used only for generating the base URL.\n   - The `$query` portion of the FPC identifier is rebuilt using the original request’s query array.\n\n5. As a result, the cache identifier still contains all original query parameters,\n   including marketing parameters that were intended to be stripped.\n\n6. This leads to different FPC entries for equivalent URLs that differ only by\n   marketing/tracking parameters.","expectedResult":"- Marketing/tracking parameters defined in `getMarketingParameterPatterns()` \n  (e.g., utm_*, gclid, fbclid, msclkid, etc.) should be removed before building \n  the Full Page Cache identifier.\n\n- The query string used in the identifier should be reconstructed from the \n  sanitized URL (after marketing params are removed), not from the original \n  request's query array.\n\n- URLs that differ only by ignored marketing parameters should produce the \n  same built-in FPC cache key.","actualResult":"- Marketing parameters (e.g., utm_*, gclid, fbclid, etc.) are stripped by regex \n  into a sanitized $url, but then reintroduced during cache identifier generation.\n\n- In `reconstructUrl()`, `$query` is rebuilt from:\n      `$this->request->getUri()->getQueryAsArray()`\n  which contains the full original query string including all marketing parameters.\n\n- Therefore, the resulting cache key still includes marketing parameters, \n  causing cache fragmentation and defeating the purpose of the new \n  marketing-parameter-stripping logic.\n\n- Only the *order* of query parameters is normalized (via ksort()), \n  but the actual marketing parameters are not removed.","source":"issue"},"signatures":[],"labels":{"area":["Framework"],"component":["PageCache"],"priority":"P2","severity":null,"reportedOn":["2.4.x"]},"categories":["Cache"],"triage":{"model":"@cf/cloudflare/clef","requestHash":"33bca696a7af1705585452cca7cdba8df3825cc4bc52f7db41fa9baec000bd31","isBugfix":0.9765,"changeKind":{"choice":"bugfix","probabilities":{"bugfix":0.9525,"feature":0.0063,"refactor":0.0253,"tests_only":0.0087,"docs_only":0.0035,"dependency":0.0037},"confidence":0.8897},"scope":{"score":1.0214,"probabilities":{"0":0.21,"1":0.5585,"2":0.2315},"confidence":0.1144},"risk":{"score":0.5823,"probabilities":{"0":0.4693,"1":0.4791,"2":0.0516},"confidence":0.1787},"area":{"choice":"framework","probabilities":{"catalog":0.0039,"checkout":0.0042,"customer":0.003,"admin":0.0035,"graphql_api":0.0022,"framework":0.9688,"frontend":0.0066,"other":0.0078},"confidence":0.93},"securityRelevant":0.0077,"reportedVersion":{"choice":"unspecified","probabilities":{"2.4.0":0.0066,"2.4.0-p1":0.0049,"2.4.1":0.0039,"2.4.1-p1":0.0033,"2.4.2":0.0052,"2.4.2-p1":0.0041,"2.4.2-p2":0.0059,"2.4.3":0.012,"2.4.3-p1":0.0093,"2.4.3-p2":0.0085,"2.4.3-p3":0.007,"2.4.4":0.0129,"2.4.4-p1":0.0074,"2.4.4-p10":0.0064,"2.4.4-p11":0.0079,"2.4.4-p12":0.0071,"2.4.4-p13":0.0069,"2.4.4-p14":0.0077,"2.4.4-p15":0.0063,"2.4.4-p16":0.0069,"2.4.4-p17":0.0056,"2.4.4-p18":0.005,"2.4.4-p2":0.0034,"2.4.4-p3":0.0043,"2.4.4-p4":0.0043,"2.4.4-p5":0.0046,"2.4.4-p6":0.0051,"2.4.4-p7":0.0048,"2.4.4-p8":0.0042,"2.4.4-p9":0.004,"2.4.5":0.011,"2.4.5-p1":0.0087,"2.4.5-p10":0.0065,"2.4.5-p11":0.008,"2.4.5-p12":0.0091,"2.4.5-p13":0.0079,"2.4.5-p14":0.0091,"2.4.5-p15":0.0059,"2.4.5-p16":0.0064,"2.4.5-p17":0.0073,"2.4.5-p2":0.004,"2.4.5-p3":0.0057,"2.4.5-p4":0.0071,"2.4.5-p5":0.0066,"2.4.5-p6":0.0063,"2.4.5-p7":0.0063,"2.4.5-p8":0.0057,"2.4.5-p9":0.0046,"2.4.6":0.0142,"2.4.6-p1":0.0114,"2.4.6-p10":0.0093,"2.4.6-p11":0.0108,"2.4.6-p12":0.0109,"2.4.6-p13":0.0087,"2.4.6-p14":0.011,"2.4.6-p15":0.0063,"2.4.6-p2":0.0046,"2.4.6-p3":0.0073,"2.4.6-p4":0.0091,"2.4.6-p5":0.009,"2.4.6-p6":0.0098,"2.4.6-p7":0.0079,"2.4.6-p8":0.0062,"2.4.6-p9":0.0055,"2.4.7":0.0126,"2.4.7-p1":0.0091,"2.4.7-p10":0.008,"2.4.7-p2":0.0065,"2.4.7-p3":0.0089,"2.4.7-p4":0.0159,"2.4.7-p5":0.0107,"2.4.7-p6":0.0133,"2.4.7-p7":0.0102,"2.4.7-p8":0.0095,"2.4.7-p9":0.0055,"2.4.8":0.0122,"2.4.8-p1":0.0107,"2.4.8-p2":0.0128,"2.4.8-p3":0.0121,"2.4.8-p4":0.014,"2.4.8-p5":0.0129,"2.4.9":0.0129,"unspecified":0.3485},"confidence":0.1167},"backportWorthy":{"score":1.2891,"probabilities":{"0":0.2219,"1":0.2671,"2":0.511},"confidence":0.0726}},"curated":{"title":"Fixes built-in FPC keys still containing marketing params after stripping","description":"Marketing parameters such as utm_*, gclid and fbclid are stripped into a sanitized URL but reintroduced during cache identifier generation, so the built-in Full Page Cache stores separate entries for URLs that differ only by them. Identifier::reconstructUrl() now parses the query string from the sanitized URL, and IdentifierForSave calls it.","categories":["Cache"],"author":"claude-code/opus-5.5","date":"2026-10-06","reviewedBy":null},"tests":{"2.4.9":{"before":"not-runnable","after":"pass","adapted":false,"runAt":"2026-10-06T09:20:15.907Z","releaseCommit":"755e34dd689021c5165db9d35ecff74f7dc51527","suites":{"unit":{"before":"not-runnable","after":"pass","runAt":"2026-10-06T09:20:15.907Z"}}}}},"_documentation":"https://magento.watch/api","_description":"Upstream fix magento2-40349 details"}