Common Workarounds
Broken Image Links in CMS and Category Pages¶
There is what looks like a core bug in Magento 2 where images inserted into both Category images and CMS content images are displayed as a broken link on the frontend. Inspecting the image URL will show that it's along the lines of admin_frontname/cms/wysiwyg/directive/___directive/randomstring - its temporary URL as used by the Admin to display the image. This is being stored in the database and served up on the frontend, which is obviously not accessible.
A simple workaround for this is to disable the WYSIWYG editor just above the content area. It'll need a bit of basic HTML knowledge to find your place, but avoids the headache of broken images.
- Click Show/Hide Editor so that the toolbars disappear
- Click Insert Image
- Select your image
- Click Insert Image
- Press Save Page
It should now appear correctly.
Attribute not saving on categories¶
This often occurs after a migration is completed from Magento 1, the attribute can be re-added to a category edit page using the standard Magento 2 way (not covered here) and it will be displayed and be editable but will not update on save.
What is happening is that the migrated custom attribute is assigned to the wrong attribute set. After a migration, there will be at least 2 attribute sets for a category, only one will be used by default and there will be no option to choose which set to use (in Open Source M2 any way).
To get a list of all the attributes and what they are set to use, use the following query.
-- Get attributes with assignments for type "catalog_category"
SELECT s.attribute_set_name, g.attribute_group_name, a.attribute_code, a.frontend_label, ea.sort_order
FROM eav_attribute_set s
LEFT JOIN eav_attribute_group g ON s.attribute_set_id = g.attribute_set_id
LEFT JOIN eav_entity_attribute ea ON g.attribute_group_id = ea.attribute_group_id
LEFT JOIN eav_attribute a ON ea.attribute_id = a.attribute_id
WHERE s.entity_type_id = (SELECT entity_type_id FROM eav_entity_type WHERE entity_type_code = 'catalog_category')
ORDER BY s.attribute_set_name,
g.sort_order,
ea.sort_order;
To display the default set that is being used by a category then you can use the following query.
-- Show the eav attribute set used by default
SELECT *
FROM eav_entity_attribute
WHERE entity_type_id = (SELECT entity_type_id FROM eav_entity_type WHERE entity_type_code = 'catalog_category')
AND attribute_id = ?;
To fix the issue, you need to change the attribute_set_id
to the correct default used by your entity (e.g. catalog_category
).
Varnish and Nginx Errors on Category Pages¶
For some incredibly stupid reason, Magento decided to use a concatenated string of every product in a category, including both the product id integer the string catalog_product_
, so for each product in the category we get catalog_product_{{PRODUCT_ID}}
. Headers can easily become tens of KB in size!
X-Magento-Tags header too large #6401
Troubleshooting Varnish with Magento 2
So Why Do They Do This?¶
They do this so that Varnish can be surgicly flushed based upon a per product basis.
if (req.method == "PURGE") {
if (client.ip !~ purge) {
return (synth(405, "Method not allowed"));
}
if (!req.http.X-Magento-Tags-Pattern) {
return (synth(400, "X-Magento-Tags-Pattern header required"));
}
ban("obj.http.X-Magento-Tags ~ " + req.http.X-Magento-Tags-Pattern);
return (synth(200, "Purged"));
}
They send a purge request which uses Varnish's ban functionality to invalidate any pages, or objects in Varnish speak, that match against that header.
Check Raw FPM Output¶
You might not even be able to see the raw output due to Nginx throwing errors. In this case you can use the technique described here:
This will allow you to see the header that is causing problems
To count the size of the header, you can use the above technique and pipe the output like this:
fmpscript | grep 'X-Magento-Tags' | wc -c
Which will then give you the number of characters
Mitigate By Increasing Limits¶
First level of mitigation is to increase limits in Nginx and Varnish
Follow these instructions to increase Varnish limits: Magento docs on Varnish
Read through this to understand Nginx header size limits: Nginx FastCGI response buffer sizes
Fix by Modifying Magento Functionality¶
The next level is to fix this by modifying this behaviour so that the header is not stupidly big.
There are a few strategies here:
Plugin to remove simple product tags
Product attributes not updating all values¶
This may be due to the large number of attribute options in the attribute, to check - see the error log and search for some thing like the following:
FastCGI sent in stderr: "PHP message: PHP Warning: Unknown: Input variables exceeded 1000. To increase the limit change max_input_vars in php.ini. in Unknown on line 0" while reading response header from upstream, client: 127.0.0.1, server: www.magento2.client.developmagento.co.uk, request: "POST /admin/catalog/product_attribute/validate/attribute_id/700/?isAjax=true
To fix the issue, update the php.ini
file and increase max_input_vars
; How many GET/POST/COOKIE input variables may be accepted
max_input_vars = 2000
Category Caches not clearing¶
As mentioned above, Magento makes extensive use of tags when caching things. This can be helpful if you need to cache category or product specific information and have it refreshed when something changes.
Unfortunately the category cache flushing only appears to clear the Full Page Cache, and not any others when a category is saved.
This can be fixed using a plugin before the category save event with the following code
<?php
namespace EdmondsCommerce\CacheClearer\Plugin\Magento\Catalog\Model;
use Magento\Catalog\Model\Category as OriginalClass;
use Magento\Framework\Model\Context;
class Category
{
private $cacheManagement;
/**
* Category constructor.
*
* @param Context $context
*/
public function __construct(Context $context)
{
$this->cacheManagement = $context->getCacheManager();
}
public function beforeSave(OriginalClass $subject)
{
if ($subject->hasDataChanges() !== false) {
$this->cacheManagement->clean([OriginalClass::CACHE_TAG . '_' . $subject->getId()]);
}
}
}