Your frontend, backend, and database — now in one Cloudflare Worker
In September 2024, we introduced beta support for hosting, storing, and serving static assets for free on Cloudflare Workers — something that was previously only possible on Cloudflare Pages. Being able to host these assets — your client-side JavaScript, HTML, CSS, fonts, and images — was a critical missing piece for developers looking to build a full-stack application within a single Worker.
Today we’re announcing ten big improvements to building apps on Cloudflare. All together, these new additions allow you to build and host projects ranging from simple static sites to full-stack applications, all on Cloudflare Workers:
Cloudflare Workers now provides production ready, generally available (GA) support for React Router v7 (Remix), Astro, Hono, Vue.js, Nuxt, Svelte (SvelteKit), and more, with GA support for more frameworks including Next.js, Angular, and SolidJS (SolidStart) to follow in Q2 2025.
You can build complete full-stack apps on Workers without a framework: you can “just use Vite" and React together, and build a backend API in the same Worker. See our Vite + React template for an example.
The adapter for Next.js — @opennextjs/cloudflare, introduced in September 2024 as an early alpha, is now v1.0-beta, and will be GA in the coming weeks. Those using the OpenNext adapter will also be able to easily upgrade to the recently announced Next.js Deployments API.
The Cloudflare Vite plugin is now v1.0 and generally available. The Vite plugin allows you to run Vite’s development server in the Workers runtime (
workerd
), meaning you get all the benefits of Vite, including Hot Module Replacement, while still being able to use features that are exclusive to Workers (like Durable Objects).You can now use static _headers and _redirects configuration files for your applications on Workers, something that was previously only available on Pages. These files allow you to add simple headers and configure redirects without executing any Worker code.
In addition to PostgreSQL, you can now connect to MySQL databases in addition from Cloudflare Workers, via Hyperdrive. Bring your existing Planetscale, AWS, GCP, Azure, or other MySQL database, and Hyperdrive will take care of pooling connections to your database and eliminating unnecessary roundtrips by caching queries.
More Node.js APIs are available in the Workers Runtime — including APIs from the
crypto
,tls
,net
, anddns
modules. We’ve also increased the maximum CPU time for a Workers request from 30 seconds to 5 minutes.You can now bring any repository from GitHub or GitLab that contains a Worker application, and Workers Builds will take care of deploying the app as a new Worker on your account. Workers Builds is also starting much more quickly (by up to 6 seconds for every build).
You can now set up Workers Builds to run on non-production branches, and preview URLs will be posted back to GitHub as a comment.
The Images binding in Workers is generally available, allowing you to build more flexible, programmatic workflows.
These improvements allow you to build both simple static sites and more complex server-side rendered applications. Like Pages, you only get charged when your Worker code runs, meaning you can host and serve static sites for free. When you want to do any rendering on the server or need to build an API, simply add a Worker to handle your backend. And when you need to read or write data in your app, you can connect to an existing database with Hyperdrive, or use any of our storage solutions: Workers KV, R2, Durable Objects, or D1.
If you'd like to dive straight into code, you can deploy a single-page application built with Vite and React, with the option to connect to a hosted database with Hyperdrive, by clicking this “Deploy to Cloudflare” button:

Start with Workers
</a>
</div>
<p>Previously, you needed to choose between building on Cloudflare Pages or Workers (or use Pages for one part of your app, and Workers for another) just to get started. This meant figuring out what your app needed from the start, and hoping that if your project evolved, you wouldn’t be stuck with the wrong platform and architecture. Workers was designed to be a flexible platform, allowing developers to evolve projects as needed — and so, we’ve <a href="https://blog.cloudflare.com/pages-and-workers-are-converging-into-one-experience/"><u>worked to bring pieces of Pages into Workers</u></a> over the years. </p><p>Now that Workers supports both serving static assets <b>and </b>server-side rendering, you should <b>start with Workers</b>. Cloudflare Pages will continue to be supported, but, going forward, all of our investment, optimizations, and feature work will be dedicated to improving Workers. We aim to make Workers the best platform for building full-stack apps, building upon your feedback of what went well with Pages and what we could improve. </p><p>Before, building an app on Pages meant you got a really easy, opinionated on-ramp, but you’d eventually hit a wall if your application got more complex. If you wanted to use Durable Objects to manage state, you would need to set up an entirely separate Worker to do so, ending up with a complicated deployment and more overhead. You also were limited to real-time logs, and could only roll out changes all in one go. </p><p>When you build on Workers, you can immediately bind to any other Developer Platform service (including <a href="https://developers.cloudflare.com/durable-objects/"><u>Durable Objects</u></a>, <a href="https://developers.cloudflare.com/email-routing/email-workers/"><u>Email Workers</u></a>, and more), and manage both your front end and back end in a single project — all with a single deployment. You also get the whole suite of <a href="https://developers.cloudflare.com/workers/observability/"><u>Workers observability</u></a> tooling built into the platform, such as <a href="https://developers.cloudflare.com/workers/observability/logs/workers-logs/"><u>Workers Logs</u></a>. And if you want to rollout changes to only a certain percentage of traffic, you can do so with <a href="https://developers.cloudflare.com/workers/configuration/versions-and-deployments/gradual-deployments/"><u>Gradual Deployments</u></a>. </p><p>These latest improvements are part of our goal to bring the best parts of Pages into Workers. For example, we now support static <a href="https://developers.cloudflare.com/workers/static-assets/headers/"><u>_headers</u></a> and <a href="https://developers.cloudflare.com/workers/static-assets/redirects/"><u>_redirects</u></a> config files, so that you can easily take an existing project from Pages (or another platform) and move it over to Workers, without needing to change your project. We also directly integrate with GitHub and GitLab with <a href="https://developers.cloudflare.com/workers/ci-cd/builds/"><u>Workers Builds</u></a>, providing automatic builds and deployments. And starting today, <a href="https://developers.cloudflare.com/workers/configuration/previews/"><u>Preview URLs</u></a> are <a href="https://developers.cloudflare.com/workers/ci-cd/builds/git-integration/github-integration/#pull-request-comment"><u>posted back to your repository as a comment</u></a>, with feature branch aliases and environments coming soon. </p><p>To learn how to migrate an existing project from Pages to Workers, read our <a href="https://developers.cloudflare.com/workers/static-assets/migrate-from-pages/"><u>migration guide</u></a>. </p><p>Next, let’s talk about how you can build applications with different rendering modes on Workers. </p>
<div>
<h2>Building static sites, SPAs, and SSR on Workers</h2>
<a href="#building-static-sites-spas-and-ssr-on-workers">
</a>
</div>
<p>As a quick primer, here are all the architectures and rendering modes we’ll be discussing that are supported on Workers: </p><ul><li><p><b>Static sites</b>: When you visit a static site, the server immediately returns pre-built static assets — HTML, CSS, JavaScript, images, and fonts. There’s no dynamic rendering happening on the server at request-time. Static assets are typically generated at build-time and served directly from a <a href="https://www.cloudflare.com/learning/cdn/what-is-a-cdn/"><u>CDN</u></a>, making static sites fast and easily cacheable. This approach works well for sites with content that rarely changes. </p></li><li><p><b>Single-Page Applications (SPAs)</b>: When you load an SPA, the server initially sends a minimal HTML shell and a JavaScript bundle (served as static assets). Your browser downloads this JavaScript, which then takes over to render the entire user interface client-side. After the initial load, all navigation occurs without full-page refreshes, typically via client-side routing. This creates a fast, app-like experience. </p></li><li><p><b>Server-Side Rendered (SSR) applications</b>: When you first visit a site that uses SSR, the server generates a fully-rendered HTML page on-demand for that request. Your browser immediately displays this complete HTML, resulting in a fast first page load. Once loaded, JavaScript "<a href="https://en.wikipedia.org/wiki/Hydration_(web_development)"><u>hydrates</u></a>" the page, adding interactivity. Subsequent navigations can either trigger new server-rendered pages or, in many modern frameworks, transition into client-side rendering similar to an SPA.</p></li></ul><p>Next, we’ll dive into how you can build these kinds of applications on Workers, starting with setting up your development environment. </p>
<div>
<h3>Setup: build and dev</h3>
<a href="#setup-build-and-dev">
</a>
</div>
<p>Before uploading your application, you need to bundle all of your client-side code into a directory of <b>static assets</b>. Wrangler bundles and builds your code when you run <code>wrangler dev</code>, but we also now support Vite with our <a href="https://www.npmjs.com/package/@cloudflare/vite-plugin"><u>new Vite plugin</u></a>. This is a great option for those already using Vite’s build tooling and development server — you can continue developing (and testing with <a href="https://developers.cloudflare.com/workers/testing/vitest-integration/"><u>Vitest</u></a>) using Vite’s development server, all using the Workers runtime. </p><p>To get started using the Cloudflare Vite plugin, you can scaffold a React application using Vite and our plugin, by running: </p>
<pre><code>npm create cloudflare@latest my-react-app -- --framework=react</code></pre>
<p>When you open the project, you should see a directory structure like this: </p>
<pre><code>...
├── api │ └── index.ts ├── public │ └── … ├── src │ └── … … ├── index.html ├── package.json ├── vite.config.ts └── wrangler.jsonc
If you run npm run build
, you’ll see a new folder appear, named /dist
.
…
├── api
│ └── index.ts
├── dist
│ └── …
├── public
│ └── …
├── src
│ └── …
…
├── index.html
├── package.json
├── vite.config.ts
└── wrangler.jsonc
The Vite plugin informs Wrangler that this /dist
directory contains the project’s built static assets — which, in this case, includes client-side code, some CSS files, and images.
Once deployed, this single-page application (SPA) architecture will look something like this:

