LW IT Solutions
« Blog Overview /WordPress Plugins & Tricks / How the Stats4U WordPress plugin works: counter...
This post in other languages:

How the Stats4U WordPress plugin works: counter code, placement, consent and requests

How the Stats4U WordPress plugin works: counter code, placement, consent and requests
Contents
  1. From pasted code to counter
  2. Right below the footer of any theme
  3. Only after consent
  4. What the browser sends to stats4u.net
  5. Multilingual and without full reloads
  6. Through the WordPress.org review
  7. All links
  8. Questions and answers
  9. Sources

Stats4U is a visitor counter by LW IT Solutions: a small image on the website that counts page views, plus statistics on stats4u.net with where visitors came from, time on page, scroll depth and the visitors online right now. No account is needed; the counter number is enough. For WordPress there is a dedicated plugin. It has been listed in the WordPress.org plugin directory since 29 September 2026, in version 1.6.0, and its source code is public on GitHub.

This post shows how the plugin works: how it reads a pasted counter code, how it places the counter right below the footer of any theme, how it waits for a consent tool, and what the browser then sends to stats4u.net. Everything was measured again on 2 October 2026 on a local test installation with WordPress 7.1.2, PHP 8.4 and Twenty Twenty-Five, using the plugin from the directory. The end covers the way through the WordPress.org review and lists all links.

From pasted code to counter

Under “Settings → Stats4U”, a wizard leads through the setup in five steps: counter, appearance, placement, consent, done. The custom configuration offers all settings on one page instead. The first step takes the code that the creator on stats4u.net produces. There are three ways to get it: an existing code, a button that opens the creator and brings the code back at its end, or a new counter number on request. The last two run from the administrator’s browser, not from the server.

The creator knows several formats, and the plugin accepts all of them. The table shows what stats4u_code_lesen() made of twelve inputs:

Pasted Result
HTML, image with link number, design, dark mode and size 150%
BBCode number, design, dark mode
Markdown number, design, language German
script code with s4u.js number, design, dark mode, counting “unique visitors”, language Polish
image address alone number and design
old long form ?action=pic&s4uid=… number and design
the number only, also with # number, design unchanged
address of the statistics page /live/<number> number
statistics page with a custom name note: paste the code or the number
globe widget globe.js note: the plugin shows image counters
any other text note: code not recognised

What the page then carries is no longer the image alone but the service’s official script, the same <script src="https://www.stats4u.net/s4u.js" data-id="…" async> that the creator produces. Up to version 1.4.0, the plugin placed only the image. That was lighter, but with an image from another server a browser sends only the site’s domain, neither the page nor where the visitor came from. Only the script delivers these details, plus time on page, scroll depth and “online now”. A single function decides what stands at the counter’s place:

// stats4u.php (excerpt)
function stats4u_zaehler_teile($e) {
    $daten = stats4u_skript_daten($e);
    if (stats4u_cmp($e) === null) {
        return array('skript' => array('src' => STATS4U_SKRIPT, 'async' => true) + $daten);
    }
    stats4u_wartet(true);
    return array('platzhalter' => $daten);
}

function stats4u_html() {
    $e = stats4u_einstellungen();
    if ($e['id'] === '') { return ''; }
    $t = stats4u_zaehler_teile($e);
    if (isset($t['skript'])) {
        return '<span class="stats4u-zaehler">' . trim(wp_get_script_tag($t['skript'])) . '</span>';
    }
    return '<span class="stats4u-zaehler" data-stats4u="' . esc_attr(wp_json_encode($t['platzhalter'])) . '"></span>';
}

Without a consent tool, this is a script tag built with wp_get_script_tag(), which escapes every attribute. With a tool, it is an empty placeholder that carries the counter’s settings as JSON. More on that below.

Right below the footer of any theme

By default, the counter appears directly below the theme’s footer. That sounds simple, but WordPress has no hook “after the footer”. The usual hook, wp_footer, sits just before </body>. Up to version 1.3.0 the counter hung there, and on a page whose body is laid out as a flexbox it ended up as a column beside the content, halfway down. Since 1.3.1, the plugin buffers the output from the end of wp_head, looks for the footer in the buffer and places the counter behind it. An element counts as the footer if it identifies itself as one:

