> ## Documentation Index
> Fetch the complete documentation index at: https://dokumentation.websale.de/llms.txt
> Use this file to discover all available pages before exploring further.

# SEO content for products

> Additional SEO fields per product, maintained in the TopRank Manager or directly in the shop, including a descriptive product name for URL and canonical.

Product data usually comes from the merchandise management system, and nobody there thinks about meta titles and meta descriptions. This is exactly the gap that the "SEO content for products" feature closes: the person responsible for SEO maintains additional fields for each product, without access to the merchandise management system and without opening the WSPManager.

In addition, the feature provides the descriptive product name for the address. Instead of `?websale8=ihr-shop&pi=100419&ci=2012054`, the address bar then shows, for example, `/Leinen/Trainingsleine-camo`.

> [!NOTE]
>
> The feature works independently of SEO URLs. You do not need SEO URLs to use SEO content. If you are planning both, it is better to convert them together, otherwise new addresses are created twice in a row, and with them two rounds of redirects.

## What is available

There are 21 fields per product:

| Field | Length | Purpose |
| - | - | - |
| SEO product name | maximum 127 characters | descriptive name in address and canonical, must be unique in the shop |
| SEO field 1 to 20 | maximum 4000 characters | free content that is output anywhere in the template |

Where the fields end up is decided solely by your template. Common practice is field 1 for the title, field 2 for the meta description and field 3 for keywords. Clarify the assignment with the person responsible for SEO in advance, because afterwards it can only be changed with effort.

## Two ways of maintenance

| | TopRank Manager | SEO mode in the shop |
| - | - | - |
| Where | Online service area, menu item "SEO content for products" | Product detail view of the shop, called with `?seomode=on` |
| How | Upload, download, edit and re-upload a data table | Fill in input fields directly on the product |
| Strength | many products at once, across subshops | result immediately visible on the product |
| Setup effort | low, no additional template | higher, a separate template and a module |

Both ways write to the same fields. You can therefore pre-fill in the TopRank Manager and refine in SEO mode.

> [!NOTE]
>
> Connected subshops automatically receive SEO content from the main subshop, just like product data. Maintenance therefore always takes place in the main subshop. If someone calls SEO mode in a connected subshop, they receive a notice telling them in which shop they need to work.

## The descriptive product name

If the SEO product name is filled in and `Link-Allow` is set to `yes`, it is used in the address and in the canonical. All programs that pass product addresses to the outside, that is FeedEngine, TopRank and others, then use it as well.

> [!WARNING]
>
> The SEO product name must be unique in the shop. If it is already assigned to another product, the entry is rejected with an error message.

> [!NOTE]
>
> If SEO URLs are active, a changed SEO product name only takes effect after manual generation in the TopRank Manager. This happens automatically with every import and every time a data feed is generated.

Guide: Generating a canonical instruction in the head · SEO URLs and link optimization

## Setup

Which steps apply to you depends on the chosen maintenance method:

| Step | TopRank Manager | SEO mode |
| - | - | - |
| 1 Activation | required | only for WEBSALE tags in the fields |
| 2 `SEOProductFields` section | yes | yes, with additional parameters |
| 3 Error texts | not applicable | yes |
| 4 Template for the input fields | not applicable | yes |
| 5 Integrate module | not applicable | yes |
| 6 Output fields in the template | yes | yes |
| 7 JavaScript library | not applicable | yes |
| 8 Testing | yes | yes |

### Step 1: Activation

Maintenance in the TopRank Manager must be activated by WEBSALE. This is not necessary for SEO mode.

> [!WARNING]
>
> If you want to use WEBSALE tags in the SEO product fields, a written order and a technical activation are required in both cases. The reason is liability: otherwise, executable template code would get into the shop via the fields without having gone through the design.

### Step 2: SEOProductFields section in the shop.config

```ini theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
<SEOProductFields>
   Allow        = yes
   Link-Allow   = yes
   Template     = incl_product_seo.htm
      <Password>
         +Entry = ...
         +Entry = ...
      </Password>
</SEOProductFields>
```

| Parameter | Meaning |
| - | - |
| `Allow` | switches the function on or off as a whole |
| `Link-Allow` | determines whether the SEO product name is used for address and canonical |
| `Template` | name of the template with the input fields, only for SEO mode |
| `<Password>` with `+Entry` | one password each for calling SEO mode, only for SEO mode |

