Skip to content
GDPR compliant. Hosted in DE.
14-day free trial →
End of LifeComponentsCRA
Compliance 14 min read 13 Sept 2026

End of Life in the Web Stack: Components Without Updates

Support windows for PHP, Node.js, Angular and other building blocks end by calendar. How to inventory components, calculate remaining runtime and plan the move before the CRA deadline.

X

XICTRAQ

XICTRAQ

End-of-LifeComponentsCRA
70.2 %
of all websites with a known server-side language run PHP (W3Techs)
17.11 %
of reporting WordPress installations run on PHP 7.4 (WordPress.org)
74 %
of pages load jQuery, the most used library on the web (Web Almanac 2024)
Support windows in the web stackCommitted window per component, as stated by the projectsMonths since release01224364860Ubuntu LTS5 yearsPHP branch4 yearsSymfony LTS3 years bugs, 4 securityNode.js LTS30 monthsAngular24 monthsUbuntu interim release9 monthsTodaywindow consumedremainingtime without updatesSources: Canonical, PHP Group, Symfony, Node.js, Angular
A component does not disappear when its support window closes. It keeps serving pages, keeps accepting orders, keeps answering requests – it simply stops receiving fixes. That is what makes end of life the quietest weakness in a web stack. PHP runs on 70.2 percent of all websites whose server-side language is known (W3Techs), and 17.11 percent of WordPress installations report a PHP branch that no longer appears on the list of supported versions (WordPress.org). The Cyber Resilience Act turns that invisibility into an obligation with a date attached: the support period of a product with digital elements should not fall below five years (Official Journal of the EU). Anyone who wants to state whether their website meets that benchmark needs a dated list, not a gut feeling.

What end of life actually means

End of life is a calendar day, not a state of the software. The day before, a PHP branch is maintained; the day after, it is not – and the files on the server are byte for byte identical. The PHP Group describes the model without room for interpretation: a release branch receives regular bug fixes for two years (PHP Group), followed by a phase in which only security issues are addressed. After a total of four years, that ends as well (PHP Group). From that day on, a newly discovered flaw in that branch simply has no patch, regardless of how serious it is.

The risk is not theoretical. For its European threat assessment, ENISA analysed 4875 incidents recorded between July 2024 and June 2025 (ENISA) – a volume that shows how routinely known weaknesses are exploited at scale. In that landscape, a component without vendor fixes is not a residual risk but an open end. How you find out that a reported flaw affects your components at all is something we took apart in our article on the EUVD and CVE vulnerability feeds.

  • What ends: bug fixes, security fixes, official distribution packages, and any binding vendor statement about newly reported flaws.
  • What does not end: the availability of the old files on package mirrors, everyday functionality, and the impression inside the team that everything is running fine.
  • What starts: your own burden of proof. From end of life onwards you have to explain why an unmaintained component sits inside a product you place on the market.
  • What stays the same: the maintenance contract, the hosting plan, the invoice – none of these documents adjusts itself when a window closes.

CRA deadlines set the pace

Two dates structure the coming years. The obligation to report actively exploited vulnerabilities applies from 11 September 2026 (European Commission), and the main obligations of the Cyber Resilience Act apply from 11 December 2027 (European Commission). In between lies the period that decides whether an organisation knows its components or has to assemble them under pressure. The support period itself should not fall below five years (Official Journal of the EU) – a lower bound that many windows in the web stack have to be measured against. What the reporting duty requires in detail is covered in our article on the CRA vulnerability reporting obligation.

Now hold that lower bound against practice. An Angular major version is typically supported for 24 months (Angular), Laravel provides bug fixes for 18 months and security fixes for two years (Laravel), and a Node.js LTS line states that critical fixes are provided for a total of 30 months (Node.js). Not one of those windows reaches five years on its own. That is not a criticism of the projects – it only means the commitment you make to your customers cannot come from a single component, but from a planned move from one window to the next.

Two dates, two different duties

11 September 2026 concerns reporting actively exploited vulnerabilities (European Commission); 11 December 2027 concerns the main obligations as a whole (European Commission). Anyone who has only the later date in the calendar is planning past the first duty. The basis for both is the same list: a reliable software bill of materials under the CRA.

