diff --git a/.optimize-cache.json b/.optimize-cache.json
index 828a3aaef7..8f1ef6e6b5 100644
--- a/.optimize-cache.json
+++ b/.optimize-cache.json
@@ -906,6 +906,7 @@
"static/images/blog/nextjs-starter-sites/deployed.png": "f3b309dd4747796f1a8272635096a29e01314fbced8f26222afa60185b3af16d",
"static/images/blog/nextjs-starter-sites/deployment-logs.png": "2873669c7ab6b816bf574d190a8a51b0c1edfd35b28423022fbf64055d97cac2",
"static/images/blog/nextjs-starter-sites/template.png": "0138c956f06c8275b87d103c1b5bdf54b6cf31d42285c43f5caec5b5d497421c",
+ "static/images/blog/nextjs-vs-react-explained-which-should-developers-use/cover.png": "a010768c8bcbf2b55fe57d47790aeaea9e167345cfa7f04581f66e01d2071991",
"static/images/blog/nodejs-v25-whats-new/cover.png": "49e3fa3a669bdd6d5d1aa1eea54693ea75c9c98aa8a1e3035e1c19c97598f007",
"static/images/blog/nuxt-starter-sites/add-platform.png": "3b13ba983ea1d2529a1f34a719acef903ec0b58879ed511012280a28ccbde17e",
"static/images/blog/nuxt-starter-sites/congrats.png": "5dfa4f03b67e0110936126f36329a17aa37bdfc4f148d61deb9aacf5d10d0981",
@@ -1272,6 +1273,8 @@
"static/images/blog/what-is-ciam/cover.png": "45a5261ae1bb8a38777f60a21ea60426c0832e3d58bf3164100548400d388ce1",
"static/images/blog/what-is-cicd-a-complete-guide-for-developers/cover.png": "7abddce55b1467188faab83abd58189173bf9aba84de3d9f28fff0be8c6e9276",
"static/images/blog/what-is-cloud-storage-an-expert-guide-for-developers/cover.png": "b7f545dbe9334d60f214e748ddfcea47484530a97f30ecf579d064a053c2821d",
+ "static/images/blog/what-is-cors-cross-origin-resource-sharing-explained/cover.png": "6d67ffa35a995451b908b2adb8ee4e45c73cb8a588b20404585c78fc8bf87d12",
+ "static/images/blog/what-is-database-caching-a-beginners-guide/cover.png": "d4c8a0be9a1b0167e295648260779e4a26ad3a1957653f4cb16268f724c25664",
"static/images/blog/what-is-docker-a-simple-guide-for-developers/cover.png": "acd9c50ad749fcf676dd58b38cc6bbffba913bf5d817c6b725bd2c305088689e",
"static/images/blog/what-is-kubernetes-an-expert-guide-for-developers/cover.png": "a7601f375841b143d62511fe3bbfb4bacb6989ddc56613f772b7dcd3d5e90688",
"static/images/blog/what-is-mcp/claude-mcp-chat.png": "26842cfebca3ec2cec89448e1c0d7ddb3f5421cc57acdb8780d48d30a54cad82",
diff --git a/src/routes/blog/post/nextjs-vs-react-explained-which-should-developers-use/+page.markdoc b/src/routes/blog/post/nextjs-vs-react-explained-which-should-developers-use/+page.markdoc
new file mode 100644
index 0000000000..6466ccd21b
--- /dev/null
+++ b/src/routes/blog/post/nextjs-vs-react-explained-which-should-developers-use/+page.markdoc
@@ -0,0 +1,174 @@
+---
+layout: post
+title: "Next.js vs. React explained: Which should developers use?"
+description: Compare React and Next.js to understand their key differences, what Next.js adds, and which option best fits your app’s SEO, performance, and project needs.
+date: 2026-07-21
+cover: /images/blog/nextjs-vs-react-explained-which-should-developers-use/cover.avif
+timeToRead: 5
+author: aditya-oberai
+category: comparisons
+featured: false
+unlisted: true
+faqs:
+ - question: Is Next.js better than React?
+ answer: Next.js is better for projects that need SEO, server-side rendering, file-based routing, and production features out of the box. React is better when you want maximum flexibility or are building a client-side application that does not need server rendering.
+ - question: What is the main difference between React and Next.js?
+ answer: React is a JavaScript library for building user interfaces. Next.js is a full-stack framework built on React that adds routing, server rendering, backend functions, and build optimizations.
+ - question: Is Next.js built on React?
+ answer: Yes. Next.js uses React for building components and adds the infrastructure needed to route, render, optimize, and deploy complete web applications.
+ - question: Can I use Next.js without React?
+ answer: No. Next.js is built on React, so every Next.js application uses React underneath.
+ - question: Is Next.js faster than React?
+ answer: Next.js can provide faster initial page loads through server rendering, static generation, and React Server Components. A lightweight React application may perform equally well for highly interactive applications where most content loads after sign-in.
+---
+Ask a room of developers whether they use Next.js or React, and half of them will say "both" without realizing the question has a real answer. The confusion is understandable: Next.js is built on top of React, so the line between them blurs the moment you start a project. But they solve different problems, and picking the wrong one adds complexity you'll pay for later.
+
+This is the core of the Next.js vs. React debate. **React** is a library for building user interfaces. **Next.js** is a framework that wraps React with routing, rendering, and infrastructure decisions already made for you. Knowing which layer you actually need saves you from either reinventing a framework or fighting one you didn't want.
+
+This article breaks down what each does, what Next.js adds on top of React, and how to decide which fits your project.
+
+# What is React?
+
+React is an open-source JavaScript library for building user interfaces, maintained by Meta. It gives you a component model, a virtual DOM for efficient updates, and hooks like `useState` and `useEffect` for managing state and side effects. That's the scope: React renders components and keeps the UI in sync with your data.
+
+What React deliberately does **not** include is just as important:
+
+- No routing. You add a library like React Router yourself.
+- No built-in data fetching conventions or server rendering out of the box.
+- No opinion on your build tooling, folder structure, or deployment.
+
+React is unopinionated by design. You assemble the rest of the stack from separate packages. That flexibility is powerful when you know what you're building, and a burden when you'd rather not make a dozen infrastructure decisions before writing a feature.
+
+```jsx
+import { useState } from "react";
+
+export default function Counter() {
+ const [count, setCount] = useState(0);
+ return ;
+}
+```
+
+# What is Next.js?
+
+Next.js is a React framework built by Vercel that adds structure and infrastructure on top of the React library. It ships with a router, multiple rendering strategies, a build system, and production optimizations, so you start from a working full-stack setup instead of an empty canvas.
+
+When you use Next.js, you're still writing React components. The difference is that Next.js decides how those components are routed, rendered, and served. It fills in the gaps React leaves open, which is why so many teams reach for it by default.
+
+Next.js has two routing systems: the older **Pages Router** and the newer **App Router**, which is built around [React Server Components](/blog/post/client-vs-server-components-react) and is the recommended default in current versions. Recent releases have leaned further into server-first rendering, streaming, and partial pre-rendering. If you want a deeper look at the latest changes, see [everything new in Next.js 16](/blog/post/everything-new-in-nextjs16).
+
+# Next.js vs. React: what's the core difference?
+
+**React is a library; Next.js is a framework built on React.** A library gives you a tool to solve one problem (rendering UI). A framework gives you a structure and makes the surrounding decisions for you (routing, rendering, bundling, and deployment conventions).
+
+Put simply: you can use React without Next.js, but you can't use Next.js without React.
+
+Here's how the two compare across the decisions that matter most:
+
+| | React | Next.js |
+| --- | --- | --- |
+| Type | UI library | Full-stack framework built on React |
+| Routing | Not included (add React Router) | File-based routing included |
+| Rendering | Client-side by default | SSR, SSG, ISR, and RSC out of the box |
+| SEO | Requires extra setup for server rendering | Strong defaults for crawlable HTML |
+| Data fetching | Bring your own pattern | Server components and built-in conventions |
+| Backend/API | Separate service required | API routes and server functions included |
+| Build tooling | You configure it (Vite, etc.) | Preconfigured and optimized |
+| Flexibility | High, you assemble the stack | Opinionated, decisions made for you |
+
+The trade-off is straightforward. React gives you maximum control and minimum assumptions. Next.js gives you speed and sensible defaults at the cost of some flexibility.
+
+# What Next.js adds on top of React
+
+Since Next.js is React underneath, the useful comparison is not "which renders UI better." It's "what does Next.js hand you that you'd otherwise build yourself." The main additions:
+
+- **File-based routing:** Create a file in the right directory and it becomes a route. No route configuration to maintain by hand.
+- **Multiple rendering modes:** Server-side rendering (SSR), static site generation (SSG), incremental static regeneration (ISR), and client-side rendering, chosen per route. Plain React is client-side rendered unless you build the server layer yourself.
+- **React Server Components:** Render components on the server and ship less JavaScript to the browser. See our breakdown of [client vs. server components in React](/blog/post/client-vs-server-components-react).
+- **API routes and server functions:** Write backend endpoints inside the same project, so light server logic doesn't require a separate service.
+- **Built-in optimizations:** Automatic code splitting, image optimization, and font optimization that you'd otherwise wire up manually.
+- **Preconfigured build tooling:** Bundling, transpilation, and production builds work out of the box.
+
+If you find yourself installing React Router, setting up a custom SSR pipeline, and hand-tuning your bundler on top of a React project, you're rebuilding a large part of what Next.js already provides.
+
+# Next.js vs. React for SEO and performance
+
+**For SEO, Next.js is the stronger default.** A standard client-side React app sends the browser a near-empty HTML shell and builds the page with JavaScript after load. Search crawlers and social preview bots can struggle with content that only appears after the bundle executes, and the page metadata isn't present in the initial HTML.
+
+Next.js renders HTML on the server (or at build time), so crawlers receive fully formed content and metadata on the first request. That directly benefits indexing, link previews, and pages where organic search matters. This is the same SSR vs. CSR trade-off covered in [SSR vs. CSR with Next.js](/blog/post/csr-vs-ssr-with-nextjs).
+
+On raw performance, the picture is more nuanced:
+
+- **Initial load:** Next.js typically wins because the browser can paint server-rendered HTML immediately instead of waiting for JavaScript to fetch data and build the DOM.
+- **Bundle size:** Server components in Next.js ship less JavaScript to the client, which speeds up interaction readiness on content-heavy pages.
+- **Highly interactive apps:** For a dashboard behind a login where SEO is irrelevant, a lean client-side React app can perform just as well without the framework overhead.
+
+Performance is not automatically better with either choice. It depends on whether your app is content-first (favoring Next.js) or interaction-first (where plain React is fine).
+
+# When should you use React on its own?
+
+Reach for React without a framework when:
+
+- You're building a **single-page application** behind authentication, like an internal dashboard or admin panel, where SEO doesn't matter.
+- You want **full control** over your stack and are comfortable choosing your own router, data layer, and build setup.
+- You're adding interactivity to **part of an existing page** rather than building a full site.
+- You're targeting a **non-web platform**, where React Native, not Next.js, is the relevant tool.
+
+A modern React app with Vite starts fast and stays lightweight. If you don't need server rendering or file-based routing, adding a framework is complexity you won't use.
+
+# When should you use Next.js?
+
+Choose Next.js when:
+
+- **SEO matters:** marketing sites, blogs, e-commerce, documentation, or any page you want indexed and shared with rich previews.
+- You want **routing, rendering, and tooling decided for you** so you can start shipping features immediately.
+- Your app benefits from a **mix of static and dynamic pages**, using SSG for content and SSR for personalized routes.
+- You want to keep **light backend logic** (form handling, webhooks, simple APIs) in the same project via API routes.
+
+For most new web projects that face the public internet, Next.js is the pragmatic default. You get production-ready patterns without assembling them yourself.
+
+# Next.js vs. React: which should developers use?
+
+There's no universally correct answer in the Next.js vs. React decision, but there is a correct answer for your project. Use this as the short version:
+
+- **Public-facing site where SEO and first-load speed matter?** Use Next.js.
+- **Interactive app behind a login where SEO is irrelevant?** React on its own is enough.
+- **Want decisions made for you so you can ship fast?** Next.js.
+- **Want full control over every layer of your stack?** React with the libraries you choose.
+
+Remember that this is not React *or* Next.js as competing technologies. Next.js is React with the surrounding infrastructure filled in. The real question is how much of that infrastructure you want to own yourself versus have handled for you.
+
+# Adding a backend to your Next.js or React app
+
+Whichever you pick, the frontend is only half the story. Both React and Next.js still need somewhere to authenticate users, store data, handle files, and run server-side logic. Next.js API routes cover light backend needs, but a growing app usually wants a real backend rather than stretching route handlers into one.
+
+This is where [Appwrite](/docs) fits. It gives your React or Next.js frontend a complete backend: [Authentication](/docs/products/auth) with OAuth, MFA, and phone login; [Databases](/docs/products/databases) with real-time subscriptions; file [Storage](/docs/products/storage) with per-user permissions; and serverless [Functions](/docs/products/functions). Because Appwrite is client-agnostic, the same setup works whether your frontend is a plain React SPA or a server-rendered Next.js app.
+
+```js
+import { Client, Account } from "appwrite";
+
+const client = new Client()
+ .setEndpoint("https://.cloud.appwrite.io/v1")
+ .setProject("");
+
+const account = new Account(client);
+const user = await account.get();
+```
+
+For SSR in Next.js, Appwrite supports server-side session handling with the Node.js SDK, so authenticated requests work correctly on the server. The [SSR vs. CSR with Next.js](/blog/post/csr-vs-ssr-with-nextjs) guide walks through both patterns in detail.
+
+# Deploy your Next.js or React app with Appwrite
+
+The Next.js vs. React choice comes down to how much infrastructure you want to own. Use React when you need control and don't need server rendering. Use Next.js when SEO, first-load performance, and sensible defaults matter more than raw flexibility. Either way, you'll need a backend and a place to host it.
+
+[Appwrite Sites](/docs/products/sites) hosts both React and Next.js apps with Git-based deployments, global edge distribution, and full support for SSR and React Server Components. Pair it with Appwrite's backend services and your frontend and backend live in one platform. [Sign up for Appwrite Cloud](https://cloud.appwrite.io) for free, or self-host using the [installation guide](/docs/advanced/self-hosting).
+
+Resources:
+
+- [Deploy a Next.js app to Appwrite Sites](/blog/post/deploy-nextjs-app-to-appwrite-sites)
+- [Client vs. server components in React](/blog/post/client-vs-server-components-react)
+- [SSR vs. CSR with Next.js](/blog/post/csr-vs-ssr-with-nextjs)
+- [Everything new in Next.js 16](/blog/post/everything-new-in-nextjs16)
+- [Appwrite Sites documentation](/docs/products/sites)
+- [React documentation](https://react.dev)
+- [Next.js documentation](https://nextjs.org/docs)
+- [Join the Appwrite Discord](https://appwrite.io/discord)
\ No newline at end of file
diff --git a/src/routes/blog/post/what-is-cors-cross-origin-resource-sharing-explained/+page.markdoc b/src/routes/blog/post/what-is-cors-cross-origin-resource-sharing-explained/+page.markdoc
new file mode 100644
index 0000000000..e08a7ef240
--- /dev/null
+++ b/src/routes/blog/post/what-is-cors-cross-origin-resource-sharing-explained/+page.markdoc
@@ -0,0 +1,137 @@
+---
+layout: post
+title: What is CORS? Cross-origin resource sharing explained
+description: Learn what CORS is, why it exists, how it works, what preflight requests are, why CORS errors happen, and how to fix them on the server, in this dev guide.
+date: 2026-07-21
+cover: /images/blog/what-is-cors-cross-origin-resource-sharing-explained/cover.avif
+timeToRead: 5
+author: aditya-oberai
+category: security
+featured: false
+unlisted: true
+faqs:
+ - question: What is CORS in simple terms?
+ answer: CORS is a browser security mechanism that uses HTTP headers to control whether a web page on one origin can read a response from another origin.
+ - question: What does CORS stand for?
+ answer: CORS stands for Cross-Origin Resource Sharing.
+ - question: What causes a CORS error?
+ answer: A CORS error occurs when the server does not return the headers the browser expects for a cross-origin request. Common causes include a missing allowed origin, a failed preflight request, or an origin mismatch.
+ - question: What is the same-origin policy?
+ answer: The same-origin policy is a browser security rule that prevents scripts on one origin from reading responses from another origin unless the server explicitly allows it through CORS.
+---
+# What is CORS? Cross-origin resource sharing explained
+
+You wire up a frontend, call your API, and the browser throws a wall of red: `Access to fetch at 'https://api.example.com' from origin 'https://app.example.com' has been blocked by CORS policy`. The request worked fine in Postman and in `curl`, so it looks like the browser is getting in your way for no reason.
+
+It isn't. **CORS (Cross-Origin Resource Sharing)** is a browser security mechanism that decides whether JavaScript on one origin is allowed to read a response from another origin. The confusing part is that CORS is enforced by the browser, not your server, and the error you see rarely tells you what actually went wrong. This guide explains what CORS is, why it exists, how it works under the hood, what preflight requests are, and how to fix CORS errors properly instead of papering over them with a wildcard.
+
+## What is CORS in simple terms?
+
+**CORS is a set of HTTP headers that lets a server tell the browser which other origins are allowed to read its responses.** Without those headers, the browser blocks cross-origin JavaScript from reading the result, even if the request itself reached the server and returned data.
+
+An **origin** is the combination of scheme, host, and port. These three URLs are all different origins:
+
+* [`https://app.example.com`](https://app.example.com) and [`http://app.example.com`](http://app.example.com) (different scheme)
+* [`https://app.example.com`](https://app.example.com) and [`https://api.example.com`](https://api.example.com) (different host)
+* [`https://app.example.com`](https://app.example.com) and [`https://app.example.com:8080`](https://app.example.com:8080) (different port)
+
+If any of the three differ between the page making the request and the resource being requested, the request is **cross-origin**, and CORS rules apply.
+
+## Why does CORS exist?
+
+To understand CORS, you first have to understand the rule it relaxes: the **same-origin policy**. By default, browsers stop a script loaded from one origin from reading responses from a different origin. This is one of the oldest and most important protections on the web.
+
+The reason is cookies. Browsers automatically attach your cookies to requests sent to a domain, including cross-origin requests. Imagine you are logged into your bank at `bank.com` and then open a malicious page at `evil.com`. Without the same-origin policy, JavaScript on `evil.com` could quietly call `bank.com/account` in the background, ride along on your session cookie, and read your balance and transaction history. That class of attack is the whole reason the boundary exists.
+
+The same-origin policy is deliberately strict, but plenty of legitimate apps need to talk across origins. A React app on `app.example.com` calling an API on `api.example.com` is completely normal. **CORS is the controlled exception**: it gives the server a way to say "these specific origins are allowed to read my responses," so browsers can permit safe cross-origin access without dropping the protection for everyone else.
+
+## How CORS works
+
+CORS is a conversation between the browser and the server carried out through HTTP headers. The browser adds an `Origin` header to cross-origin requests, and the server answers with `Access-Control-*` headers describing what it permits. If the response headers don't cover the request, the browser refuses to hand the response to your JavaScript.
+
+There are two paths this conversation can take: simple requests and preflighted requests.
+
+### Simple requests
+
+**A simple request is one the browser considers safe enough to send directly, without asking permission first.** A request qualifies as simple when it meets all of these conditions:
+
+* The method is `GET`, `HEAD`, or `POST`.
+* The only headers set are ones on the CORS safelist, such as `Accept`, `Accept-Language`, and `Content-Type`.
+* If `Content-Type` is set, it is `text/plain`, `multipart/form-data`, or `application/x-www-form-urlencoded`.
+
+For a simple request, the browser sends it straight to the server with an `Origin` header. The server responds with the data plus an `Access-Control-Allow-Origin` header. If that header matches the requesting origin (or is the wildcard `*`), the browser exposes the response to your code. If it's missing or doesn't match, the browser blocks it and logs a CORS error.
+
+### Preflight requests
+
+The moment a request steps outside the "simple" rules, for example by sending `Content-Type: application/json`, a custom header like `Authorization`, or a method like `PUT`, `PATCH`, or `DELETE`, the browser does something extra first. **This is the preflight request: an automatic `OPTIONS` request the browser sends before the real one to check whether the actual request is allowed.**
+
+The preflight `OPTIONS` request includes headers describing what the real request intends to do:
+
+* `Access-Control-Request-Method` names the HTTP method the real request will use.
+* `Access-Control-Request-Headers` lists the custom headers it will send.
+
+The server responds with the matching permissions:
+
+* `Access-Control-Allow-Origin` for the allowed origin.
+* `Access-Control-Allow-Methods` for the allowed methods.
+* `Access-Control-Allow-Headers` for the allowed request headers.
+* `Access-Control-Max-Age` for how long the browser can cache this preflight result, so it doesn't repeat the check on every call.
+
+Only if the preflight response approves the method, headers, and origin does the browser send the real request. You don't write the preflight yourself; the browser generates it. But your server does have to answer it, which is a common source of errors when an `OPTIONS` handler is missing.
+
+### Requests with credentials
+
+If your request needs to send cookies or HTTP authentication across origins, there are two extra rules. The client must opt in (for example, `fetch(url, { credentials: 'include' })`), and the server must respond with `Access-Control-Allow-Credentials: true`.
+
+There's an important catch here: **when credentials are involved, the wildcard `Access-Control-Allow-Origin: *` is not allowed.** The server has to echo back the exact requesting origin instead. This trips up a lot of developers who get things working with a wildcard and then break authentication the moment they add cookies.
+
+## Why CORS errors happen
+
+Here is the detail that causes the most confusion. **A CORS error almost never means CORS itself is broken. It usually means the response was missing the headers the browser expected, and the reason is often a different failure entirely.**
+
+Because the browser blocks the response before your JavaScript can read it, you can't see the real status code or error body in your code. A misconfigured server, a `500` error, a `404` on the endpoint, or an auth failure can all surface as a generic CORS message even though the underlying problem has nothing to do with origins.
+
+The most common causes are:
+
+* **The server doesn't send CORS headers at all.** The API was never configured to allow cross-origin requests, so no `Access-Control-Allow-Origin` header comes back.
+* **The allowed origin doesn't match.** A trailing slash, `http` vs `https`, or `www` vs the bare domain is enough to fail the check.
+* **No `OPTIONS` handler.** The server returns data on `GET`/`POST` but returns an error or nothing on the preflight `OPTIONS` request, so the real request never fires.
+* **Wildcard plus credentials.** The server sends `Access-Control-Allow-Origin: *` while the client sends credentials, which the browser rejects.
+* **The real error is upstream.** A bad request ID, wrong endpoint, or server exception returns a `4xx`/`5xx` without CORS headers, and the browser reports it as a CORS problem.
+
+To see what's really happening, open your browser's **Network tab** and inspect the failed request. The raw status code and response body there will tell you far more than the console's CORS message. If you're debugging this against an Appwrite backend specifically, [Solving CORS errors with Appwrite](/blog/post/cors-error) walks through the exact causes and fixes.
+
+## How to fix CORS errors on the server
+
+**CORS is fixed on the server, not the client.** You cannot disable CORS from your frontend code, and you shouldn't try to route around it with browser flags or extensions, because that only hides the problem on your machine while every real user still hits the wall.
+
+The correct fix is to configure the server to return the right headers:
+
+* **Set `Access-Control-Allow-Origin` to your frontend's exact origin.** For production, list the specific domains you serve from rather than using `*`. A wildcard means any website on the internet can call your API, which defeats the purpose of the same-origin policy.
+* **Handle the `OPTIONS` preflight request.** Return a `200`/`204` with `Access-Control-Allow-Methods` and `Access-Control-Allow-Headers` that cover what your client actually sends.
+* **Echo the origin when using credentials.** If you send cookies or `Authorization` headers, respond with the specific origin and `Access-Control-Allow-Credentials: true`.
+* **Match the origin exactly.** Scheme, host, and port all have to line up, with no trailing slash.
+
+If you're setting CORS headers inside serverless functions, the response model can differ from a traditional Express server. [Handle CORS errors in Appwrite Functions](/blog/post/handle-cors-in-serverless-functions) shows how to attach the headers correctly and respond to preflight requests in that environment.
+
+## CORS is not a substitute for authorization
+
+One last point worth being direct about: CORS is a browser protection, not an access control layer. It stops other websites' JavaScript from reading your responses in a user's browser. It does nothing to stop a server, a script, or a tool like `curl` from calling your API directly, because those clients don't enforce CORS at all.
+
+That means CORS is not a replacement for authentication, API keys, or permission checks. You still need real authorization on the server. Treat CORS as one layer in a defense-in-depth setup, alongside proper auth and rate limiting. For more on where that responsibility sits, see [Appwrite's security documentation](/docs/advanced/security).
+
+## Handling CORS with Appwrite
+
+If you're building on Appwrite, you don't configure CORS headers by hand. Instead, you register the origins your app runs on as **platforms** in the Appwrite Console. Add a web platform with your hostname (`localhost` in development, your domain in production), and Appwrite allows requests from that origin while rejecting everything else. This is the same allowlist model CORS is built on, exposed as a setting instead of raw headers.
+
+Because Appwrite handles the CORS layer for its own APIs, most "blocked by CORS policy" messages against an Appwrite backend come down to a missing or mistyped platform, a wrong project ID, or the wrong regional endpoint, rather than headers you need to write yourself. When you call your own [Appwrite Functions](/docs/products/functions) directly from the browser, you set the headers in the function response, which is where the serverless guide above comes in.
+
+CORS stops being a mystery once you see it for what it is: a server-side allowlist enforced by the browser to keep untrusted origins from reading your responses. Configure the right origins, answer the preflight, keep the wildcard out of production, and back it with real authorization, and the red console errors go away for good. If you want a backend that manages the CORS layer for you and lets you focus on your app, Appwrite is a solid place to start.
+
+## Getting started with Appwrite
+
+* [Solving CORS errors with Appwrite](/blog/post/cors-error)
+* [Handle CORS errors in Appwrite Functions](/blog/post/handle-cors-in-serverless-functions)
+* [Appwrite Functions documentation](/docs/products/functions)
+* [Appwrite security documentation](/docs/advanced/security)
+* [Join the Appwrite Discord server](https://appwrite.io/discord)
diff --git a/src/routes/blog/post/what-is-database-caching-a-beginners-guide/+page.markdoc b/src/routes/blog/post/what-is-database-caching-a-beginners-guide/+page.markdoc
new file mode 100644
index 0000000000..97f793dd95
--- /dev/null
+++ b/src/routes/blog/post/what-is-database-caching-a-beginners-guide/+page.markdoc
@@ -0,0 +1,153 @@
+---
+layout: post
+title: "What is database caching? A beginner's guide"
+description: Database caching speeds up your app by serving repeated queries from fast storage. Learn how it works, the main strategies, and when you actually need it.
+date: 2026-07-21
+cover: /images/blog/what-is-database-caching-a-beginners-guide/cover.avif
+timeToRead: 5
+author: aditya-oberai
+category: performance
+featured: false
+unlisted: true
+faqs:
+ - question: What is a cache hit and a cache miss?
+ answer: A cache hit happens when the requested data is already stored in the cache. A cache miss happens when the data is not available, so the application queries the database and usually stores the result for future requests.
+ - question: What is TTL in database caching?
+ answer: TTL stands for time to live. It defines how long cached data remains available before it expires and must be fetched from the database again.
+ - question: Is Redis required for database caching?
+ answer: No. Redis is a popular option, but applications can also use Memcached, local in-process caches, distributed caches, CDNs, or caching features built into a backend platform.
+ - question: What is the best database caching strategy?
+ answer: Cache-aside is the most common strategy for most applications. The application checks the cache first, queries the database after a miss, and then stores the result in the cache.
+---
+**Database caching is a technique that stores the results of frequent or expensive database queries in fast, temporary storage so your app can serve them instantly instead of hitting the database every time.** Done well, it turns slow, repeated reads into responses that feel instant and takes a large chunk of load off your primary database.
+
+This guide explains what database caching is, how it works, the main caching strategies, where the cache lives, how invalidation works, and when you actually need it. It's written for developers who want a complete picture, not just a definition.
+
+# What is database caching?
+
+Database caching is the practice of keeping copies of frequently accessed data in a fast storage layer that sits between your application and your database. When a request comes in, your app checks the cache first. If the data is there, it's returned immediately. If not, the app queries the database, stores the result in the cache, and returns it.
+
+The database remains the source of truth. The cache is a faster shortcut to data your app reads over and over. Think of it like the notepad on your desk: your main database is the filing cabinet, reliable but slower to open, while the cache holds the few things you need right now so you can grab them instantly.
+
+Most caches live in memory rather than on disk, which is why they respond in under a millisecond. That speed is the entire point. Reading from RAM is orders of magnitude faster than reading from disk, and database caching is how you put that speed in front of your data.
+
+# How does database caching work?
+
+Database caching works by intercepting reads before they reach the database. Every request either finds its data already stored (a **cache hit**) or doesn't (a **cache miss**).
+
+Here's the typical flow:
+
+1. Your app receives a request for some data, such as a product listing.
+2. It checks the cache using a **key**, usually derived from the query or resource ID.
+3. On a **cache hit**, the cached value is returned immediately, and the database is never touched.
+4. On a **cache miss**, the app queries the database, stores the result in the cache under that key, and returns it.
+5. The next identical request is now a cache hit, served straight from memory.
+
+The ratio of hits to total requests is your **hit rate**, and it's the single most important number in caching. A high hit rate means most requests skip the database entirely. A low hit rate means the cache is adding overhead without much payoff, which usually points to the wrong data being cached or keys that rarely repeat.
+
+# Why database caching matters
+
+Databases are often the slowest part of a request and the hardest part to scale. A query that joins several tables, sorts a large result set, or runs an aggregation can take tens or hundreds of milliseconds. Multiply that by thousands of concurrent users and the database becomes a bottleneck.
+
+Database caching attacks this in three ways:
+
+* **Lower latency.** Cached reads come from memory in under a millisecond, so pages and API responses feel instant.
+* **Less database load.** Every cache hit is a query your database never has to run, which frees capacity for writes and uncacheable reads.
+* **Lower cost.** Serving reads from a cache is far cheaper than scaling database instances or paying per-query on a managed database.
+
+The cost of *not* caching shows up as slow pages under traffic, database CPU pinned near its limit, and expensive vertical scaling to buy headroom you could get for free. For read-heavy workloads, caching is usually the highest-leverage performance change you can make.
+
+# Database caching strategies
+
+There are several caching strategies, and the right one depends on your read and write patterns. Most systems mix a few of them.
+
+## Cache-aside (lazy loading)
+
+The application manages the cache directly. It checks the cache first, and on a miss, loads from the database and populates the cache. This is the most common strategy because it's simple and only caches data that's actually requested. The trade-off is that the first request for any item always misses.
+
+## Read-through
+
+The cache sits inline and loads missing data from the database itself, so the application only ever talks to the cache. It's cleaner than cache-aside but requires a cache layer that supports it. Behavior on a miss is otherwise similar: fetch, store, return.
+
+## Write-through
+
+Every write goes to the cache and the database at the same time, keeping the two in sync. Reads are always fresh, but writes are slightly slower because they touch two systems. This suits data that's read often and must never be stale.
+
+## Write-back (write-behind)
+
+Writes go to the cache first and are flushed to the database asynchronously. This makes writes fast but risks data loss if the cache fails before the flush, so it's used carefully and usually only where some durability risk is acceptable.
+
+Here's how the main strategies compare:
+
+| Strategy | Reads | Writes | Best for |
+| ------------- | ------------------------------- | ----------------------------- | ---------------------------------- |
+| Cache-aside | App checks cache, loads on miss | Write to DB, invalidate cache | General-purpose read-heavy apps |
+| Read-through | Cache loads on miss | Handled separately | Simpler app code |
+| Write-through | Always fresh | Slower, writes twice | Data that can't be stale |
+| Write-back | Fast | Fast, flushed later | Write-heavy, tolerant of some risk |
+
+# Where does the cache live?
+
+Database caching can happen at several layers, and they're often combined.
+
+* **In-memory cache.** A store like [Redis](/blog/post/what-is-redis-a-complete-guide-for-developers) or Memcached holds data in RAM for microsecond reads. This is the most common place to cache database results.
+* **Local (in-process) cache.** Data cached inside the application process itself, which is the fastest option but isn't shared across servers and vanishes on restart.
+* **Distributed cache.** A shared cache cluster accessible by every application server, so a value cached by one server is a hit for all of them.
+* **Edge and CDN caching.** For read-heavy public data, a [CDN](/blog/post/what-is-cdn) can cache API responses close to users, cutting both latency and origin load.
+
+Redis is by far the most popular choice for database caching because it's fast, supports expiration natively, and offers rich data structures. If you're evaluating it, our [complete guide to Redis](/blog/post/what-is-redis-a-complete-guide-for-developers) walks through how it works and why it's so fast.
+
+# Cache invalidation and TTL
+
+Cache invalidation is the hardest part of database caching. The moment you store a copy of data, it can go stale when the underlying record changes. Your job is to decide how stale is acceptable and how to refresh.
+
+The two main approaches are:
+
+* **Time-based expiration (TTL).** Each cached entry gets a **time to live**. When it expires, the next request repopulates it from the database. Invalidation is automatic and predictable, but data can be stale for up to the length of the TTL.
+* **Event-based invalidation.** When a record is written or deleted, you explicitly remove or update its cache entry. This keeps data fresh but adds complexity, because every write path has to know which cache keys it affects.
+
+TTL is the pragmatic default for most read-heavy data. You pick a window that matches how stale the data can safely be. A product catalog might tolerate 5 minutes, while a public leaderboard might refresh every few seconds. Data where staleness is unacceptable, like an account balance or a shopping cart, is usually a poor fit for time-based caching.
+
+# When do you actually need database caching?
+
+You need database caching when the same data is read far more often than it changes, and when database read latency or load is hurting your app. Be honest about both conditions before adding a cache.
+
+Caching is a strong fit for:
+
+* **Read-heavy, slow-changing data** such as product listings, category pages, blog content, and configuration.
+* **Expensive queries** with joins, aggregations, or large sorts that repeat often.
+* **Public, shared responses** where many users request the same thing.
+
+Caching is a poor fit, or needs extra care, for:
+
+* **Data that must always be current**, like balances, inventory counts, or cart contents.
+* **Highly personalized data** that rarely repeats between users, giving you a low hit rate.
+* **Write-heavy workloads** where invalidation churn cancels out the benefit.
+
+A cache is not free. It adds a moving part, a new failure mode, and the ongoing question of correctness. If your database is comfortably fast and lightly loaded, you may not need one yet. Add caching to solve a measured problem, not preemptively.
+
+# Database caching best practices
+
+* **Set a TTL on everything.** Expiration keeps memory bounded and gives you a safety net against stale data even when explicit invalidation is missed.
+* **Cache the right data.** Target read-heavy, slow-changing, frequently repeated queries, not everything.
+* **Design keys carefully.** Include the parameters that make a query unique, such as filters, sort, and pagination, so different results don't collide on one key.
+* **Measure your hit rate.** A low hit rate means you're caching the wrong things or your keys don't repeat.
+* **Plan for cache misses and failures.** Your app should degrade gracefully to the database if the cache is unavailable.
+* **Match staleness to the data.** Short TTLs for fast-changing data, longer TTLs for stable content.
+
+# Getting started with caching on Appwrite
+
+If you're building on [Appwrite](https://cloud.appwrite.io/), you can get the benefits of database caching without wiring up and maintaining a separate cache. [Appwrite Databases](/docs/products/databases) supports [TTL-based list caching](/blog/post/announcing-list-cache-ttl): you pass a `ttl` parameter to your existing `listRows` call, and Appwrite serves identical repeat requests from memory until the window expires. The query, endpoint, and SDK stay the same, so you can roll it out behind a feature flag.
+
+We put that cache under sustained load and measured it in our [TTL caching benchmark](/blog/post/ttl-cache-performance-benchmark), where 100,000 requests per phase showed the biggest gains at the tail of the latency distribution. Beyond the database layer, [Appwrite Sites](/docs/products/sites) and the [Appwrite Network](/docs/products/network/caching) add edge caching for static and dynamic content, so you can cache at every layer from one platform.
+
+Appwrite Cloud gives you a fully managed backend with Auth, Databases, Storage, Functions, Sites, and Realtime out of the box, scaling automatically as your app grows. [Sign up for Appwrite Cloud](https://cloud.appwrite.io/) and give your next build a backend that's fast by default.
+
+## Resources
+
+* [Appwrite Databases documentation](/docs/products/databases)
+* [TTL list caching announcement](/blog/post/announcing-list-cache-ttl)
+* [Benchmarking TTL list caching](/blog/post/ttl-cache-performance-benchmark)
+* [What is Redis? A complete guide for developers](/blog/post/what-is-redis-a-complete-guide-for-developers)
+* [Appwrite Network caching docs](/docs/products/network/caching)
+* [Join the Appwrite Discord](https://appwrite.io/discord)
\ No newline at end of file
diff --git a/static/images/blog/nextjs-vs-react-explained-which-should-developers-use/cover.avif b/static/images/blog/nextjs-vs-react-explained-which-should-developers-use/cover.avif
new file mode 100644
index 0000000000..ab4e487e28
Binary files /dev/null and b/static/images/blog/nextjs-vs-react-explained-which-should-developers-use/cover.avif differ
diff --git a/static/images/blog/what-is-cors-cross-origin-resource-sharing-explained/cover.avif b/static/images/blog/what-is-cors-cross-origin-resource-sharing-explained/cover.avif
new file mode 100644
index 0000000000..760b58b1e7
Binary files /dev/null and b/static/images/blog/what-is-cors-cross-origin-resource-sharing-explained/cover.avif differ
diff --git a/static/images/blog/what-is-database-caching-a-beginners-guide/cover.avif b/static/images/blog/what-is-database-caching-a-beginners-guide/cover.avif
new file mode 100644
index 0000000000..8ee9ace011
Binary files /dev/null and b/static/images/blog/what-is-database-caching-a-beginners-guide/cover.avif differ