Key Takeaways
- Your exposure is wider than Flask itself: Flask pulls in six direct dependencies, each with its own CVE history.
- Rushed migrations carry real costs: Compressed upgrades break extensions, force Python version bumps, and overrun their estimates.
- End of life does not have to mean unpatched: Extended support keeps EOL Flask versions receiving security fixes while you plan the upgrade.
If you’re running Flask in production, you’ve probably had that moment. Maybe it was a pip-audit warning that flagged an old version in your requirements.txt. Maybe it was a security team email asking about your framework versions. Maybe it was a colleague forwarding a CVE advisory with “Does this affect us?” in the subject line.
And then comes the question nobody wants to dig into: “How old is the Flask version we’re running, and what would it take to update it?”
Pallets, the organization behind Flask, supports only the latest feature release. If you aren’t on it, you’re in unsupported territory, and the risks are more significant than many teams realize. But Flask EOL doesn’t have to mean panic mode. You have more options than you might think. Let’s walk through what running an unsupported Flask version means for your production environment and, more importantly, what you can realistically do about it without setting your engineering roadmap on fire.
How Flask Releases Actually Work
Before we get into the “what do we do about this” part, it’s worth understanding how Flask manages its version lifecycle, because it differs from most frameworks in a way that has direct implications for your security posture.
Flask doesn’t follow a long-term support model with defined EOL dates the way Angular or Node.js do. Instead, Pallets (the organization that maintains Flask) publishes a Version Support Policy that is short and explicit: the latest feature version receives bug and security fixes, and older feature versions do not. Depending on severity and the level of effort involved, a security fix may be backported one version on request, but that is not guaranteed. In practice, when a new feature release ships, the previous line stops receiving updates, security patches included.
The release history looks like this: version 1.0 arrived in April 2018, followed by 1.1 in July 2019. Flask 2.0 shipped in May 2021, adding support for async view functions and raising minimum versions across the Pallets ecosystem. Versions 2.1, 2.2, and 2.3 followed through 2022 and 2023. Flask 3.0 landed in September 2023, requiring Werkzeug 3.0 and building on the API cleanup that began with Flask 2.3. Flask 3.1.0 was released in November 2024, and the latest release as of August 2026 is Flask 3.1.3, published in February 2026 – itself a security fix release.
That policy has a specific consequence. The current feature line is 3.1.x, so you need to be on its latest fix release to be fully supported. Anything on 3.0.x or earlier sits outside the supported window, and newly disclosed vulnerabilities affecting those lines should not be expected to receive an upstream fix.
What Flask EOL Really Means for Your Production API
Okay, so your version isn’t the latest. Your application is running fine, your endpoints are responding, and your CI pipeline is green. So what’s the problem?
Vulnerabilities don’t stop being discovered just because a release line stops receiving patches. And every published advisory states exactly which versions are affected, which means an unsupported deployment accumulates documented, permanently unpatched weaknesses that anyone can look up.
Flask’s own CVE history makes this concrete. The most widely felt example is CVE-2023-30861 (CVSS 7.5, High), a session cookie disclosure issue that triggered security bulletins from vendors shipping Flask inside enterprise products, including IBM Maximo Application Suite.
The underlying defect was narrow. Flask sets a Vary: Cookie response header when the session is used, which tells caches not to serve that response to a different user. In one permanent-session scenario, Flask refreshed the session cookie without treating the session as accessed, so the header was missing while a Set-Cookie header was present. Given a caching proxy that doesn’t strip cookies, a permanent session, SESSION_REFRESH_EACH_REQUEST left at its default, and no explicit Cache-Control header, a response carrying one user’s session cookie could be cached and served to another user. Where the session cookie controls access to an authenticated account, that isn’t a theoretical edge case.
Pallets fixed it in Flask 2.3.2 and 2.2.5 – the then-current feature line and the one before it, exactly as the support policy describes. Nothing was published for 2.1.x or earlier, so teams on those versions had no upstream fix to install.
If that sounds like history, it isn’t. In February 2026, Pallets disclosed CVE-2026-27205, the same class of issue: the session wasn’t marked as accessed when code only checked its keys, using operators such as in and len, so Vary: Cookie was again omitted under comparable caching conditions. It affects every Flask release before 3.1.3 – which is to say every end-of-life version running today. Flask 3.1.1 had already shipped a separate session-signing fix (CVE-2025-47278) in May 2025. Three security releases in three years, and only the current line received them.
Compliance frameworks add a second dimension. PCI DSS v4.0.1 Requirement 6.3.3 requires patches for critical vulnerabilities to be installed within one month of release. When no patched release exists for your version, that clock never starts and the finding stays open. SOC 2 and similar control-based assessments don’t ban unsupported components outright, but they do expect a documented risk assessment, compensating controls, or a credible route to receiving fixes. Running an unsupported Flask version with none of those in place is a gap an assessor will ask about.
The Hidden Risk: Flask’s Dependency Tree
When teams think about Flask security, they think about the framework itself: routing, the request context, session handling. That’s the obvious layer.
But the framework doesn’t run in isolation. Installing current Flask also installs six direct runtime dependencies: Werkzeug (the WSGI layer and request parsing), Jinja2 (templating), MarkupSafe (escaping strings for safe inclusion in markup), ItsDangerous (session cookie signing), Click (CLI tooling), and Blinker (signals). On Python 3.9 there’s a seventh, importlib-metadata. Each has its own CVE history and release cadence, and the exact set varies by Flask version – Blinker, for example, only became mandatory in Flask 2.3.
What makes this tricky is how tightly the versions are tied together. Flask 3.1.0 requires Werkzeug 3.1 or newer, ItsDangerous 2.2 or newer, and Blinker 1.9 or newer. Flask specifies minimum versions rather than exact pins, so an older Flask release doesn’t freeze your whole tree by itself. The practical constraint comes from the other direction: a vulnerability in Werkzeug’s request parsing or ItsDangerous’s signing logic affects your application as directly as a flaw in Flask, and upgrading that one package on its own can break compatibility with your Flask version, your extensions, or your own code.
You can also learn more about Python security issues and the risks that can affect applications across the broader Python ecosystem.
Your Options When Flask EOL Arrives
Alright, so you’re running an unsupported Flask version. What are the realistic paths forward?
There are three, and they trade off differently. Upgrading or migrating reduces long-term legacy risk but can require substantial engineering work. Extended support closes the immediate security gap and buys time to do that work properly.
Option 1: Upgrade to the Latest Supported Flask Release
This is the official answer: move to the current release line (Flask 3.1.x as of this writing), get the latest security patches, and stay current. For teams with well-maintained codebases, it’s the right long-term move.
Flask 3.x brings real improvements. Support for async view functions, introduced in 2.0, has continued to develop, though Flask remains a WSGI framework rather than an async-first one. The Sans-IO restructuring in 3.0 separated Flask’s core logic from its WSGI plumbing, which is what allows projects such as Quart to reuse it. Dependency versions are current and maintained. If you can upgrade cleanly, you should.
But here’s what “upgrade to the latest version” involves when you’re coming from Flask 1.x or early 2.x:
Python version requirements: Flask 3.0 requires Python 3.8 at minimum, and Flask 3.1 raised that floor to Python 3.9, dropping 3.8 entirely. If your production environment runs an older Python – common in legacy applications and in containers built on pinned base images – a Python upgrade becomes a prerequisite before you touch Flask.
Broken extensions: This is the big one. Flask’s ecosystem includes Flask-Login, Flask-SQLAlchemy, Flask-Migrate, Flask-Admin, and dozens of other extensions, each with its own compatibility requirements. When Flask 3.0 shipped alongside Werkzeug 3.0 on September 30, 2023, Werkzeug removed the long-deprecated url_decode and url_encode helpers that Flask-Login 0.6.2 imported at module load. Applications that picked up the new Werkzeug failed to start with an ImportError, and the fix didn’t land until Flask-Login 0.6.3 on October 30 – a month of breakage for one of Flask’s most widely used extensions. Any application relying on more than a handful of extensions carries that same coordination risk through every major upgrade.
Removed deprecated APIs: Flask 2.3.0 removed a batch of APIs that had been deprecated in 2.2, including app.before_first_request, bp.before_app_first_request, the json_encoder and json_decoder attributes, the FLASK_ENV environment variable, and the send_file_max_age_default, use_x_sendfile, and propagate_exceptions properties on the app. Flask 3.0 didn’t reintroduce any of them. Application code or extensions still using these APIs will break as soon as your upgrade path reaches 2.3. That matters most when jumping from 1.x or early 2.x straight to 3.x: you inherit the 2.3 removals and the 3.0 changes in the same migration.
Werkzeug compatibility cascade: Because Werkzeug is so tightly coupled to Flask, upgrading Flask generally means upgrading Werkzeug, which can affect extensions, custom middleware, and any code that touches Werkzeug internals directly.
For a small, actively maintained Flask application, this might be a focused sprint. For a large API with years of accumulated patterns, custom extensions, and an uptime requirement during migration, it means careful planning, comprehensive testing, and a real possibility of production regressions.
Understand Werkzeug Security before upgrading Flask.
Option 2: Migrate to an Alternative Framework
Some teams look at a Flask upgrade and think, “maybe this is the moment to reconsider the whole thing.”
FastAPI draws attention for its automatic API documentation, native async support, and Pydantic-based validation. Django REST Framework offers a batteries-included approach for teams that prefer convention over configuration. Starlette provides a lightweight ASGI foundation for custom API stacks.
There are genuine technical arguments for each, depending on your requirements and your team’s direction.
But be clear about the scope. You’re not swapping a dependency. You’re rewriting route handlers against a different API surface, replacing Flask’s request context model, reworking authentication and session handling, and finding equivalents for the Flask extensions you depend on – Flask-Login, Flask-SQLAlchemy, Flask-Migrate – or rebuilding that logic yourself.
The long-term benefits can justify the work in the right context. The timeline and cost tend to exceed initial estimates once the full scope is mapped, and maintaining the existing Flask application while building its replacement adds its own overhead.
Option 3: Third-Party Extended Support
The option teams overlook most often is extended security support from a specialist vendor.
TuxCare provides Endless Lifecycle Support (ELS) for end-of-life Flask versions. Instead of forcing a migration before you’re ready, or leaving your APIs exposed while you plan one, you continue running a covered Flask version with security patch coverage in place.
The idea is straightforward: you buy time and control. Time to plan a proper upgrade when it makes sense for your team and your application. Control over your migration schedule, instead of having a vulnerability disclosure set it for you at the worst possible moment.
The Real Cost of Rushing a Flask EOL Upgrade
Consider what happens when a team compresses a Flask migration into an unrealistic timeline because a security audit flagged unsupported dependencies.
Increased support load: Compatibility issues that didn’t surface in compressed testing start appearing in production. An extension that worked in the test environment behaves differently under load. Your support queue fills with issues that trace back to the migration, and diagnosing them requires deep context about what changed in which dependency.
System instability: A Werkzeug version bump changes how multipart form data is parsed, and an upload endpoint starts rejecting files it used to accept. A session handling change causes intermittent authentication failures that only appear under specific load patterns. These are the issues that generate 3 a.m. pages and missed SLAs.
Technical debt accumulation: In the rush to get compliant, teams take shortcuts. Deprecated API usage gets wrapped in compatibility shims instead of being refactored properly. “We’ll clean this up after the migration” becomes permanent, and six months later you’re running a technically current codebase full of workarounds and deferred cleanup.
Budget overruns: The original estimate was two weeks. Five weeks in, you’re still tracking down extension compatibility issues, the contractor is still billing, features have been pushed, and engineers who should be building new capabilities are debugging version conflicts.
The difference between a well-planned Flask upgrade and a panic-driven one isn’t subtle. It’s the difference between a controlled project with a defined scope and a rolling emergency that consumes your team’s capacity for months.
Keeping End-of-Life Flask Secure with TuxCare
This is where TuxCare’s Endless Lifecycle Support changes the equation.
TuxCare has spent 15 years delivering security patches for end-of-life operating systems, runtimes, libraries, and application frameworks. It reports more than 220,000 patches shipped and over 5,000 CVEs resolved across some 10,000 packages for more than 2,300 enterprise clients – a track record built in production environments rather than a set of promises about them.
What Makes TuxCare’s Approach Different
Coverage across the dependency chain: End-of-life risk rarely sits only in the top-level package. For a Flask application it’s spread across Werkzeug, Jinja2, MarkupSafe, ItsDangerous, and whatever sits beneath them. When a covered dependency needs a security fix, TuxCare patches, rebuilds, and validates the affected packages together, rather than leaving you to update one component and find out what it breaks. Direct and transitive dependency coverage is included at no additional cost.
Automated backporting with expert validation: TuxCare tracks the NVD, vendor advisories, and GitHub security feeds, then backports fixes using algorithmic and AI-assisted automation with security engineers validating the result. Packages go through integrity, functionality, and regression testing and are signed before they reach customer repositories. To see how the process works end to end, read TuxCare’s technical whitepaper on its Open Source Patching Process.
Delivery through your existing pip workflow: Patched packages are distributed from a TuxCare-hosted repository that you configure as a pip index or extra index. You’re not overhauling your build pipeline, retraining your team, or changing your deployment process – just pointing at a source that keeps producing fixes after Pallets moves on.
SLA-backed response times: TuxCare’s published targets for Endless Lifecycle Support for Libraries are patches within 14 days of public disclosure for high- and critical-risk vulnerabilities (CVSS 7.0 and above), 60 days for medium risk, and 90 days for low risk. You’re working from documented timelines you can plan a remediation window around, with 24/7 support behind them.
Predictable cost: Extended support is priced at a fixed rate for the contract term, which makes it straightforward to compare against a migration. Set that figure beside the engineering hours, testing cycles, contractor fees, potential downtime, and delayed features an accelerated upgrade would require, and you’re comparing two real numbers instead of one number and a hope.
Audit-ready documentation: Each release comes with information on which CVEs were addressed and which package versions changed, plus machine-readable SBOMs (SPDX and CycloneDX) and VEX documents. That’s the evidence vulnerability-management and change-control reviews ask for, though the specific requirements always depend on the framework, your system boundary, and your assessor.
How to Get Started
If you’re running an end-of-life Flask version and want to understand your exposure, TuxCare maintains a CVE tracker showing the vulnerabilities associated with covered Flask versions and the fixes released for them. Published coverage currently includes Flask 0.12.5, 1.1.2, 1.1.4, and 2.2.1, with additional versions available on request.
Check vulnerabilities and available ELS fixes for Flask.
Don’t Let Flask EOL Put Your APIs at Risk
Supporting only the latest feature release is a pragmatic policy for a small open-source team. It’s less convenient for organizations running production applications that can’t upgrade every time a new release ships.
What matters is that Flask EOL doesn’t have to mean scrambling. You have options. You can plan a thoughtful upgrade when it suits your team, evaluate a different framework if there’s a real architectural reason to move, or use extended support to keep receiving security fixes and supporting your compliance obligations while you make those decisions on your own schedule rather than an auditor’s.
The teams that handle Flask EOL well are the ones that recognize the risk early, understand the options available to them, and make deliberate choices instead of reactive ones.
If Flask EOL is on your radar, it’s worth finding out what TuxCare’s Endless Lifecycle Support covers. The best next step may be securing the version you already run while you plan the upgrade you actually want.