Support windows compared

The table below collects what the projects themselves commit to. It does not replace an inspection of your own installation, but it makes the orders of magnitude visible – and the spread is considerable. Between the shortest and the longest window in this list there is roughly a factor of ten: six months for a Node.js Current branch against five years for Python, Ubuntu LTS and OpenSSL LTS. Anyone combining all of these components in one product does not have one end of life, but a chain of them.

ComponentCommitted windowSourceWhat applies afterwards
PHP branch2 years bug fixes, 4 years totalPHP Groupno fixes at all, including security fixes
Node.js LTS30 monthsNode.jsline runs out, move to the next LTS required
Node.js Current6 monthsNode.jseither LTS status or end of the line
Python5 years from releasePython Software Foundationbranch is closed
Ubuntu LTS5 years standard maintenanceCanonicalcoverage ends without a paid extension
Ubuntu interim release9 monthsCanonicalmove to the following version required
Debian stable3 years full, 2 years LTSDebian Projectno project maintenance afterwards
Angular24 monthsAngularno further security patches
Laravel18 months bug fixes, 2 years securityLaravelmajor version left unmaintained
Symfony LTS3 years bug fixes, 4 years securitySymfonysecurity fixes only, ending after 4 years
.NETLTS 3 years, STS 2 yearsMicrosoftthe version number decides the window
OpenSSL LTSat least 5 yearsOpenSSLmove to the next LTS release
OpenSSL without LTS (from 3.5)13 monthsOpenSSLfull support ends
MariaDB Community LTS3 yearsMariaDB Foundationno ready-made packages any more

Two rows in that table deserve particular attention. An Ubuntu interim release receives only nine months of updates (Canonical) – running production on it means facing an unplanned move several times a year. And with OpenSSL the label decides: an LTS release is committed to at least five years of support (OpenSSL), while a release without that label is fully supported for only 13 months from 3.5 onwards (OpenSSL). Both components stay invisible as long as they work. They become visible the moment a scanner reads the version out of a response header – see our article on version disclosure through server headers.

Why old branches survive so stubbornly

The numbers show a very clear pattern: old branches do not disappear, they settle. 51 percent of websites run on a CMS (Web Almanac 2024) and therefore inherit its version maintenance along with every extension. Only 63.5 percent of PHP websites use the version 8 branch (W3Techs); the rest sit on older ones. Among WordPress installations that report back to WordPress.org, 17.11 percent run on PHP 7.4 and 1.346 percent still run on PHP 5.6 (WordPress.org). The second figure is small, but it describes installations whose foundation has not seen a fix in years. What that means for day-to-day operation is covered in our article on WordPress security monitoring.

The reasons for this inertia are rarely technical. A branch change touches extensions, custom modifications, build chains and test coverage at the same time, and each of those has a different owner. As long as nothing visibly breaks, postponement wins every prioritisation round. End of life changes nothing about that dynamic – it merely shifts the cost of postponement from maintenance into liability. The decisive question is therefore not whether a move is due, but how many months of remaining runtime are still on the clock.

Remaining runtime instead of version numbers

A version number does not tell you how urgent something is. Only the remaining runtime until end of life turns an inventory into an order of work. That is exactly what the CRA component cockpit is for: components, deadlines and hints about reported vulnerabilities in one place, sorted by what closes first.

The front end ages most quietly

Server components attract attention because they ask for updates. In the front end, by contrast, the code sits as a file in the deployment directory and does not announce itself. jQuery remains the most used library on the web and appears on 74 percent of pages (Web Almanac 2024), and jQuery UI still runs on 22 percent despite being largely deprecated (Web Almanac 2024). These files are not pulled along by any distribution update, they appear in no server package manager and in no system list. How to find them anyway is shown in our article on detecting vulnerable JavaScript libraries.

jQuery Migrate on 33 percent

A compatibility shim that is only needed when legacy calls are still in use – it sits on 33 percent of pages (Web Almanac 2024), where it marks technical debt rather than function.

core-js on 41 percent

core-js is present on 41 percent of pages (Web Almanac 2024), usually pulled in unnoticed through the build tool chain. What nobody added deliberately, nobody maintains deliberately either.