For maintenance in the TopRank Manager, `Allow` and `Link-Allow` are sufficient.

> [!NOTE]
>
> Multiple passwords make sense if several people work with SEO mode. If you remove an `+Entry`, access via this password is closed immediately. After three incorrect entries, SEO mode is locked for five minutes.

> [!NOTE]
>
> SEO mode requires cookies in the browser and always runs encrypted. If you append `?seomode=on` to an unencrypted address, the shop itself redirects to the encrypted address. Every login to SEO mode is logged.

Reference: SEOProductFields section

### Step 3: Define error texts

Only for SEO mode. The texts appear in the input form when an entry cannot be accepted.

```ini theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
<SEOProductFields-Text>
   ErrorNameTooLong         = Der SEO-Produktname darf maximal 127 Zeichen lang sein. Bitte korrigieren Sie Ihre Eingabe.
   ErrorFieldTooLong        = Ein Eintrag darf maximal aus 4000 Zeichen bestehen. Bitte korrigieren Sie Ihre Eingabe.
   ErrorNameAlreadyExists   = Der SEO-Produktname wird bereits für ein anderes Produkt verwendet. Bitte korrigieren Sie Ihre Eingabe.
   ErrorConnectedSubshop    = In diesem Subshop können keine Produkte optimiert werden. Bitte wechseln Sie in den Haupt-Subshop.
   ErrorMainCatIndexTooLong = Der Hauptkategorieindex ist zu lang (maximal 255 Zeichen).
</SEOProductFields-Text>
```

Reference: SEOProductFields-Text section

### Step 4: Create the template for the input fields

Only for SEO mode. The input fields are not located directly on the product detail view but in a separate template. Create it under the name you entered after `Template` in step 2, and save it in your template directory.

The following example shows five of the twenty fields. Add the others following the same pattern.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{WS-Include}
  {!E-PSEO_Connected_Subshop}

    <div id="SeoProductError">
      {ST-Ajax}<WS-Ajax-SeoProductError>{/ST-Ajax}
        {E-PSEO_Error}
          <p>Es ist ein Fehler aufgetreten.</p>
          <p>~E-PSEO_Error~</p>
        {/E-PSEO_Error}
      {ST-Ajax}</WS-Ajax-SeoProductError>{/ST-Ajax}
    </div>

    <div id="SeoProductSuccess">
      {ST-Ajax}<WS-Ajax-SeoProductSuccess>{/ST-Ajax}
        {E-PSEO_Success}
          <p>Ihre Eingaben wurden gespeichert.</p>
        {/E-PSEO_Success}
      {ST-Ajax}</WS-Ajax-SeoProductSuccess>{/ST-Ajax}
    </div>

    <form class="seoform" action="/websale8/" method="post" id="SeoFormEdit">
      <input type="hidden" name="Ctx" value="~WS-CTX~">
      <input type="hidden" name="pi" value="~PR-ProdIndex~">
      <input type="hidden" name="~E-PSEO_Action_Save~" value="true">

      <div id="SEOFields">
        {ST-Ajax}<WS-Ajax-SEOFields>{/ST-Ajax}

        Aktuelle Adresse des Produktes:<br>
        ~PR-Link~

        <label for="~E-PSEO_Name_Input~">SEO-Produktname für den sprechenden Namen in der Adresse (maximal 127 Zeichen):</label>
        <input type="text" name="~E-PSEO_Name_Input~" value="~E-PSEO_Name~">
        {E-PSEO_Error_Name}~E-PSEO_Error_Name~{/E-PSEO_Error_Name}

        <label for="~E-PSEO_MainCatIndex_Input~">Hauptkategorieindex (maximal 255 Zeichen):</label>
        <input type="text" name="~E-PSEO_MainCatIndex_Input~" value="~E-PSEO_MainCatIndex~">
        <input type="hidden" value="yes" name="~E-PSEO_MainCatIndex_Hidden~">
        {E-PSEO_Error_MainCatIndex}~E-PSEO_Error_MainCatIndex~{/E-PSEO_Error_MainCatIndex}

        <label for="~E-PSEO_Field1_Input~">SEO-Feld 1 (maximal 4000 Zeichen), beispielsweise für den Meta-Titel:</label>
        <textarea name="~E-PSEO_Field1_Input~">~E-PSEO_Field1~</textarea>
        <input type="hidden" value="yes" name="~E-PSEO_Field1_Hidden~">
        {E-PSEO_Error_Field1}~E-PSEO_Error_Field1~{/E-PSEO_Error_Field1}

        <label for="~E-PSEO_Field2_Input~">SEO-Feld 2 (maximal 4000 Zeichen), beispielsweise für die Meta-Beschreibung:</label>
        <textarea name="~E-PSEO_Field2_Input~">~E-PSEO_Field2~</textarea>
        <input type="hidden" value="yes" name="~E-PSEO_Field2_Hidden~">
        {E-PSEO_Error_Field2}~E-PSEO_Error_Field2~{/E-PSEO_Error_Field2}

        <input type="submit" value="Eingaben speichern"
               onclick="return ws_AJAXsendFormTemplate('SeoFormEdit', '~WS-Charset~', seoStartFunction, seoFailureFunction, 'seoSuccessCallback()', 'seoErrorCallback()')">

        {ST-Ajax}</WS-Ajax-SEOFields>{/ST-Ajax}
      </div>
    </form>

    <form class="seoform" action="/websale8/" method="post" id="SeoFormReset">
      <input type="hidden" name="Ctx" value="~WS-CTX~">
      <input type="hidden" name="pi" value="~PR-ProdIndex~">
      <input type="hidden" name="~E-PSEO_Action_Reset~" value="true">
      <input type="submit" value="Eingaben zurücksetzen" name="~E-PSEO_Action_Reset~"
             onclick="return ws_AJAXsendFormTemplate('SeoFormReset', '~WS-Charset~', seoStartFunction, seoFailureFunction, 'seoSuccessCallback()', 'seoErrorCallback()')">
    </form>

  {/!E-PSEO_Connected_Subshop}

  {E-PSEO_Connected_Subshop}
    In diesem Subshop können Sie keinen SEO-Content bearbeiten.
    Bitte nehmen Sie Ihre Änderungen im Subshop ~E-PSEO_Connected_Subshop~ vor.

    SEO-Produktname: {E-PSEO_Name}~E-PSEO_Name~{/E-PSEO_Name}{!E-PSEO_Name}Keine Angaben{/!E-PSEO_Name}
    SEO-Feld 1: {E-PSEO_Field1}~E-PSEO_Field1~{/E-PSEO_Field1}{!E-PSEO_Field1}Keine Angaben{/!E-PSEO_Field1}
    SEO-Feld 2: {E-PSEO_Field2}~E-PSEO_Field2~{/E-PSEO_Field2}{!E-PSEO_Field2}Keine Angaben{/!E-PSEO_Field2}
  {/E-PSEO_Connected_Subshop}