// stats4u.php (excerpt)
function stats4u_ist_seitenfuss($name, $tag) {
    if (preg_match('/(?<![\w-])role\s*=\s*["\']?contentinfo\b/i', $tag)) { return true; }
    if (preg_match('/(?<![\w-])itemtype\s*=\s*["\']?[^"\'>]*WPFooter/i', $tag)) { return true; }
    $namen = array('colophon', 'footer', 'site-footer', 'main-footer', 'page-footer', 'global-footer',
                   'elementor-location-footer');
    if ($name === 'footer') { $namen[] = 'wp-block-template-part'; }
    foreach (array('id', 'class') as $attr) {
        if (preg_match('/(?<![\w-])' . $attr . '\s*=\s*(?:"([^"]*)"|\'([^\']*)\'|([^\s>]+))/i', $tag, $m)) {
            $wert = strtolower(($m[1] ?? '') . ($m[2] ?? '') . ($m[3] ?? ''));
            if (array_intersect(preg_split('/\s+/', trim($wert)), $namen)) { return true; }
        }
    }
    return false;
}

Among several candidates, the last outermost one wins, so that with themes that wrap a <footer> in another footer area the counter sits below the whole bar. Without a recognisable footer, the counter goes to the end of the page. Until the script has drawn the image, a CSS rule with :has() hides the empty box, so no margin is left as a gap. Since version 1.6.0, the callback of the output buffer does this work; the plugin no longer prints the buffered page itself. That change came from the WordPress.org review.

On the test installation, the counter landed in the described place for every setting. “Below the footer” put it directly after </footer>, “inside the footer” just before it, “at the end of the page” before the final scripts. “Below the content” put it on single pages only, not on the front page. The corners fixed it at the bottom right or left, and “nowhere automatically” left it out. For custom places there are the block “Stats4U counter” and the shortcode , in a template as do_shortcode( '' ). Block and shortcode on the same page gave two counters, the block with its own alignment.

Only after consent

A site with a consent tool selects it in the fourth step. The list has 27 entries: 25 tools, CookieYes additionally in its legacy mode, and the WP Consent API, plus any tool that blocks scripts through attributes of its own. The counter’s place then holds an empty span with the settings in data-stats4u. At the end of the page sits a small release script that the tool runs only after consent and that then puts the official s4u.js into every placeholder. Measured, it looked like this:

Tool Release script in the HTML
Complianz <script type="text/plain" data-category="statistics">
Cookiebot <script type="text/plain" data-cookieconsent="statistics">
consented.eu <script type="text/plain" data-consented="stats4u">
CookieYes <script data-cookieyes="cookieyes-analytics" src="…/freigabe.js">
WP Consent API inline script that asks wp_has_consent() and listens for changes

In none of these cases did s4u.js appear directly in the HTML. For tools that release only scripts with an address of their own, such as CookieYes, the plugin includes the file freigabe.js. With the WP Consent API, the script first checks whether a consent type is set at all, because without a type wp_has_consent() returns true even after a refusal. For a tool that is not in the list, custom attributes can be entered. Only type, class and data-* get through:

// stats4u.php (excerpt)
function stats4u_eigene_attribute($roh) {
    $aus = array('type' => 'text/plain');
    if (preg_match_all('/([a-zA-Z][a-zA-Z0-9_.:-]*)\s*=\s*(?:"([^"]*)"|\'([^\']*)\'|([^\s"\'>]+))/', (string) $roh, $m, PREG_SET_ORDER)) {
        foreach ($m as $a) {
            $name = strtolower($a[1]);
            if (!preg_match('/^(data-[a-z0-9_.:-]+|class|type)$/', $name)) { continue; }
            $wert = $a[2] !== '' ? $a[2] : ((isset($a[3]) && $a[3] !== '') ? $a[3] : ($a[4] ?? ''));
            $aus[$name] = substr(preg_replace('/[^A-Za-z0-9 _.:\/,#-]/', '', $wert), 0, 80);
        }
    }
    return $aus;
}

An entered onload="alert(1)" was missing from the output; what remained was type="text/plain" and data-category="statistics".

From pasted code to counted visit
The Stats4U plugin places the official counter script where chosen; counting happens in the browser, and the WordPress server never contacts stats4u.net

What the browser sends to stats4u.net

For this measurement, a Chrome with a fresh profile paused every request. Only the script file s4u.js itself was allowed to load. Every other request to stats4u.net was logged and then aborted, so no test count reached the service. The page stayed open for 70 seconds, then the browser navigated away.

