# How to Implement Core SEO Functionality in Next.js Using every-app/open-seo

> Learn how to implement core SEO functionality in Next.js with every-app/open-seo. Generate meta tags, sitemaps, and structured data easily.

- Repository: [Every App/open-seo](https://github.com/every-app/open-seo)
- Tags: how-to-guide
- Published: 2026-07-06

---

**Open-SEO provides a self-hosted MCP (Machine-Client-Protocol) API that aggregates data from Google Search Console, DataForSEO, and Lighthouse, allowing Next.js applications to generate meta tags, sitemaps, and structured data at build or request time.**

This guide explains how to integrate the `every-app/open-seo` repository into a Next.js project to automate SEO artifact generation. By offloading heavy analytics processing to Open-SEO’s backend, your Next.js frontend remains lightweight while serving fully optimized pages.

## What Is Open-SEO and the MCP API?

Open-SEO is a self-hosted backend service that normalizes data from disparate SEO sources into a unified **MCP (Machine-Client-Protocol) API**. Rather than managing separate integrations with Google Search Console, DataForSEO SERP APIs, and Lighthouse audits in your frontend, Open-SEO handles the throttling, caching, and pagination internally.

Your Next.js application communicates with Open-SEO via HTTP endpoints exposed by the MCP layer. According to the source code in [`src/serverFunctions/searchPerformance.ts`](https://github.com/every-app/open-seo/blob/main/src/serverFunctions/searchPerformance.ts), the `/api/seo/meta` endpoint extracts the correct row from DataForSEO SERP responses and returns a normalized JSON payload containing titles, descriptions, and Open Graph fields.

## Prerequisites and Environment Setup

Before implementing the integration, configure your environment variables to authenticate with the MCP layer. As implemented in [`src/serverFunctions/middleware.ts`](https://github.com/every-app/open-seo/blob/main/src/serverFunctions/middleware.ts), all MCP routes are protected by a `createMiddleware` wrapper that validates project tokens against environment variables.

Create a `.env.local` file in your Next.js project root:

```bash
OPENSEO_MCP_URL=https://your-open-seo-instance.com
OPENSEO_PROJECT_TOKEN=your_project_token_here

```

The middleware reads `OPENROUTER_API_KEY` or `DATAFORSEO_API_KEY` to validate requests, so ensure these match your Open-SEO deployment configuration.

## Implementing Meta Tags and Canonical URLs

### Fetching SEO Metadata from the MCP Endpoint

The function `computeNextCheckAt` in [`src/shared/rank-tracking.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/rank-tracking.ts) handles URL normalization and canonicalization, ensuring consistent canonical URLs across your tracked pages. To inject these into your Next.js pages, call the MCP endpoint inside `getServerSideProps` or `getStaticProps`.

Here is a complete implementation for a dynamic page:

```tsx
// pages/[slug].tsx
import { GetServerSideProps } from 'next';
import Head from 'next/head';
import type { SEOData } from '@/types';

export const getServerSideProps: GetServerSideProps = async ({ params, req }) => {
  const slug = params?.slug as string;
  const res = await fetch(
    `${process.env.OPENSEO_MCP_URL}/api/seo/meta?url=${encodeURIComponent(slug)}`,
    {
      headers: { Authorization: `Bearer ${process.env.OPENSEO_PROJECT_TOKEN}` },
    }
  );
  const seo: SEOData = await res.json();

  return { props: { seo } };
};

export default function Page({ seo }: { seo: SEOData }) {
  return (
    <>
      <Head>
        <title>{seo.title}</title>
        <meta name="description" content={seo.description} />
        <meta property="og:title" content={seo.title} />
        <meta property="og:description" content={seo.description} />
        <meta property="og:image" content={seo.ogImage} />
        <link rel="canonical" href={seo.canonical} />
      </Head>
      <article>{/* page content */}</article>
    </>
  );
}

```

This approach leverages Open-SEO’s pre-aggregated data, eliminating the need for complex API orchestration in your frontend.

## Generating Dynamic Sitemaps and Robots.txt

### Proxying the XML Sitemap

The [`RankCheckWorkflow.ts`](https://github.com/every-app/open-seo/blob/main/RankCheckWorkflow.ts) workflow iterates over all tracked URLs and writes an XML sitemap to Cloudflare KV or Postgres, depending on your deployment. Rather than generating sitemaps client-side, create a thin API route in Next.js that proxies the pre-generated XML from Open-SEO.

Create [`pages/api/sitemap.xml.ts`](https://github.com/every-app/open-seo/blob/main/pages/api/sitemap.xml.ts):

```tsx
import type { NextApiRequest, NextApiResponse } from 'next';

export default async function handler(_: NextApiRequest, res: NextApiResponse) {
  const resp = await fetch(`${process.env.OPENSEO_MCP_URL}/sitemap.xml`, {
    headers: { Authorization: `Bearer ${process.env.OPENSEO_PROJECT_TOKEN}` },
  });
  const xml = await resp.text();

  res.setHeader('Content-Type', 'application/xml');
  res.setHeader('Cache-Control', 's-maxage=86400, stale-while-revalidate');
  res.send(xml);
}

```

The `Cache-Control` headers enable CDN-level caching, reducing load on both your Next.js server and the Open-SEO backend.

### Serving Robots.txt

Similarly, the `RankCheckWorkflow` generates [`robots.txt`](https://github.com/every-app/open-seo/blob/main/robots.txt) as part of the same KV payload. Serve it via a dedicated API route:

```tsx
// pages/api/robots.txt.ts
import type { NextApiRequest, NextApiResponse } from 'next';

export default async function handler(_: NextApiRequest, res: NextApiResponse) {
  const resp = await fetch(`${process.env.OPENSEO_MCP_URL}/robots.txt`, {
    headers: { Authorization: `Bearer ${process.env.OPENSEO_PROJECT_TOKEN}` },
  });
  const txt = await resp.text();

  res.setHeader('Content-Type', 'text/plain');
  res.send(txt);
}

```

## Injecting Structured Data (JSON-LD)

Open-SEO extracts structured data from Lighthouse audits via the [`lighthouseStoredPayload.ts`](https://github.com/every-app/open-seo/blob/main/lighthouseStoredPayload.ts) library. This file parses audit results and surfaces the `structuredData` field if present in the page source.

To include JSON-LD in your Next.js pages, extend your `Head` component:

```tsx
{seo.jsonLd && (
  <script 
    type="application/ld+json" 
    dangerouslySetInnerHTML={{ __html: seo.jsonLd }} 
  />
)}

```

This ensures search engines receive properly formatted schema markup without requiring manual JSON-LD construction in your frontend components.

## Displaying Rank Tracking Data

For dynamic keyword-rank tables, query the `RankTrackingRepository` which stores pre-aggregated rank data for each project. The repository implementation in [`src/server/features/rank-tracking/repositories/RankTrackingRepository.ts`](https://github.com/every-app/open-seo/blob/main/src/server/features/rank-tracking/repositories/RankTrackingRepository.ts) provides optimized queries that pull the latest rank data without heavy computation on the frontend.

Fetch this data in `getServerSideProps` and render it in a table component. Because Open-SEO handles the aggregation, your page bundle stays lightweight while displaying real-time SEO performance metrics.

## Summary

- **Open-SEO** acts as a self-hosted MCP backend that consolidates Google Search Console, DataForSEO, and Lighthouse data.
- **Meta tags and canonical URLs** are fetched from `/api/seo/meta` and injected via `next/head`, with canonical logic handled in [`src/shared/rank-tracking.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/rank-tracking.ts).
- **Sitemaps and robots.txt** are generated by [`RankCheckWorkflow.ts`](https://github.com/every-app/open-seo/blob/main/RankCheckWorkflow.ts) and served through thin Next.js API routes with CDN caching.
- **Structured data** is extracted from Lighthouse audits in [`lighthouseStoredPayload.ts`](https://github.com/every-app/open-seo/blob/main/lighthouseStoredPayload.ts) and embedded as JSON-LD.
- **Authentication** uses the `createMiddleware` wrapper in [`src/serverFunctions/middleware.ts`](https://github.com/every-app/open-seo/blob/main/src/serverFunctions/middleware.ts), requiring only environment variables in your Next.js project.

## Frequently Asked Questions

### What is the MCP protocol in Open-SEO?

The **Machine-Client-Protocol (MCP)** is a lightweight API layer that standardizes communication between your Next.js frontend and various SEO data sources. It handles authentication, caching, and data normalization, exposing a consistent interface for requesting meta tags, rank data, and audit results without managing multiple third-party APIs directly.

### How does Open-SEO handle authentication?

According to [`src/serverFunctions/middleware.ts`](https://github.com/every-app/open-seo/blob/main/src/serverFunctions/middleware.ts), Open-SEO uses a `createMiddleware` wrapper that validates requests against project tokens and API keys stored in environment variables like `OPENROUTER_API_KEY` or `DATAFORSEO_API_KEY`. Your Next.js application must include the `Authorization: Bearer` header with the `OPENSEO_PROJECT_TOKEN` when calling MCP endpoints.

### Can I use Open-SEO with static site generation (SSG)?

Yes. While the examples above use `getServerSideProps`, you can implement the same fetch logic inside `getStaticProps` for static generation. The MCP endpoints return JSON data suitable for both static and dynamic rendering, allowing you to build SEO-optimized pages at deploy time while keeping the Open-SEO backend as your single source of truth.

### Where does Open-SEO store sitemap and robots.txt data?

The [`RankCheckWorkflow.ts`](https://github.com/every-app/open-seo/blob/main/RankCheckWorkflow.ts) workflow writes XML sitemaps and [`robots.txt`](https://github.com/every-app/open-seo/blob/main/robots.txt) files to **Cloudflare KV** or **Postgres**, depending on your deployment configuration. Your Next.js application then proxies these files via API routes, enabling efficient CDN distribution while maintaining the generation logic on the Open-SEO backend.