{/WS-Include}
```

> [!NOTE]
>
> Each field has an associated hidden field `~E-PSEO_Field…_Hidden~`. Without this field, clearing the text field is not applied when saving.

#### What happens after saving

In both cases, saving is done via AJAX. The difference lies in what becomes visible afterwards:

| Variant | Behavior | Change to the example |
| - | - | - |
| Reload page | the entire product page is rebuilt, all entries are immediately visible at their target location | the `ST-Ajax` brackets around the success and error area are dropped; instead, the success area sets `document.location.href` to `~PR-Link~` via JavaScript |
| Only update the AJAX areas | only the bracketed areas are renewed, the page stays as it is | the example above, unchanged |

In both cases, the product address is only rebuilt after manual generation in the TopRank Manager.

> [!NOTE]
>
> If, after saving, you want to see the entered WEBSALE tags for checking purposes instead of their result, bracket the input field with `WS-NoParsing`:
>
> ```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
> <textarea name="~E-PSEO_Field1_Input~">{WS-NoParsing}~E-PSEO_Field1~{/WS-NoParsing}</textarea>
> ```

Reference: Tags for "SEO content for products" (E-PSEO\_) · PR-Link() · PR-ProdIndex · WS-Ajax-"ID" · WS-CTX · ST-Ajax · WS-NoParsing

### Step 5: Integrate the module on the product detail view

Only for SEO mode. The `WS-Module(product_seo)` tag integrates the template you just created.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{ST-SeoMode}
   ~WS-Module(product_seo)~
{/ST-SeoMode}
```

> [!WARNING]
>
> The `ST-SeoMode` bracket is mandatory. Without it, your customers also see the input fields.

Reference: ST-SeoMode · WS-Module()

### Step 6: Output the fields in the template