Legacy JavaScript on mobile pages

More than two thirds of mobile pages still ship transformed legacy JavaScript (Web Almanac 2024). That is compiled output whose origin can no longer be read from the deployed state.

Vue 2 unmaintained since the end of 2023

Vue 2 reached end of life on 31 December 2023 (Vue.js) and has received no fixes since. The files remain on every package mirror and can still be included in a build.

On top of that comes everything that foreign servers reach into your page with: embeds for payment, maps, chat or measurement. Someone else decides their version state, and an end of life there rarely reaches you at all. This class of components belongs in the inventory just as much as your own package – we looked at it separately in our article on third-party scripts, because there the silent replacement of a file is an issue alongside end of life.

The inventory: eight fields per component

A list that only holds names does not help at this point. To turn an inventory into a decision, every component needs the same fields – and they have to be built so that a missing value stays recognisably missing instead of quietly passing as zero. The same logic applies to bought-in services: anyone with providers in the stack needs their evidence in the same structure, as described in our article on NIS2 supply chain vetting.

  1. Name and branch – not the patch version, but the branch the support window is attached to (the PHP branch, for instance, not the third digit).
  2. Origin – distribution package, vendor package, library in the deployment directory, or an embed from a foreign server.
  3. End of life as stated by the vendor – as a date, with a reference to the page it is published on.
  4. Source of that date – the address where the commitment is published; without it, the date is an assertion.
  5. Remaining runtime in months – the only value that produces a meaningful sort order.
  6. Measured yes or no – a field of its own that records whether the value was actually determined or merely estimated.
  7. Accountable role – a role, not a person, so the assignment survives a staffing change.
  8. Successor – the branch you will move to, including its own end of life. Without this field, every move becomes fresh research.
$ php -r 'echo PHP_VERSION_ID, PHP_EOL;'
80400
$ node --version && npm ls --prod --depth=0
v22.14.0
$ composer show --direct --format=json > inventory-composer.json
$ grep -rEho 'jquery[.-][0-9]+\.[0-9]+\.[0-9]+' public/ | sort -u
jquery-3.6.0

Calculating and grading remaining runtime

An inventory turns into an order of work as soon as every component carries a remaining runtime. The arithmetic is trivial; the value behind it is not, because it turns a list into a plan. Python, for example, fixes end of life firmly at five years after a version is released (Python Software Foundation) – the date is therefore settled on release day and can be entered years in advance. What matters is that a value which was not determined stays visibly undetermined. We recommend the same separation between measured and assumed for the health check endpoint, which otherwise returns green answers for dependencies nobody checked.

remaining_runtime.py
from datetime import date

def remaining_months(eol, today=None):
    """Months until end of life. None means: not determined."""
    if eol is None:
        return None
    today = today or date.today()
    return (eol.year - today.year) * 12 + (eol.month - today.month)

def grade(months):
    if months is None:
        return "unmeasured"      # do not treat as 0
    if months < 0:
        return "expired"
    if months <= 3:
        return "critical"
    if months <= 12:
        return "plan"
    return "watch"

inventory = [
    ("php-branch", date(2027, 12, 31)),
    ("node-lts", date(2027, 4, 30)),
    ("frontend-library", None),
]
for name, eol in inventory:
    m = remaining_months(eol)
    print(f"{name}: {grade(m)} ({m if m is not None else 'no date'})")

Four grades are enough to steer by: expired, critical, plan, watch – plus the fifth category unmeasured, which deliberately gets no number. A component without a determined end of life is not a safe component, it is an unknown one. Filling those cases with a zero or a generous default produces a list that looks reassuring throughout. A regular security scan adds the outside view of the same stack: whatever versions become visible from outside belong in the same table as what you know from inside.

Planning the migration before the window closes

