NewTry MagicWP now - first month free
Back to blog

LiteSpeed Cache XSS via Comments (CVE-2026-18978): Update to 7.9

CVE-2026-18978 lets unmoderated comments inject scripts on LiteSpeed Cache sites up to 7.8.1. Update to 7.9 and check two settings.

LiteSpeed Cache XSS via Comments (CVE-2026-18978): Update to 7.9

On 27 August 2026, LiteSpeed Technologies published a security notice for CVE-2026-18978, a stored cross-site scripting vulnerability in LiteSpeed Cache. The plugin has more than seven million active installations, so any advisory against it is worth reading properly rather than skimming. This one is unusual in two ways. The injection point is the public comment form rather than an admin screen or a REST endpoint, and the fix had already been shipping for three weeks by the time anyone outside the vendor and the reporting researcher knew the bug existed.

The practical question is not whether LiteSpeed Cache is safe to run. It is whether your site spent August serving a cached page with someone else's JavaScript in it, and whether anything is still sitting in your cache now. This LiteSpeed Cache vulnerability only bites under a specific combination of plugin and WordPress settings, and working out whether that combination was true on your site takes about five minutes.

TL;DR

  • CVE-2026-18978 is an unauthenticated stored XSS in LiteSpeed Cache. Per the Wordfence advisory, versions up to and including 7.8.1 are affected. LiteSpeed patched it in 7.9, released 5 August 2026.
  • Two things had to be true for a site to be exposed: Next-Gen Image Format set to WebP or AVIF in the plugin's Image Optimization settings, and a comment form that publishes at least some comments without moderation.
  • Update to 7.9 or later, then purge the full cache. Removing a bad comment from the database does not remove it from cache entries that were already generated.
  • The "Comment author must have a previously approved comment" setting is not an authorization boundary. WordPress matches the email address of a previously approved commenter, which is knowledge, not proof of identity.
  • Version 7.9 raises the minimum requirements to PHP 7.4 and WordPress 6.0. Sites below either line cannot take the patched release and need a different plan.
  • At the time of writing, neither Patchstack nor WPScan lists this CVE. A vulnerability scanner keyed to those feeds may report a 7.8.1 site as clean.

What CVE-2026-18978 actually is

Stored cross-site scripting means an attacker gets their own script saved somewhere the site will later print into a page. Every visitor who loads that page runs the script in their own browser, in the context of your domain. That is different from reflected XSS, where the attacker has to talk a specific person into clicking a specific link. Stored XSS is fire-and-forget. It runs against whoever shows up next.

In this case the storage medium is a WordPress comment. According to LiteSpeed's advisory, unauthenticated attackers could inject arbitrary web scripts through comments, and those scripts would then execute for anyone viewing the affected page.

What makes it interesting is that comments are not normally a script injection route. WordPress strips dangerous markup out of comment content on save for anyone without the unfiltered_html capability, and escapes what remains on output. That protection assumes the comment text is going to be printed as HTML into an HTML context. The vulnerability sits downstream of that assumption, in the plugin's image replacement pass, which runs over the finished page after WordPress has already done its escaping. More on why that distinction matters below.

Here is where the versions land:

Version Released Status
7.8 3 Mar 2026 Affected
7.8.0.1 17 Mar 2026 Affected
7.8.1 1 Apr 2026 Affected, last vulnerable release
7.9 5 Aug 2026 Patched

The Wordfence advisory covers all versions up to and including 7.8.1, so anything on an older branch is in range too. There is no partial mitigation by staying on 7.8 instead of 7.8.1. The only fixed version is 7.9 or later.

LiteSpeed's own timeline is short and, to the vendor's credit, fast: Wordfence reported the issue on 5 August 2026, and LiteSpeed patched it and released 7.9 to the WordPress plugin directory the same day.

The two conditions that decided whether you were exposed

LiteSpeed's advisory is specific about preconditions, which is more than most vendor notices offer. Both had to be true at once.

Condition one: Next-Gen Image Format set to WebP or AVIF