When Request Content
on load s4u.js 13,237 bytes transferred, Brotli, cached for an hour, no Set-Cookie
on load action=cfg, GET the counter number only: the counter’s settings
on load action=pic, image counter number, design, dark mode, language, page address, referrer and a random number against caching; this request counts
after 60 s action=ping, POST the counter number only, for “online now”
on leaving action=puls, beacon counter number, page address, visible seconds, scroll depth and load time

After loading, the browser held no cookies and nothing in local storage. The script turned the counter into a link to its statistics page, https://www.stats4u.net/live/<number>. The service sets this link, not the plugin; a counter set to “not public” on stats4u.net links to the service’s home page. The WordPress server itself never contacts stats4u.net: the plugin’s code contains not a single HTTP call. The preview on the settings page and in the editor carries the parameter rl=1 and does not count.

Multilingual and without full reloads

When the counter’s language is set to “the language of each page”, the plugin takes it from the locale of each request. With Polylang, the counter carried data-lang="en" on the English front page, de on the German one and pl on the Polish one. Themes that switch pages without a full reload count only the first page unless they help. For them there is the event s4u:seiteweg, which closes the time on the old page, and, after consent, the function window.stats4uAn(), which fills new placeholders. This website uses exactly that: it runs Stats4U 1.6.0 with consented.eu, and its page switching without reload fires s4u:seiteweg before every swap.

Through the WordPress.org review

The first submission was put on hold on 26 September 2026. The review team asked for every value to be escaped at output, including HTML that a function returns, and for no translation files in the package. Version 1.6.0 does both. Every output goes through esc_html(), esc_attr(), esc_url(), wp_kses() or wp_print_script_tag(), and the callback of the output buffer places the counter below the footer. The 18 translations that shipped in the package up to 1.5.0 now sit in the GitHub repository as templates; translation happens on translate.wordpress.org, and WordPress loads the language packs by itself. On 29 September 2026, the review team approved Stats4U.

On deletion, the plugin removes its option and the remembered view of its settings page. Measured: an option of 236 bytes and the view were present before, both gone afterwards. The counter on stats4u.net and its statistics remain.

All links

Questions and answers

Does Stats4U set a cookie?

No. In the measurement with a fresh browser profile, the browser held no cookie and nothing in local storage after loading, and the response with s4u.js carried no Set-Cookie. As with any request to another server, the browser’s IP address and user agent reach stats4u.net. A suggested paragraph for the privacy policy is at stats4u.net/privacy-embed.

What happens if stats4u.net is unreachable?

Then the counter is missing from the page, and nothing else. The script loads asynchronously and does not hold up the page. Until it has drawn an image, a CSS rule hides the empty box, so no gap is left.

Do visits in the preview or the editor count?

No. The preview on the settings page and in the block editor loads only the counter image with the parameter rl=1, which means “display only”. Up to version 1.1.0, every look at the settings page counted as a visit; that is fixed.

Does deleting the plugin also delete the counter?

No. On deletion, the plugin removes its option and the remembered view of its settings page, and nothing else. The counter on stats4u.net belongs to the site owner and keeps its statistics.

Sources

Lukas Wojcik

Lukas Wojcik

Systems architect and technology enthusiast specializing in scalable tracking solutions, GMP Stack (GA4 & GTM), and robust backend architectures. Advocate for clean code and privacy-first design.

Get in Touch

Briefly describe your project or inquiry for a tailored response. This site is protected by reCAPTCHA.

Write a comment

Experience with other plugins or hosting environments and questions about the setup are welcome here.

The email address is not published. Required fields are marked with an asterisk.

ALL ARTICLES & CATEGORIES

CCTV

Follow this category by RSS

Cloud & AI

Follow this category by RSS

Data Privacy

All 18 articles in this category Follow this category by RSS

Digital Analytics

All 55 articles in this category Follow this category by RSS

Digital Marketing

All 37 articles in this category Follow this category by RSS

IT & Networks

All 17 articles in this category Follow this category by RSS

Music Production

All 15 articles in this category Follow this category by RSS

Raspberry PI

Follow this category by RSS

Smart Home

All 18 articles in this category Follow this category by RSS

Web Development

All 11 articles in this category Follow this category by RSS

WordPress Plugins & Tricks

All 11 articles in this category Follow this category by RSS