When a request comes in, Cloudflare looks at the pathname and automatically serves any static assets that match that pathname. For example, if your static assets directory includes a blog.html
file, requests for example.com/blog
get that file.
Static sites
</a>
</div>
<p>If you have a static site created by a static site generator (SSG) like <a href="https://docs.astro.build/en/concepts/why-astro/"><u>Astro</u></a>, all you need to do is create a <code>wrangler.jsonc</code> file (or <code>wrangler.toml</code>) and tell Cloudflare where to find your built assets: </p>
<pre><code>// wrangler.jsonc
{ “name”: “my-static-site”, “compatibility_date”: “2025-04-01”, “assets”: { “directory”: “./dist”, } }
Once you’ve added this configuration, you can simply build your project and run wrangler deploy. Your entire site will then be uploaded and ready for traffic on Workers. Once deployed and requests start flowing in, your static site will be cached across Cloudflare’s network.

You can try starting a fresh Astro project on Workers today by running:
npm create cloudflare@latest my-astro-app – –framework=astro
You can see our other supported Frameworks and how to get started in our framework guides.
Single-page applications (SPAs)
</a>
</div>
<p>If you have a single-page application, you can explicitly enable <code>single-page-application</code> mode in your Wrangler configuration: </p>
<pre><code>{
“name”: “example-spa-worker-hyperdrive”, “main”: “api/index.js”, “compatibility_flags”: [“nodejs_compat”], “compatibility_date”: “2025-04-01”, }, “assets”: { “directory”: “./dist”, “binding”: “ASSETS”, “not_found_handling”: “single-page-application” }, “hyperdrive”: [ { “binding”: “HYPERDRIVE”, “id”: “d9c9cfb2587f44ee9b0730baa692ffec”, “localConnectionString”: “postgresql://myuser:mypassword@localhost:5432/mydatabase” } ], “placement”: { “mode”: “smart” } }
By enabling this, the platform assumes that any navigation request (requests which include a Sec-Fetch-Mode: navigate
header) are intended for static assets and will serve up index.html
whenever a matching static asset match cannot be found. For non-navigation requests (such as requests for data) that don’t match a static asset, Cloudflare will invoke the Worker script. With this setup, you can render the frontend with React, use a Worker to handle back-end operations, and use Vite to help stitch the two together. This is a great option for porting over older SPAs built with create-react-app
, which was recently sunset.
Another thing to note in this Wrangler configuration file: we’ve defined a Hyperdrive binding and enabled Smart Placement. Hyperdrive lets us use an existing database and handles connection pooling. This solves a long-standing challenge of connecting Workers (which run in a highly distributed, serverless environment) directly to traditional databases. By design, Workers operate in lightweight V8 isolates with no persistent TCP sockets and a strict CPU/memory limit. This isolation is great for security and speed, but it makes it difficult to hold open database connections. Hyperdrive addresses these constraints by acting as a “bridge” between Cloudflare’s network and your database, taking care of the heavy lifting of maintaining stable connections or pools so that Workers can reuse them. By turning on Smart Placement, we also ensure that if requests to our Worker originate far from the database (causing latency), Cloudflare can choose to relocate both the Worker—which handles the database connection—and the Hyperdrive “bridge” to a location closer to the database, reducing round-trip times.
SPA example: Worker code
Let’s look at the “Deploy to Cloudflare” example at the top of this blog. In api/index.js
, we’ve defined an API (using Hono) which connects to a hosted database through Hyperdrive.
import { Hono } from “hono”;
import postgres from “postgres”;
import booksRouter from “./routes/books”;
import bookRelatedRouter from “./routes/book-related”;
const app = new Hono();
// Setup SQL client middleware
app.use("*", async (c, next) => {
// Create SQL client
const sql = postgres(c.env.HYPERDRIVE.connectionString, {
max: 5,
fetch_types: false,
});
c.env.SQL = sql;
// Process the request
await next();
// Close the SQL connection after the response is sent
c.executionCtx.waitUntil(sql.end());
});
app.route("/api/books", booksRouter);
app.route("/api/books/:id/related", bookRelatedRouter);
export default {
fetch: app.fetch,
};
When deployed, our app’s architecture looks something like this:

If Smart Placement moves the placement of my Worker to run closer to my database, it could look like this:
Server-Side Rendering (SSR)
</a>
</div>
<p>If you want to handle rendering on the server, we support a number of popular full-stack <a href="https://developers.cloudflare.com/workers/frameworks/"><u>frameworks</u></a>. </p><p>Here’s a version of our previous example, now using React Router v7’s server-side rendering:</p><a href="https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/templates/tree/main/react-router-postgres-ssr-template"><img src="https://deploy.workers.cloudflare.com/button" /></a>
You could also use Next.js with the OpenNext adapter, or any other framework listed in our framework guides.