A move that only starts after end of life happens under time pressure and reliably produces new defects along the way. The workable order is therefore reversed: the date for the move is derived from the end of life, not from free capacity. In practice, dependencies tend to surface that nobody had on their list – a certificate problem, for instance, that only becomes visible after moving to a newer library, as described in our article on detecting an incomplete certificate chain.

  1. Set the target date: the move is finished before the window closes – not on the same day.
  2. Choose the successor: the specific target version including its own end of life, so the next move is already in the calendar.
  3. Check dependencies: extensions, custom modifications and the build chain against the target version, before dates are promised.
  4. Pull test coverage forward: whatever is not checked before the move gets checked in production afterwards.
  5. Bring security headers along: a migration is the cheapest moment to set the cross-origin headers COOP, COEP and CORP properly.
  6. Define the way back: a documented route back that has been rehearsed without time pressure, not a theoretical one.
  7. File the evidence: date, target version, test result – that is what later proves the support period was upheld.

A component without a determined end of life is not a safe component, it is an unknown one. That difference decides whether a list is a basis for decisions or merely reassurance.

— XICTRAQ

Keeping end-of-life dates in view continuously

An inventory is a cut-off date; end of life is a movement. Between the two lies the reason spreadsheets rarely survive this task: they start ageing the moment they are finished. The CRA component cockpit therefore keeps the inventory running, connects it with hints about reported vulnerabilities, and speaks up when a remaining runtime falls below the configured threshold – 90, 30 and 7 days before the date, instead of once a year when someone reviews the list.

The organisational gain is larger than the technical one. Anyone who can state at any time which components run in which version, and when their window closes, answers customer and auditor questions from the record rather than from memory. Which plans include this capability is listed on the pricing overview, together with the applicable minimum contract term. The effort for the first inventory remains yours – the effort for every following one does not.

Sources and studies

This article draws on: Official Journal of the EU (Cyber Resilience Act, recital on the support period), European Commission (CRA deadlines), PHP Group (Supported Versions), Node.js (Previous Releases), Python Software Foundation (Developer's Guide, Versions), Canonical (Ubuntu release cycle), Debian Project (Releases), Angular (Releases), Laravel (support policy), Symfony (Releases), Microsoft (.NET support policy), OpenSSL (release strategy), MariaDB Foundation (maintenance policy), Vue.js (Vue 2 end of life), W3Techs (Usage of PHP), WordPress.org (PHP statistics), Web Almanac 2024 (JavaScript and CMS chapters) and ENISA (Threat Landscape 2025). The figures quoted are point-in-time values and may change; every number in this article is attributed to its source.

How long is a PHP branch actually supported?
A PHP branch receives regular bug fixes for two years, followed by a phase covering security issues only; after a total of four years support ends completely (PHP Group). For comparison, a Node.js LTS line states that critical fixes are provided for a total of 30 months (Node.js). The windows therefore differ considerably, even though both components often sit in the same project.
Is a component unsafe immediately after end of life?
No. From that day on it is simply without any prospect of a fix. As long as no new flaw becomes known, nothing changes technically; once one does become known, there is no patch for that branch. The risk therefore does not jump, it accumulates – and there is no way to measure or predict when the next report arrives.
Which CRA deadlines matter for end-of-life dates?
The duty to report actively exploited vulnerabilities applies from 11 September 2026, and the main obligations from 11 December 2027 (European Commission). Substantively it also matters that the support period of a product with digital elements should not fall below five years (Official Journal of the EU). Together, that makes end of life a figure you have to document.
Is watching the server software enough?
As a rule, no. Server packages announce themselves through the package manager, libraries in the deployment directory do not, and scripts embedded from foreign servers not at all. An inventory that only queries the package manager misses precisely the part of the stack that ages most quietly.
How often should the inventory be reviewed?
A continuous review works better than an annual appointment, because end-of-life dates and vulnerability reports occur independently of each other. A threshold logic has proved practical: a notice at 90, 30 and 7 days of remaining runtime, plus an immediate notice as soon as a vulnerability affects a component in the inventory.
Why do old front-end libraries appear on the list so often?
Because nobody updates what nobody owns. jQuery appears on 74 percent of pages (Web Almanac 2024) and jQuery Migrate on 33 percent (Web Almanac 2024) – the latter is only needed when legacy code is still in use. A collection of the remaining points is available in our website security checklist.

Related in XICTRAQ

Start in 60 seconds

Let XICTRAQ watch your website.

Permanent free plan available. No credit card, no installation, no hidden fees.