This step applies to both maintenance methods. Output is done with `PR-SeoFreeField1` to `PR-SeoFreeField20`. A fallback to the existing product data is useful as long as a field is empty.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
<head>
   ...
   <title>{PR-SeoFreeField1}~PR-SeoFreeField1~{/PR-SeoFreeField1}{!PR-SeoFreeField1}~PR-Name_StripHtml~{/!PR-SeoFreeField1}</title>
   <meta name="description" content="{PR-SeoFreeField2}~PR-SeoFreeField2~{/PR-SeoFreeField2}{!PR-SeoFreeField2}~PR-Descr_StripHtml~{/!PR-SeoFreeField2}">
   ...
</head>
```

> [!NOTE]
>
> To try it out, you can initially bracket the output in `ST-DesignTest`. Then only you, in design test mode, see what the fields deliver.
>
> ```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
> {ST-DesignTest}{PR-SeoFreeField1}~PR-SeoFreeField1~{/PR-SeoFreeField1}{/ST-DesignTest}
> ```

Reference: PR-SeoFreeField1…20 · PR-Name\_StripHtml · PR-ShortDescr\_StripHtml · ST-DesignTest

### Step 7: JavaScript library and callback functions

Only for SEO mode. The form submits via `ws_AJAXsendFormTemplate`, so the WEBSALE system library and the design component must be integrated on the product detail view, at least in version 1.8.1.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
<head>
   ...
   <script type="text/javascript" src="/$WS/ws_sysdata/js/ws-system-1.8.3.js"></script>
   <script type="text/javascript" src="../ihr-verzeichnis/ws-design-1.8.3.js"></script>
   ...
</head>
```

Add the four functions named in the `onclick` of the forms to the design component. They may remain empty but must be present.

```js theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
// vor dem Absenden, beispielsweise für eine Ladeanzeige
function seoStartFunction() {}

// nach erfolgreichem Speichern
function seoSuccessCallback() {}

// nach einem fachlichen Fehler beim Speichern
function seoErrorCallback() {}

// wenn die AJAX-Verbindung selbst fehlschlug
function seoFailureFunction() {}
```

Materials: WEBSALE JavaScript library

### Step 8: Testing

For maintenance in the TopRank Manager: create a data table with SEO content for a few products, upload it and check the output in the shop.

For maintenance in SEO mode:

1. Call a product with `?seomode=on` and log in with one of the stored passwords.
2. Check whether all desired input fields appear.
3. Enter content, save and check whether the entries are still in the fields afterwards.
4. Exit SEO mode with `?seomode=off`.

In both cases, finally:

1. If you have bracketed the output in `ST-DesignTest`, call the product with `?test=on&design-test=on`.
2. Check whether the content is in the intended places and whether the descriptive name appears in the address.
3. Remove the `ST-DesignTest` brackets before passing access on to the people responsible for SEO.

Guide: Switching the shop's test modes on and off

## Maintaining SEO content

### In the TopRank Manager

The list is created with tab-separated columns. The assignment to the product is made via the product index, the assignment to the subshop via the subshop ID. Using the download function, you retrieve the existing state, edit or delete it and upload it again. The exact structure of the table is described in the TopRank Manager itself.

Guide: Configuring and activating TopRank

### In SEO mode

Append `?seomode=on` to any shop call. After entering the password, the shop jumps back to the page originally called and from now on shows the input fields for every product. You exit the mode again with `?seomode=off`.

Without active SEO URLs, saved changes take effect immediately. With active SEO URLs, a changed SEO product name only takes effect after generation in the TopRank Manager.

## Related topics

* Guide: SEO URLs and link optimization
* Guide: Generating a canonical instruction in the head
* Guide: Configuring and activating TopRank


## Related topics

- [SEO optimization with TopRank](https://dokumentation.websale.de/en/v8s/seo/seo-optimierung-mit-toprank.md)
- [E-PSEO_ (SEO content for products)](https://dokumentation.websale.de/en/v8s/referenz/tags/e-pseo.md)
- [SEO URLs and link optimization](https://dokumentation.websale.de/en/v8s/seo/seo-urls-und-link-optimierung.md)
- [Basics and technology](https://dokumentation.websale.de/en/v8s/referenz/konfigurationen/shopkonfiguration-shop-config/grundlagen-und-technik.md)
- [WEBSALE JavaScript library](https://dokumentation.websale.de/en/v8s/materialien/websale-javascript-bibliothek.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.