I will soon lock down the b2evolution.net site. It will become read only and neither you nor me will be able to post new content to it.
Original post blogged on b2evolution.
The fourth feature release of Drupal 11 is another performance breakthrough. Using only a third of the database and cache lookups compared to Drupal 11.0 and 10.6 for the same requests. It also comes with 15-25% better compression of JS and CSS, much faster translation file handling, a new native command line interface, improved password hashing and a lot more.

With Drupal 11.3, we announced that it was the biggest performance improvement of the decade. Drupal 11.4 is arguably the biggest performance improvement of the decade again!
Drupal 11.4 reduces database queries by half compared to 11.3 across a wide range of requests due to optimizations in how entity fields are loaded.
Now, on a completely cold cache, Drupal 11.4 will execute just over 1/3rd of the database and cache lookups compared to Drupal 11.0 or 10.6, representing hundreds of milliseconds saved.
As well as entity loading, entity listing queries have also been significantly improved via reducing the number of table joins, leading to fewer slow queries. This should particularly benefit sites using JSON:API.
To reduce the cost of rendering menus and improve render cache hit rates, menu blocks now have a configuration option to not generate CSS classes for ancestor menu links.
We have made recipe-based site installation twice as fast. This significantly improves the UX of installing Drupal CMS and other site recipes. Installing individual recipes is also markedly faster.
Importing translations during the installer or during site operation is now much faster. On a test site with 66 projects and 38 languages, checking for translation updates was 87% faster on Drupal 11.4 compared to 11.3.
The APIs handling translation files and import have undergone an extensive modernization effort. All .inc files and several important APIs in locale.module have been deprecated and updated to OOP with special attention paid to performance and organization.
Drupal now supports Brotli compression for aggregated CSS and JavaScript files in addition to the existing gzip compression. Brotli typically provides 15-25% better compression ratios than gzip, resulting in faster page loads for browsers that support it. The feature relies on the PHP Brotli extension: ext-brotli.
The drupal/core-recommended package no longer pins minor versions for dependencies like Guzzle, Twig, and Symfony Polyfills. In the past, stricter version rules and Composer 2.9's blocking behaviour forced sites to wait for a new Drupal release to get important security fixes. Now, you can install these security fixes immediately. Since the updated dependencies at that time may not have been tested with Drupal core yet, site owners should ensure adequate quality assurance occurs before deploying to production.
A new extensible ./vendor/bin/dr command line interface was added. While Drupal already includes a CLI script with hardcoded commands, it is not extensible. This new interface was built by a team which included the maintainers of the Drush utility. Drush has been a mainstay for people using Drupal with the command line. Now a transitional period starts as Drush is gradually replaced with the core dr CLI over time. Learn how to make your existing Drush commands compatible.
The default installation, the Standard profile, is now leaner. It no longer includes the Article and Page content types, and commenting is disabled by default. Further core startup simplifications are planned for upcoming releases.
The Navigation module is now enabled in the standard administrative interface. The legacy Toolbar module remains available but is scheduled for removal in Drupal 12.
A new overview page has been added under the "Manage display" tab for content entity bundles. Previously, this tab led to the form editing the default view mode. Now, it lists all display modes for the bundle with their label and description and allows one to toggle the enabled/disabled status. The listing makes it easier to integrate tools such as Drupal Canvas.
Text formats using CKEditor can now be configured to include the FullScreen plugin. This plugin lets users expand the editor to the whole browser viewport, giving more space to comfortably edit content in a distraction-free environment.
The password hashing algorithm is now configurable. The new argon2id option provides much stronger hashing compared to the old bcrypt method. Drupal 12 will default to argon2id, but your site can already start to adopt it. If you update the setting, users' passwords will be rehashed on their next login.
You can now use attributes on your controllers to specify the routes the controller is used for. Any class in a module's Controller namespace (for example, Drupal\example\Controller) that have the Symfony\Component\Routing\Attribute\Route attribute will be picked up as route definitions. Even multiple routes can be defined on one class. This supplements the existing .routing.yml based declarations.
It is now possible to use the Drupal\Core\Entity\Attribute\Bundle attribute to define bundle classes, when in need of specific logic for an entity subtype. This previously required an entity_type_info or entity_type_info_alter implementation.
All .theme and .theme-settings.php files in core have moved to PHP classes. Support for .theme files is still planned to be retained in Drupal 12 to ease the transition, but will be removed in Drupal 13.
Most .module files have been converted too: 32 modules are fully converted to PHP classes, with 11 modules remaining (4 of which are deprecated for removal in Drupal 12).
A team of 26 key contributors worked on 57 issues since January 2026 to get here, making Drupal's code more consistent. Also thanks to the dozens of users that worked on the many decades old issues that this initiative built upon.
Drupal now integrates the Symfony Runtime component to separate bootstrapping logic from request handling. This provides a clear separation of concerns between preparing the environment (runtime) and handling a request, which will also later enable better integration with FrankenPHP.
A new trait for kernel tests, HttpKernelUiHelperTrait, allows kernel tests to make HTTP requests to the test site and make assertions against the returned content. This has the potential for many browser tests to be converted to kernel tests, which are much faster to run because unlike browser tests, they don't fully set up a test site by running the Drupal installer.
The Gin administrative theme has been added to Drupal core as the "Default Admin" experimental theme. The theme includes a new dark mode option.
While it is not yet actually the default admin theme, when it becomes stable it will replace Claro as the look of Drupal's backend. We encourage module maintainers to test their module's UIs and provide feedback!