This lives at LiteSpeed Cache > Image Optimization > Image Optimization Settings > Next-Gen Image Format. Its default value is OFF. Setting it to WebP or AVIF does two things: it tells the QUIC.cloud optimization service to generate next-generation copies of your images alongside the optimized originals, and it tells LiteSpeed to serve those .webp or .avif files in place of .jpg and .png to browsers that support them.

That second half is the part that matters here. Serving next-gen formats is not just a file swap on disk. The plugin has to find image references in the rendered page and rewrite them, which is why there is a companion setting called WebP/AVIF Attribute To Replace listing which element and attribute pairs get rewritten by default. The plugin is, in other words, editing your finished HTML on the way out.

Two operational notes worth having in front of you. On a multisite network this setting moves to the Network Admin screen, so a per-site check will not find it. And enabling next-gen formats creates additional cache varies, meaning a single URL can have more than one stored cache entry. Keep that in mind for the cleanup section.

Being off by default sounds reassuring, but next-gen image delivery is one of the main reasons people install LiteSpeed Cache in the first place. Treat "it's off by default" as a reason to check rather than a reason to relax.

Condition two: comments that publish without moderation

The attacker also needed to be able to get a comment onto a page without a human approving it. LiteSpeed's advisory describes two ways that happens, both governed by Settings > Discussion:

  • Comment author must have a previously approved comment is unchecked, so comments are auto-approved outright.
  • That box is checked and Comment author must fill out name and email is also checked, which lets an attacker reuse the name and email address of someone who has already had a comment approved and inherit their auto-approval.

The first case is common on small sites where the owner turned moderation off because approving comments was tedious. The second case is the one that will surprise people, because those two checkboxes read like a security posture rather than a spam convenience.

Why "previously approved comment" is a weaker gate than it looks

WordPress documentation describes the setting plainly: comments are only posted immediately if the comment author's email address matches the address of a previously approved comment. Otherwise the comment is held for moderation.

That is a match against a value, not a verification of a person. Nothing in the comment form proves that the address belongs to whoever typed it. There is no confirmation email, no token, no session. If a value that has been approved before shows up again, the comment goes live.

This is fine for its intended purpose. It was built to stop drive-by spam bots from filling your moderation queue, and it does that. The failure is in how the setting gets read. A checkbox labelled "must have a previously approved comment" sounds like an identity gate, so people treat it as one, and then treat everything that publishes through it as trusted content. It is not trusted content. It is unauthenticated input that skipped the queue.

The practical rule to take away: any comment that publishes without a human looking at it should be treated, from a security point of view, exactly like an anonymous POST to a public endpoint. Every plugin that later processes that text is processing attacker-controllable input.

Why a page cache makes stored XSS harder to clean up

This is the part that most write-ups about XSS in WordPress skip, and it is the part that actually determines how much work you have in front of you.

On a site with no page cache, an injected comment is rendered through PHP on every request. Delete the comment and the next request produces a clean page. The database is the single source of truth and fixing it fixes everything.

A full-page cache breaks that relationship. Once a page containing the injected content has been generated, its HTML is written into a cache entry. From that point on, the server can hand that stored HTML to visitors without running PHP, without reading the comments table, and without any awareness that the comment behind it has since been deleted. The page keeps serving the script until the cache entry expires or is explicitly purged.

So the cleanup order matters:

  1. Update the plugin.
  2. Deal with the comment in the database.
  3. Then purge the cache.

Do it in a different order and you can purge a cache that immediately regenerates from a page that still contains the bad comment.

Three further wrinkles specific to this situation:

  • Next-gen formats create extra cache varies. One URL can have several stored variants. Purging a single URL is not reliable here. Purge everything.
  • A CDN in front of your origin has its own copy. Purging at the origin does not clear an edge cache. If you run QUIC.cloud CDN, Cloudflare, or any other edge layer, purge there too and confirm it happened rather than assuming.
  • Browser caching does not apply to the HTML in the usual way, but any visitor who has the page open is still running the script until they navigate away. There is nothing you can do about that after the fact, which is an argument for purging sooner rather than doing more investigation first.

How to check your site in five minutes

If you have SSH and WP-CLI, which every serious WordPress host should give you, this is quick. Start with the plugin version:

