Limits and guarantees¶
SEO Suite Pro does five jobs, and each of them stops somewhere. This page writes out each stop as the sentence you would otherwise have assumed.
An overstated guarantee costs more than a missing feature, because you act on it. So every "never", "only" and "does not" below is load-bearing, and nothing is claimed here that is not true of the version you can install today.
At a glance¶
| If you are asking | The short answer |
|---|---|
| Does installing it change my shop? | No. Every area arrives switched off, and so does the suite; nothing changes until you turn on what you want. More |
| Does an upgrade keep my settings? | Yes, apart from the switches, except the one update from 1.3.0 or earlier: note your settings down before that one. After an upgrade the suite and each area are off again on every shop; everything else you set comes back as you left it. Your redirect rules, image descriptions, markup overrides and crawl rules survive too. More |
| Does it tell me how to rank better? | No. It does not score your pages, does not tell you what to write, and makes no claim about what any search engine will do. More |
| Will generating overwrite URLs or meta I wrote? | Generate never overwrites anything. Regenerate everything does, including ones you set by hand, and there is no undo. More |
| Can manufacturers get meta titles and descriptions? | No. Manufacturers get a URL keyword and nothing else, because OpenCart holds no meta title and no meta description for a manufacturer. More |
| Which pages carry markup? | Products, categories, brand pages, information pages and the home page, and no others. A theme that emits no </head> carries no markup at all. More |
| Does alt text work with any theme? | No. A theme this extension cannot read gets nothing, and the screen says so. Only the alt attribute is rewritten, never the title. More |
| Are broken URLs always recorded? | Only while OpenCart's own SEO URLs are on. Serving a redirect does not depend on that setting. More |
| Does the sitemap list every page? | No. A page with no URL keyword is left out, and so is anything your store does not display. Only products carry a <lastmod>. More |
| Does the sitemap regenerate on its own? | Only if your store's cron is running. Otherwise use the command or Generate; there is deliberately no web address to paste instead. More |
What holds across all five areas¶
Every area arrives switched off, and so does the suite. Installing SEO Suite Pro changes nothing about what your shop serves until you have been through the screens and turned on what you want. There is no default configuration applied on your behalf.
Turning an area off stops it completely. Each storefront handler checks its own switch before it reads anything, so an area that is off costs a page nothing and leaves it exactly as your theme rendered it.
The suite's own switch does not stop the areas. It is the switch OpenCart's extension list reads to show SEO Suite Pro as enabled, and no storefront handler reads it. To stop an area, switch that area's own Status off.
An upgrade gives your settings back, and switches everything off. An OpenCart upgrade is an uninstall and then an install, and the uninstall deletes the suite's settings on every shop. The suite keeps its own copy of what you saved on each screen and puts it back when the new version installs. The switches are the exception: the suite and each of the five areas come back off on every shop, the way a fresh install does, so after an upgrade you switch on what you had running and nothing else needs typing again. Which values, key by key, is on the settings reference, and it is also the first section of the changelog.
One exception: updating from 1.3.0 or earlier. Those versions kept no copy, and the old version's uninstall runs before the new one can keep anything, so that one update asks for your values again. Note them down before you update; every update after it keeps them. How to.
What an upgrade does not touch is the work you put into the suite. Your redirect rules, the list of broken URLs, your image descriptions, your per-page markup overrides and your crawl rules all live outside the settings OpenCart deletes, and all five survive an uninstall on purpose, because an upgrade is an uninstall, and losing your work for pressing Upgrade is not a trade anybody agreed to. Removing any of it stays a deliberate act on a screen.
The published sitemap files are the exception: they are removed on uninstall, so after an upgrade they are gone until the sitemap is generated again.
Nor does it touch what the suite wrote into your catalogue. A URL keyword is an address somebody has linked to and a meta description is copy somebody approved, so both outlive the extension that generated them. Uninstalling SEO Suite Pro leaves your catalogue exactly as it stands.
Five areas, five permissions. Each area screen is its own route with its own access and modify permission, so a user group can be given the wording screens without the sitemap schedule or the redirect rules. See requirements and install.
Nothing here is an audit of your search ranking. SEO Suite Pro generates, publishes and redirects. It does not score your pages, does not tell you what to write, and makes no claim about what any search engine will do with what it publishes.
URLs & meta¶
Generate never overwrites anything. An entity that already has a URL keyword is left exactly as it is, and the same is true per output for meta titles and descriptions: a product with a title somebody wrote and no description gets the description only.
Regenerate everything cannot be undone
Regenerate everything does overwrite, and it is the one destructive action in the suite. It is a second button behind a confirmation that spells out what it is about to do: every URL keyword, meta title and meta description in the shops and languages you ticked, including ones you set by hand and URLs a search engine has already indexed. There is no undo. A keyword it replaces is an address somebody may have linked to; take a database backup first, and read the confirmation rather than clicking past it.
Manufacturers get a URL keyword and nothing else. OpenCart holds no meta title and no meta description for a manufacturer in any 4.x release (there is no column and no table for them), so the screen offers no manufacturer meta field, and a run handed one drops it rather than counting work nobody could do. Giving brands meta text would mean this extension adding columns to a core table and rendering them itself, which is a different product from this one.
A generated meta value longer than a search result shows is written whole. Sixty characters of a title and a hundred and sixty of a description are about what is displayed; a longer one is written as it rendered and counted in the summary, so you can shorten the template. Cutting it here would end your sentence mid-word on your behalf. The one hard stop is the column itself, which holds 255 characters. Beyond that the value is kept to what fits, and it was already counted as over-length before it got there.
A keyword that something else already answers on is numbered, not skipped.
Two products rendering to the same string is ordinary, and a page with no URL is
not, so the second one becomes imac-2. "Already answers on" is judged the way
OpenCart's own resolver judges it, on the last segment, so mac and
desktops/mac count as the same URL.
One keyword step is generated per entity, including for a category.
OpenCart's own admin writes a hierarchical desktops/mac; a generated keyword is
the single step your template rendered. Put the hierarchy in the template
instead ([category_path] gives desktops-mac), because a keyword built out of
a parent's row would depend on the order the run happened to reach them in. Both
spellings resolve to the same page.
A name the transliteration table cannot render falls back to an identifier.
A product whose name is written in a script the table does not cover gets
product-241 rather than a blank keyword, which is a URL you can find and
replace rather than a page with no address.
Canonical tags on listing pages are off, and they replace rather than add. With the setting on, a paginated, sorted or filtered listing carries one canonical pointing at the listing's own URL. It replaces the one OpenCart emits, which points at the page you are on. That is the correction the setting exists to make. It is off on a fresh install because it is a search-visibility decision and not one an extension should make on a live shop the moment it is installed.
A run is a series of batches and can be interrupted. Two hundred entities per request by default, resumed from where it stopped. It resumes only when it is the same run: change a template, the scope, or the overwrite choice and it starts again from the beginning, because half a catalogue generated from one template and half from another is not a run anybody asked for. Re-running is safe either way, since what already has a value is skipped.
Markup¶
The markup is spliced into the rendered page, immediately before
</head>. OpenCart offers no document slot that could carry an Open Graph
tag, so there is no cleaner mechanism available. A page whose theme has replaced
core's header wholesale and emits no </head> comes back exactly as it was and
carries no markup at all. Emitting nothing is the only answer that cannot make
such a page worse.
Five page types can describe themselves, and no others. Products, categories, brand pages, information pages and the home page. Any other page (search results, the account pages, the checkout) carries nothing from this area, by design: the trigger is the route, so there is no route-sniffing in your storefront.
A brand page can say its name and its address and nothing else. OpenCart stores no description for a manufacturer and its controller passes no image, so there is nothing else on the page to say.
The price and the availability are read from what the page already computed. The price in the tag is the string the page prints, so the tag and the page cannot disagree about what a shopper would pay. The consequence is the limit: a price or a stock status the page's own controller did not resolve cannot be in the markup.
Product codes are read from the page where the page has them. With
Product codes and photographs on, OpenCart 4.1.0.3 and later put the
product's codes on the page, filtered to the identifiers you have switched on,
and that is what the Product block carries. On 4.0.2.x and 4.1.0.0 the page
has none, so they are read from the product's own row instead: one query, on
the product page only, and only while the switch is on. The photographs are
always the ones the page shows, so a picture whose file is gone is not listed.
A GTIN failing its check digit is left out. The GTIN is the first of the
EAN, the UPC and the JAN that is 8, 12, 13 or 14 digits with a valid GS1 check
digit, and only one is emitted. A code that does not add up identifies some
other product or none, so it is dropped rather than repaired. The same goes for
an ISBN that is not 13 digits or 10 ending in an optional X, and an MPN
longer than 70 characters. Codes you defined yourself under OpenCart 4.1's
identifiers are not emitted, because no search engine has a property for them.
The return policy says what you typed, and is checked against nothing.
OpenCart holds no return policy of its own to read, so the one on the home
page's Organization block is exactly as true as the Return policy fieldset.
It is stated once for the whole shop, never on each offer, and only while
Organization structured data is on, a kind of policy is chosen and at least one
valid country is given. Like every other setting, an upgrade keeps it, apart
from the one update out of 1.3.0 or earlier, after which the home page states no
return policy until you retype it.
No shipping and no condition are claimed. OpenCart prices shipping per address and per cart, so a rate typed into a settings screen is a price the checkout can contradict. OpenCart records no item condition either, and one typed for a whole catalogue is a claim about products nobody looked at.
What product details cost. Switched off, as they ship, nothing: the cost reference was measured that way. Switched on, the product page makes no extra query on OpenCart 4.1.0.3 and later and one on 4.0.2.x and 4.1.0.0, and grows by a few dozen bytes per code and by one URL per additional photograph. A return policy adds a few hundred bytes to the home page and nothing anywhere else.
A star rating is claimed only where there is one. A rating with no reviews behind it is a structured-data violation rather than an inaccuracy, and a search engine can drop the whole page's markup for it. So nothing is claimed when your store has reviews switched off, when nobody has reviewed the product, or when the average is off its own scale.
A stock status you named is reported as out of stock unless you say
otherwise. OpenCart reaches for a named status, such as 2-3 Days or
Pre-Order, precisely when there is none to sell, so that is how it is reported. If you sell
ahead of stock, name the statuses on the settings screen. What the markup cannot
say is when there will be stock, because nothing on the page says either.
A family with nothing to say emits nothing. A block holding only the site's
name and the page type tells a scraper less than the <title> it would
otherwise have fallen back to.
Language alternates need keywords, and stop at three refusals. Nothing is
emitted on a single-language store, nothing on a page carrying noindex, and
nothing at all where the set cannot be made to agree with the page's own
canonical. A language in which an entity has no keyword is left out of the group
rather than linked at a query-string URL, which would point a crawler at the very
duplicate the set exists to resolve.
sku is your product's model. It is the nearest thing a core product has to
a stock keeping unit: OpenCart's own sku field is one of several optional codes
most catalogues leave empty, while the model is required by the admin form.
Alt text¶
Only the alt attribute is rewritten. The title keeps the product's name,
because a tooltip naming the product is what a sighted shopper is being told
there.
A theme this extension cannot read gets nothing, and the screen says so. The rewrite works on the template's own source, and a template whose image markup it cannot match is left exactly alone. Mislabelling an image is worse than not improving it, and the person who pays for the difference uses a screen reader. So the screen tells you the rewrite did not apply rather than leaving you to wonder why a page never changed.
On a product page it is all the images or none of them. OpenCart builds the additional images as a list that carries no identifiers and silently skips any whose file is missing from disk. Where the stored rows cannot be lined up against what the page actually renders, nothing is emitted for that product and the page keeps core's behaviour, main image included, because a page where some photographs are described and the rest silently are not is one you cannot tell from a working one.
Words somebody typed are never overwritten by a run, and an image nobody has described keeps core's fallback of the product name. Describing one photograph of five costs the other four nothing.
A pattern that renders to nothing writes nothing. alt="" is not the absence
of a description: it tells a screen reader the image is decoration and to skip
it, so it is worse than the product name core would have repeated.
Your pattern has to be able to tell one photograph from the next.
{position} is the only value that differs between two images of one product
({count} is the same on all of them), so a pattern without it writes the same
words onto every image, which is core's behaviour with extra steps. The screen
warns you before the run rather than after it, and does not stop you.
A description belongs to a photograph, not to a product. It is keyed by product, image path and language, so replacing a product's main image is a new key and not an edited one: the words described the photograph, and the photograph is gone. The card falls back to core's behaviour rather than describing a picture nobody is looking at. A product sold in two shops is described once, exactly as its name is.
The fields on core's product form rest on three anchors in a core template. If a future OpenCart release rewrites that markup the fields silently do not appear. The form still renders, still saves, and every description already written is still there and still on the storefront. It degrades to missing rather than to broken, and it is asserted on every release we test against, so a new one is caught here rather than by you.
A save that carries no such fields changes nothing. An import, an API call or a form rendered while the area was off writes nothing rather than clearing what the product had.
Redirects¶
Broken URLs are recorded only while OpenCart's own SEO URLs are on. With them off, core looks no keyword up, and re-resolving anyway would log every URL in your store as broken. Serving a redirect does not depend on that setting, because a rule is a row somebody typed about one exact path.
Three answers, and you pick per rule. 301 permanent, which is the default and the one a search engine acts on; 302 temporary, which is there because a browser caches a 301 and will not ask again for months; and 410 gone, which has no target and is for a page with no successor.
A target must be somewhere on the web or somewhere on this store. Anything
that is not http, https or a path here is refused, on the form and on an
import alike. A redirect that ran somebody else's script in a shopper's browser
would not be a redirect.
Chains are flattened when you save, not when a shopper follows one. So the storefront's answer is always one row and one hop, and a loop is something you are told about while you are still looking at the form that would have caused it. Saving also re-points whatever pointed at the path you are saving.
Capturing renamed slugs is off, and a captured rule is a guess. With it on, a slug you rename in the admin gets a 301 from the old URL to the new one, and a deleted page's URL joins the list of broken ones at no hits, since there is nothing this extension could choose on your behalf for a page that is gone. It is off by default because a store reorganising its catalogue over a weekend would otherwise collect a redirect for every slug it touched. Captured rules are badged, so you can recognise a guess before you delete it.
A decision you made stands. A path you ignored, or gave somewhere to go by hand, is never overwritten by a capture. Nothing is written while the old keyword still resolves, either: two entities swapping slugs, or a second language still spelling it the same way, look exactly like a rename from the outside.
A capture that cannot be made safely is silent. It writes nothing rather than raising a refusal on a product save nobody is looking at. The URL is still broken, so it is recorded as a miss the first time anybody asks for it.
The list of recorded misses has a ceiling, 1,000 rows by default, and the URLs nobody has asked for in the longest are dropped first, however many hits they had. A redirect, or a URL you ignored, is never dropped by it.
The list downloads whole, and a mapped row imports back as it stands. The
screen shows the 20 most-hit; Download CSV writes every URL still on the
list, for every store, up to the ceiling above. Its first four columns are the
import's own, store_id,path,target,response, with target and response
left empty, so a row you fill in imports without being rearranged. A row you
leave empty is refused by the import like any other incomplete row, so delete
the ones you did not map first. The path is written with a leading /, which
the import trims, and a referrer that begins with =, +, -, @, a tab or
a carriage return is written behind a ' so a spreadsheet shows it rather than
runs it. The file is available to anyone who can open the Redirects screen.
What is recorded about a visitor is a sample referrer and nothing else: no IP address, no customer, no session, no user agent, and a hit count rather than a row per visit. Written out in full on what this holds about a person.
XML sitemap¶
The suite owns the block between its own two markers in robots.txt and
treats every other byte as yours. Core's shipped rules and anything you or
another extension added are kept identically, not merely equivalently, and
the block is replaced where it stands rather than moved to the end. Switching the
area off, or clearing your rules, removes the block rather than writing an empty
one.
Uninstalling gives the file back by removing our block, not by restoring a
copy. A Disallow you added a month after installing would be destroyed by an
uninstall that reinstated a month-old snapshot, which is the one thing this area
exists not to do. A robots.txt this extension created and nobody else has
written in is removed; once somebody else has written in it, it is yours and it
stays.
A web root this extension cannot write to is reported, not worked around.
There is no dynamic route serving robots.txt and there cannot be one: your web
server answers for a file that exists and OpenCart never sees the request, so a
route would only ever be reached on a store where the file is missing, which is
the one case where a route is no use either. The screen names which of the three
problems it is, with the path.
A page with no URL keyword is left out of the sitemap, and so is anything your store does not display. A sitemap advertising a page that 404s is worse than one that omits it.
Only products carry a <lastmod>. Manufacturers and information pages have
never had a date column in OpenCart 4, and categories lost theirs inside the
range of releases this runs on. A date is left out rather than substituted,
because <lastmod> is a claim a crawler acts on, and today's date on every
category would be a wrong one every day.
Photographs are listed only for products, only for files that exist, and as
the original file. With Each product's photographs on, a product's URL
lists its main image and then its additional ones, in the order the product page
shows them, up to Google's limit of 1,000. A path whose file is not in your image
directory is left out, so no listed image is a 404. Each is the file you
uploaded under /image/, never a resized copy from image/cache/, which exists
only once some page has asked for that size. Categories and manufacturers list
none, and nothing but the location is written: Google no longer reads a
caption, title, location or licence. The photographs count toward the 50MB
limit below, so a catalogue with many of them splits into more files.
A Disallow you wrote over /image/ in robots.txt is not checked against
the sitemap. If your crawl rules block your image directory, the sitemap still
lists the photographs, and a crawler that honours the rule will not fetch them.
A catalogue past 50,000 URLs or 50MB is published as several files with an index over them. A crawler handed one file over either limit rejects the file rather than the entries past it, so the split happens before the limit is reached.
Sitemaps are removed when you uninstall. Unlike your crawl rules, they are entirely ours: documents nobody can regenerate once the extension is gone, listing URLs that go out of date from the day they are left behind.
Limits on unattended regeneration¶
Nothing regenerates on its own unless your store's cron is running. The suite
registers one hourly job on OpenCart's own scheduler and asks the cycle you chose
whether a run is due; if your host never calls OpenCart's cron.php, that job
never fires and your sitemap stays as the last run left it. This is not something
the extension can detect for you: a scheduler that is never called looks exactly
like a cycle that has not come round.
On OpenCart 4.1.0.4 it cannot fire at all, and that is OpenCart's bug rather
than this extension's: cron.php crashes inside OpenCart before any extension is
reached, so every scheduled task on that store is dead, ours and everybody
else's. See
4.1.0.4.
The settings screen names the release and says what it has cost you, and on that
release it stops telling you that no scheduled run has happened yet, because the
answer is not one you could act on.
What you get instead is a command, documented on the
install page: a crontab line
running php extension/seo/seo.php from your store's directory regenerates on
whatever timing you give it, and exits non-zero when it could not.
There is deliberately no web address to paste instead, which is the one thing on this page you may want and will not get. A guarded URL is what an extension offers a merchant with no shell, and it costs a secret that then lives in a host's control panel, in a browser history and in a server log. For work that sends something, such as a mail a customer is waiting for, that trade is worth making. For a sitemap it is not: a document that regenerates late costs nothing a crawler notices, and the next run makes it up in full. If you have no shell, Generate on the settings screen writes the same document, and nothing else about the suite depends on the schedule.
Both doors write the same record. A run driven from a crontab line reports on the settings screen exactly as a scheduled one does, and writes the same line into the diagnostic log, so there is one place to look either way.
Language¶
The screens are English, and one sentence a shopper meets is too. SEO Suite Pro declares English and only English, so every screen reads as English whatever language your admin is in. The suite's own shopper-facing text is a single string: the shipped wording an image description is generated from, which is why it lives with the sentences a shopper reads even though the only screen that shows it is in the admin. It is a placeholder: the moment you write your own pattern, per language, what reaches a screen reader is your words and not ours.
How translations are counted
How a translation is counted, and what is served for a string nobody has read, is on the shared language promise page.
Everything else the suite publishes is addresses, not prose. A sitemap is a
list of URLs, the markup carries text your own catalogue already holds in the
language the page is in, and the only English in robots.txt is the comment
marking where our block begins and ends, addressed to whoever opens that file,
which is you.
Your crawl rules are yours, and none of this reaches them. The Disallow and
Allow lines you typed are your store's own, kept outside our block, and an
update leaves them where they are, and so does uninstalling. Editing our .php
files under extension/ is a different act: an update replaces those files and
takes your change with them.
OpenCart 4.1.0.1 and 4.1.0.2: no language can be added¶
On those two releases OpenCart's own Add Language screen answers You have already added this language! for a language nobody has added, whatever code you give it. Its check is missing a pair of brackets, so the condition is true for every code the store has not seen before.
- What that costs here is hreflang. The alternates SEO Suite Pro writes name the same page in every language your store sells in, so a store that cannot be given a second language has nothing for them to name. On a fresh 4.1.0.1 or 4.1.0.2 store the feature has nothing to do.
- A store that already had its languages is unaffected. Upgrading into one of those releases keeps every language you had, and the alternates work exactly as Guides describes.
- Everything else is whole either way: keywords, meta, redirects, the XML sitemap, the alt text and every screen.
- Editing an existing language still works. It is only adding a new one that is refused.
It is OpenCart's, not ours: the same save on a bare 4.1.0.1 or 4.1.0.2 store with nothing installed is refused the same way. Nothing an extension can repair, which is why neither release is one of the releases SEO Suite Pro claims. OpenCart fixed it in 4.1.0.3; upgrading to that or newer is the whole of the remedy, and the Markup screen says so while you are on either.
Anything not described here¶
Assume it is absent and ask before you buy. A boundary you find before the purchase is a sale we did not want; found afterwards, it is a refund and a review.