Since Drupal 11.3 Andrei Mateescu was appointed as a provisional general core committer and is now a Content Moderation and the Workflows module maintainer too. Also Edward Wu was appointed as provisional release manager.
Various wonderful contributors also took our call for subsystem maintainership:
We also thank maintainers that stepped down in this period:
If you are looking to make the leap from Drupal user to Drupal contributor, or you want to share resources with your team as part of their professional development, there are many opportunities to deepen your Drupal skill set and give back to the community. Check out the Drupal contributor guide.
You would be more than welcome to join us at DrupalCon Rotterdam in September 2026 to attend sessions, network, and enjoy mentorship for your first contributions.
Drupal 12 will be released with the upcoming Drupal 11.5 at the beginning of December this year. Drupal 11.5 will be a Long Term Support release with version 11 support expected until the end of 2028.
[!CAUTION] v2.3.9 is a maintenance release for sites on v2.3.8 or earlier. Upgrade from any 2.x at or below v2.3.8. If your site tracks the
masterbranch, you are already past v2.3.9, so installing it would be a downgrade. v2.4.x is planned to be the next forward step.[!NOTE] There are no security fixes in this release. If your site is still on v2.3.7 or earlier, the release you need urgently is v2.3.8, which closed an unauthenticated SQL injection and two further advisories. v2.3.9 carries all of that forward and adds bug fixes on top.
A-Za-z0-9_-. MySQL and MariaDB permit periods, spaces, dollar signs and Unicode in database names, verified on MariaDB 10.11 and MySQL 8.0, so an existing database named, for example, e107_2.3.8 could not be used at all. The restriction protected nothing, because whoever fills in that form already holds the database credentials, and the name only ever reaches SQL inside backticks. It is now escaped there instead. The table prefix keeps its A-Za-z0-9_ rule, because the prefix really is dropped into queries unquoted. (https://github.com/e107inc/e107/discussions/5819)via.placeholder.com has gone; the domain no longer completes a TLS handshake. thumb.php used to redirect missing-image requests there, the hero plugin's three sample slides embedded its URLs directly, and the media handler had a special case for the domain name. thumb.php now serves a small SVG placeholder it generates itself, with cache headers, in about two microseconds; the hero slides ship as local SVG files; and image detection now ignores the query string, so existing database content still pointing at the dead service is classified correctly instead of by domain name. (https://github.com/e107inc/e107/issues/5581)login.php, ignoring a customlogin.php if the site had one, and the login page could only render in its bare iframe form. Worse, with registration disabled and no social login, the stock login page turns a guest away and bounces them back to the page they came from, looping until the browser aborts. e107 now redirects to whichever login page the site is configured to use, and falls back to the members-only notice in exactly that dead-end case, so the loop cannot form. (https://github.com/e107inc/e107/issues/5335)http://twitter.com/share link and a Facebook "like" frame into every gallery overlay. The insecure addresses were blocked as mixed content on any HTTPS site, and the Facebook frame never received the right address anyway, so what remained was broken markup on every image. The default is now empty. A theme that defines its own share template still overrides it and is unaffected. (https://github.com/e107inc/e107/discussions/5651)SECURITY.md file at the top of the repository still pointed reporters at an e-mail address that does not reliably deliver. GitHub serves the policy from .github/SECURITY.md, which asks for private reports through GitHub Security Advisories, so the stale copy has been removed. To report a vulnerability in e107, use Security Advisories. (https://github.com/e107inc/e107/pull/5822)e107-tests harness, backported from master. release/v2.3.x can now run its own PHP matrix locally, including the 5.6 and 7.0 legs whose end-of-life Debian bases previously could not build a container at all. State moves out of per-worktree docker/.envs/ files into Docker labels and per-environment named vendor volumes, so list is global, gc reaps orphans, and the repository's composer.lock is never rewritten (composer install, with a loud per-environment composer update fallback when the interpreter cannot satisfy the lock). Adapted for this branch: no Selenium service, DEFAULT_PHP=8.2, acceptance browses http://localhost/ from inside the web container, and Codeception stays at 4.2.2. Validated on 5.6, 7.0, 7.4, 8.1 and 8.2. (https://github.com/e107inc/e107/pull/5837, https://github.com/e107inc/e107/pull/5836)testCdnUrlContainsVersion asserts each CDN version appears in its resolved URL, and testCdnFileReportsHardcodedVersion exercises every entry's own version_arguments pattern against real bytes vendored under tests/_data/library_headers/. testRefreshCdnFixtures regenerates those fixtures from the live CDNs and is the one Internet-touching path, skipped unless E107_REFRESH_LIBRARY_FIXTURES is set. (https://github.com/e107inc/e107/issues/4560)check_name()'s non-strict mode (the database name) no longer imposes a character-set rule; strict mode (the table prefix) keeps A-Za-z0-9_, because the prefix is interpolated unquoted into every query. A new quoteDbName() doubles backticks and wraps the name, and the three CREATE/ALTER/DROP DATABASE sites use it. e_db_pdo::database() and e_db_mysql::database() double backticks in their quoted interpolations; mysqli_select_db() needs nothing. The legacy quote and semicolon guard stays, because a semicolon cannot be represented in the PDO DSN. SqlInjectionFixesTest pins the new contract, including a backtick-doubling breakout guard. New strings LANINS_148, LANINS_149 and LANINS_150 replace the misused LANINS_105. (https://github.com/e107inc/e107/discussions/5819)login.php forced e_IFRAME unconditionally. It now defers to the login core template, so setting $LOGIN_TEMPLATE['page']['noiframe'] = true in a theme override renders the login page with the full site chrome. The default is unchanged. The members-only redirect target also respects e_LOGIN now rather than hardcoding login.php. (https://github.com/e107inc/e107/issues/5335)LAN_418 in lan_upload.php is now LAN_UL_041. LAN_407 and LAN_418 were each defined with different values in two core language packs, so load order decided which text won. The unused LAN_407 blurb has been dropped from lan_signup.php (the signup e-mail composes from LAN_EMAIL_04/05/06), and lan_upload.php's "Absolute maximum file size: " is renamed to LAN_UL_041, with upload.php repointed. lan_search.php keeps sole ownership of LAN_418. Translators and language-pack maintainers: add LAN_UL_041 to your lan_upload.php; the old LAN_418 definition there is no longer read. (https://github.com/e107inc/e107/issues/5730)sc_cb_avatar() honours $parm. The chatbox avatar shortcode hardcoded crop => 'C' and rebuilt its own options array, discarding any crop, w or h a template passed in, which pinned chatbox avatars to the legacy thumb.php query-string URL while sibling avatar shortcodes forwarded $parm and reached SEF addresses. $parm is now merged over the defaults. 'C' remains the default crop, so existing rendering is unchanged, but a template can pass crop=false to escape to a SEF-eligible URL where thumb.php is broken on the host. (https://github.com/e107inc/e107/issues/5657)e_library_manager takes an injectable file handler. It defaults to e107::getFile(), and detectPath() and getVersion() read through it, so a test can stub every remote read. _before() injects a handler that throws on any fetch, so an accidental network read now fails loudly rather than quietly downloading. testDetectionVersionConsistency compares each CDN pin against its bundled local sibling offline, which catches drift between the shipped copy and the hard-coded version, and testCoreLibraryPresence has dropped its remote URL probe. The cdn.jquery.once pin is corrected: it claimed 2.1.2 while the bundled copy ships 2.2.3, and the legacy jsDelivr path it used does not serve 2.2.3 at all, so it is repointed at the npm path. (https://github.com/e107inc/e107/issues/4560)e_parse::toAvatar() fetches through the e107::getFile() singleton. Its base64 data-URI branch fetched a real avatar over the network at test time, so any DNS or network hiccup silently fell through to the plain remote ![]()
and failed the assertion, which looked order-dependent under the shuffled suite. The fetch is now injectable and the test stubs it. (https://github.com/e107inc/e107/pull/5841)build-release.yml is replaced by release.yml. The build step is unchanged in substance, but the workflow now finishes the job instead of stopping at a workflow artifact: it uploads the archives and core_image.phar to the GitHub Release, then uploads the nine archives to SourceForge over SFTP with a pinned host key and sets the per-platform default downloads. A guard asserts the full tag history was fetched before the core integrity image is built, and a build whose tag does not match the version it produced refuses to upload. (https://github.com/e107inc/e107/pull/5831)e_navigation::admin() cross-join. The sorted branch built a list of every entry's display text and then, for each text, re-appended every entry matching it, emitting K entries K times each for a same-named group. The text list is now deduplicated before the cross-join. (https://github.com/e107inc/e107/issues/5786)pclzip reading zero bytes. fread($h, 0) throws a ValueError on PHP 8.2+, so an archive containing a zero-byte file fatally errored during in-memory compression. The read is guarded, and the file is still written as a zero-byte entry. (https://github.com/e107inc/e107/issues/5208)stripos() folds case for ASCII only, so a title differing in case on a non-ASCII letter could be dropped by the PHP post-filter even though the case-insensitive database collation had matched it. The five title filters now use the parser's ustrpos() and ustrtolower(), which use mbstring when it is present and fall back to the byte functions when it is not, so there is no new hard dependency. (https://github.com/e107inc/e107/discussions/5791)Thanks to
Full changelog: https://github.com/e107inc/e107/compare/v2.3.8...v2.3.9
In modern SEO, small details often make a big difference. Meta tags such as meta_description, meta_keywords, and page_title still play an essential role in how your content is displayed and understood by search engines. They directly influence how your stories appear in results, and therefore how often users click them.
I’ve recently built a simple, lightweight utility that helps Geeklog site administrators automatically generate these fields using OpenAI’s API. It works with any Geeklog 2.x site and doesn’t require additional plugins or modifications.
The Geeklog AI SEO Updater connects directly to the Geeklog database and detects any stories missing a meta_description. For each one, it uses the gpt-4o-mini model to analyze the article’s title and introduction, then generates:
The script automatically detects the language of your site from $_CONF['language'] — it works in French, English, German, Spanish, Italian, and Japanese. You can test results safely in preview mode (?dry=1) or apply them in update mode with a defined article limit.
While modern search engines rely on full-page analysis, meta descriptions remain key to how your site looks in results. They act as your article’s “headline” on Google or Bing and have a strong effect on click-through rate.
Meta keywords, even though no longer used by Google for ranking, remain valuable inside Geeklog. They can be used for internal linking or automated features such as:
💡 For example, another script could automatically link stories that share two or more identical keywords, creating a stronger internal SEO structure.
The script only updates stories with empty meta descriptions and checks that you have the proper story.edit admin rights before execution.
# Preview mode (no database update)
https://yourdomain.com/admin/utils/update_meta.php?dry=1
# Update 10 stories
https://yourdomain.com/admin/utils/update_meta.php?limit=10
You can also schedule it as a weekly cron job to automatically optimize metadata for newly published stories.
This script lays the groundwork for more semantic features inside Geeklog. The generated keywords can later feed new modules such as:
By combining Geeklog’s simplicity with AI-assisted metadata, content becomes easier to navigate and better structured for both users and search engines.
The full documented code is available on the Geeklog Community Forum: 👉 https://www.geeklog.net/forum/index.php?forum=10
It supports PHP 5.6 +, Geeklog 2.x, and optionally integrates with the IndexNow plugin to notify Bing. Every line of the shared version is commented for easy understanding and adaptation.
With this AI-powered updater, Geeklog proves it can stay modern and efficient without losing its core simplicity. This approach helps keep older content relevant, visible, and better connected — with minimal effort and full control from the admin panel.
🧩 Author: Ben, Geeklog community contributor Discuss and download on the forum: https://www.geeklog.net/forum/index.php?forum=10
Posted October 28, 2025 | 7:41 pm