terminal
wp plugin list --name=litespeed-cache --fields=name,status,version,update_version

Anything below 7.9 in the version column is in the affected range.

Next, check whether next-gen image replacement was on. The plugin's WP-CLI command can dump every option, so filter rather than guessing at the option key:

terminal
wp litespeed-option all --format=csv | grep -i webp

Then the three WordPress Discussion options that decide whether comments publish on their own:

terminal
wp option get comment_moderation
wp option get comment_previously_approved
wp option get require_name_email

Read them together, not separately:

comment_moderation comment_previously_approved What it means
1 anything Everything is held for a human. Not exposed by condition two.
0 0 Comments auto-publish. Exposed.
0 1 Auto-publish for returning addresses. Exposed per the vendor's second case.

One naming note: the comment_previously_approved option was called comment_whitelist before WordPress 5.5. Since LiteSpeed Cache 7.9 requires WordPress 6.0 or newer, the current name is the one you want, but older automation scripts may still reference the old key.

Without shell access, the same checks are two admin screens: LiteSpeed Cache > Image Optimization > Image Optimization Settings for the Next-Gen Image Format value, and Settings > Discussion for the moderation checkboxes. On multisite, check Network Admin for the image setting.

Updating, and the requirement change that will trip some sites

Take a backup before you touch a caching plugin on a production site. This is not a formality: a caching plugin update that goes badly does not produce a clean error page, it produces a site that looks subtly wrong for some visitors and fine for you. Restore is much less painful than diagnosis. On MagicWP you can take an on-demand backup before the change and restore with one click, and one-click staging gives you somewhere to try the update first if the site is busy enough to justify it.

terminal
wp plugin update litespeed-cache
wp plugin list --name=litespeed-cache --field=version
wp litespeed-purge all

Now the gotcha. Version 7.9 raised the plugin's minimum requirements to PHP 7.4 and WordPress 6.0. If your site is below either line, WordPress will not offer you the update, and you will sit on a vulnerable version indefinitely without any obvious signal that something is wrong. The update screen simply will not mention it.

If that is you, the honest options are:

  • Fix the underlying version first. A site on PHP 7.2 in late 2026 has a longer list of problems than this one CVE, and PHP 7.4 itself is well past end of life. Moving to a currently supported PHP release is the real fix.
  • Turn off the trigger while you plan. Setting Next-Gen Image Format back to OFF removes the first of the two conditions. You lose the image size savings, which is a real cost, but it is a defensible interim position.
  • Turn off comment auto-approval. Setting comments to require manual approval removes the second condition. This is usually the cheaper trade-off of the two, and on most sites it is a good default anyway.

Do not treat either of those as a substitute for updating. They are stopgaps for sites that genuinely cannot take 7.9 today.

If you think a comment might have got through

Be realistic about what you can and cannot establish here.

You can list what published in the window between enabling next-gen image formats and updating to 7.9:

terminal
wp comment list --status=approve \
  --fields=comment_ID,comment_date,comment_author,comment_author_email,comment_post_ID

What you cannot do is grep the comments table for <script> and declare the site clean if nothing turns up. The comment content stored in the database went through WordPress's own sanitising on save. What mattered was how that already-sanitised text was handled later, during the page's image replacement pass. A payload that produces script execution downstream will not necessarily look like a script tag sitting in the database.

So the sensible position is: absence of an obvious payload is not proof of nothing happening. Weigh it against everything else you know. Was next-gen replacement actually enabled during that window? Did comments actually publish without moderation? Did you get unexplained reports of odd redirects, popups, or admin sessions ending early?

If you have real reason to think an administrator loaded an injected page, invalidate logged-in sessions and rotate passwords for privileged accounts. WP-CLI can destroy a user's sessions; check wp help user session for the exact syntax on your version rather than copying a command from a blog post. Session cookies are the highest-value thing an XSS payload takes from a logged-in admin, and rotating them is cheap.

Then purge everything, at the origin and at any edge in front of it.

The fix shipped three weeks before the advisory

Look at the dates again. Wordfence reported it on 5 August. LiteSpeed patched and released 7.9 on 5 August. The public advisory and the vendor's blog post came on 27 August.

That gap is normal and it is deliberate. Coordinated disclosure exists so the fix is available before the description of the bug is. It works. But it has a consequence people rarely state directly: for those twenty-two days, whether your site was protected had nothing to do with how closely you follow WordPress security news. It was decided entirely by your update policy.

A site on automatic plugin updates was patched on 5 August, before there was anything to read. A site with a monthly manual update window may still be on 7.8.1 today, having had three weeks of availability it did not use.

The counterargument to auto-updating is real and specific to this category: caching and optimization plugins are exactly the ones you are nervous about updating unattended, because a bad release does not throw a fatal error, it quietly serves broken CSS to a subset of visitors. That is a legitimate fear. The resolution is not to disable auto-updates, it is to pair them with a backup taken before the change and a way to roll back quickly. Automatic updates with a restore path are strictly better than manual updates with a three-week lag.

If you manage sites for clients, the version to check across the portfolio is 7.9, and the check is worth running today rather than at the next scheduled maintenance window.

Why your scanner might be quiet about this

Worth knowing before you trust a green tick.

At the time of writing, Patchstack's LiteSpeed Cache entry shows twenty patched vulnerabilities and none present, and its most recent record covers versions up to and including 7.7. WPScan lists nineteen vulnerabilities for the plugin, with the most recent published on 26 May 2026 and covering versions below 7.8. Neither database has an entry for CVE-2026-18978 yet.

That is not a criticism of either. Databases populate at their own pace, and Wordfence is the CNA of record here. But the practical effect is that a vulnerability scanner or a managed security service keyed to those feeds may look at a site running 7.8.1 and report nothing to fix. "No findings" and "not affected" are different statements, and for the next few days on this particular CVE they will diverge.

Separately, a second LiteSpeed Cache advisory landed on the same day: CVE-2026-3129, an authenticated Author-or-above stored XSS via image tag attributes, affecting versions up to and including 7.7 and fixed in 7.8. Patchstack scores that one at 5.9 and rates it low priority. It is a different bug with a different fixed version and a much higher bar to exploit, since it needs an account with publishing rights. Updating to 7.9 covers both, which is the only reason to mention it here.

The wider lesson: escaping and rewriting are different jobs

There is a structural point in this bug that applies well beyond LiteSpeed Cache, and it is the reason this article is longer than "update your plugin."

WordPress's output escaping is contextual. When core escapes comment content, it is escaping it to be safe as HTML text inside an HTML document. That guarantee holds right up until something moves the value somewhere else.

Optimization plugins move things. They buffer the finished page, parse it, rewrite attribute values, combine and inline assets, and inject inline scripts carrying configuration data. Every one of those operations takes text that was escaped for one context and relocates it into another. A string that is inert as HTML body text can behave very differently once it has been lifted into an attribute, a URL, or a JavaScript literal inside a <script> block.

This is not a design flaw unique to one vendor. It is the standing cost of the entire output-rewriting category, and every serious caching and optimization plugin carries some version of it. The takeaway for anyone auditing a WordPress stack is that "the CMS sanitises user input" is an incomplete answer. The question is what happens to that input after the CMS is finished with it. Any plugin that post-processes rendered HTML deserves the same scrutiny you would give a plugin that accepts uploads, because it is doing something just as consequential with untrusted data.

It is also why platform-level protections and application-level fixes are not interchangeable. A WAF rule can catch known payload shapes at the edge. It cannot know that a value which was safe three transformations ago has since been relocated into a script context. Only the fix does that.

What else changed in 7.9

Version 7.9 was a large release, and a few other entries in it are worth an operator's attention:

  • The default value for caching logged-in users changed to false. Fewer opportunities to accidentally serve a private page to the public is a straightforwardly good default, but check it if you deliberately relied on the old behaviour.
  • Public access to data files in the static cache folder was blocked. Cache directories that are readable over HTTP are a recurring source of information disclosure across the whole caching plugin category.
  • Several ESI fixes, including nonce generation for actions with custom nonce lifetimes and handling of comment blocks that fail hash validation.

One thing the 7.9 changelog does not do is label any entry as a security fix. If your update process involves reading changelogs to decide how urgently to apply something, this release is a good example of why that process has a blind spot. The changelog was published on 5 August; the reason it was urgent was published on 27 August.

Frequently Asked Questions

Do I need to do anything if my LiteSpeed Cache is already on 7.9?

No. Version 7.9 contains the fix for CVE-2026-18978, and it shipped on 5 August 2026. If you updated at any point in the last three weeks you were patched before the advisory was public. The one thing worth doing anyway is confirming the version rather than assuming, because a failed automatic update leaves no notice on the dashboard. Run wp plugin list --name=litespeed-cache --field=version or check the Plugins screen.

Was my site definitely exploited if it was running an affected version?

No, and you should resist concluding either way without evidence. Both conditions had to be true at the same time: next-gen image replacement enabled, and comments publishing without moderation. Plenty of affected installations never had both. Exploitation also requires someone to have actually targeted your site. There is no public report of exploitation in the wild for this CVE as of writing, though that can change and is worth re-checking.

Is turning off comments enough to fix this?

It removes one of the two required conditions, so it does reduce risk while you plan an update. It is not a fix. The vulnerability is in the plugin's handling of page output, and it stays there until you install 7.9. Disabling comments is a reasonable stopgap for a site that cannot take the update today because of the PHP or WordPress version requirements, but it should come with a date attached for doing the real thing.

Why does deleting the comment not immediately fix the page?

Because a page cache serves stored HTML without consulting the database. Once a page containing the injected content has been cached, that copy keeps going out to visitors regardless of what happened to the comment behind it. You need to purge the cache after deleting the comment, and if you have a CDN in front of the origin you need to purge there too. Next-gen image formats also create additional cache varies, so purge everything rather than a single URL.

What CVSS score does CVE-2026-18978 have?

No score is available from the sources we could verify at the time of writing. Neither Patchstack nor WPScan has published an entry for this CVE, and the CVE record itself has not been enriched. Treat any score you see quoted elsewhere as needing a source. The preconditions are narrow but the attacker requires no authentication and no user interaction, which in practice puts it above the scores usually attached to author-level XSS in the same plugin.

Does this affect me if I don't run a LiteSpeed web server?

Yes, potentially. LiteSpeed Cache's general features, including image optimization and next-gen format replacement, work on any web server. Only the server-level page caching requires LiteSpeed Web Server, OpenLiteSpeed, LiteSpeed Web ADC, or QUIC.cloud CDN. If you installed the plugin on Nginx or Apache purely for its optimization features and turned on WebP, you were in scope.

How do I stop being three weeks behind on the next one?

Turn on automatic plugin updates and make sure you have a backup taken before each change plus a fast rollback path. For the caching layer specifically, a staging copy to test against is worth the setup time. The pattern that fails is manual updates on a monthly cycle, because the gap between a fix shipping and an advisory publishing is routinely two to four weeks, and that gap is exactly where the risk lives.

Conclusion

CVE-2026-18978 is a narrow vulnerability with an unusually wide install base behind it. Update LiteSpeed Cache to 7.9 or later, purge the full cache afterwards, and check whether next-gen image replacement and unmoderated comments were both switched on during the window between April and August. If they were, spend a few extra minutes reviewing what published in that period and rotating admin sessions.

The broader thing to carry away is the caching-specific part. Stored XSS on a cached site is not a database problem you can fix in the comments screen, because your cache is a second copy of the page that outlives whatever you delete. Cleanup order matters, and purging at the edge matters as much as purging at the origin.

If keeping seven-figure-install plugins patched within hours instead of weeks is the part of this that worries you, that is largely a hosting problem rather than a WordPress one. MagicWP handles automatic updates with rollback, on-demand backups before every change, and one-click staging for testing a caching update before it reaches visitors, which turns "we'll get to it next maintenance window" into a non-event.

A
Alex
MagicWP
Writing about WordPress, performance, and the infrastructure that makes sites fast.

Get the best of MagicWP in your inbox.

Monthly engineering notes, product updates, and WordPress performance tips. No spam, unsubscribe anytime.

Join 12,000+ builders. We send one email a month.