EXPRESS.JS SEO AUDIT · NODE.JS · EJS · PUG · AI VISIBILITY

Free Express.js SEO Audit

Express.js is unopinionated about SEO — metadata management, sitemaps, and robots.txt are all things you build yourself. DeepSEOAnalysis audits Express apps and surfaces exactly which pieces are missing, with route handler snippets, template variable patterns, and static middleware configuration for each fix.

Audit my Express app →How we score

WHAT WE CHECK

6 Express.js-specific SEO checks

Beyond the 80+ general checks, the engine applies Express-aware analysis — covering template engine metadata inheritance across EJS, Pug, Handlebars, and Nunjucks layouts; route URL slug patterns; sitemap middleware configuration; and static-directory robots.txt serving.

📝

Template engine metadata coverage

Express.js is template-engine-agnostic: you choose EJS (`<%- title %>`), Pug (`title= pageTitle`), Handlebars (`{{title}}`), or Nunjucks (`{{ title }}`). Whichever you use, the layout/base template must expose per-route overrides for `<title>`, `<meta name="description">`, `<link rel="canonical">`, and OG tags. A common pattern is passing these as local variables from each route handler: `res.render("post", { title: post.title, description: post.excerpt, canonical: "/blog/" + post.slug })`. Common gaps: the layout template has a hard-coded `<title>Site Name</title>` with no variable reference; meta description is missing from the base layout; OG tags are set but always use the homepage image and title. The audit checks every crawled page for a unique, populated title tag and meta description, flagging pages that share the same EJS/Pug/Handlebars default.

🔗

Route URL structure

Express route definitions use path parameters: `app.get("/posts/:id", ...)` produces numeric-ID URLs (`/posts/42`); `app.get("/posts/:slug", ...)` produces clean slug URLs (`/posts/my-post-title`). The slug value must come from a database column — typically added as a `slug` field populated via a `slugify()` call on the title at creation time. Common gaps: content-heavy routes use `:id` (numeric IDs) with no slug column in the schema; Express router groups expose API routes at paths without a `/api/` prefix, making them accessible to crawlers; admin or debug routes lack robots.txt Disallow protection. The audit flags parameter-heavy, non-keyword URL patterns and surfaces any admin-path or API-path candidates for crawl blocking.

🗺️

Sitemap configuration

Express has no built-in sitemap generator. The two standard approaches are the `express-sitemap` npm package or a manual async route that queries your database and returns XML. Without either, Googlebot discovers pages only through link-following — reliable for small sites, problematic as page count grows. Common gaps after implementing: the sitemap query includes draft, private, or soft-deleted records; `lastmod` is hardcoded to the build date rather than derived from the database `updated_at` timestamp; the `/sitemap.xml` URL is not referenced as a `Sitemap:` directive in `public/robots.txt`; the sitemap route is not cached and regenerates expensively on every crawler request. The audit checks sitemap presence, URL coverage, `lastmod` accuracy, and the Sitemap directive in robots.txt.

🤖

robots.txt and AI crawler access

Express serves static files via `express.static()` middleware — typically `app.use(express.static("public"))`. A `public/robots.txt` file is served correctly at `/robots.txt` (the URL root path Googlebot checks), unlike Flask's `static/` directory which serves at `/static/robots.txt`. Common Express gaps: no `robots.txt` exists at all (Express creates none by default), `/api/` routes are not disallowed (Googlebot will crawl JSON endpoints and may try to index them), admin or debug routes lack Disallow entries, and AI crawlers (GPTBot, ClaudeBot, PerplexityBot) are accidentally blocked via a broad `Disallow: /` or missing User-agent handling. The audit checks the file at `/robots.txt`, verifies all major AI crawler user-agents are allowed, and flags API or admin paths without Disallow rules.

Server-rendered template advantages

Express with a server-side template engine (EJS, Pug, Handlebars, Nunjucks) renders pages server-side and returns complete HTML on the first HTTP response. Googlebot receives the full content, all metadata, and structured data immediately — no JavaScript rendering step, no two-wave crawl queue delay. This is Express's core SEO advantage over SPA architectures. The audit verifies that crawled pages deliver complete HTML (H1, body text, JSON-LD, metadata) in the initial response, and flags Express apps where primary content is injected by client-side JavaScript (common in Express apps that use React or Vue on the frontend while Express serves only an API). Express REST API endpoints returning JSON are flagged for robots.txt Disallow recommendations.

🧠

AI visibility for Express apps

Express's static middleware makes `llms.txt` straightforward: place it in `public/llms.txt` and `app.use(express.static("public"))` serves it at `/llms.txt` automatically. FAQPage and Article JSON-LD can be rendered inline via your template engine — pass the structured data object from the route handler and render it as a `<script type="application/ld+json">` tag in the template. No special library needed: `JSON.stringify(faqSchema)` in an EJS `<%- %>` tag (unescaped) works correctly. The audit checks all five AI visibility signals — llms.txt presence, AI crawler access, FAQPage/HowTo schema, question-heading ratio (≥20%), and content chunkability (avg section ≤400w) — with Express-specific static middleware and template patterns for each fix.

HOW IT WORKS

Audit your Express app in 60 seconds

  1. Enter your Express app URL. Works with any Express deployment: PM2 + nginx, Heroku, Fly.io, Render, Railway, DigitalOcean App Platform, or AWS/GCP. No source code, template files, or Node.js shell access needed — the audit crawls the live site.
  2. We crawl and analyse. The engine crawls up to 50 pages (free) or 1,000 pages (paid), checks template engine metadata coverage across all crawled routes, validates JSON-LD structured data, tests robots.txt at the correct URL path, measures CrUX Core Web Vitals, and evaluates all five AI visibility signals.
  3. You get a prioritised report. Express-aware issues first — template metadata gaps, numeric-ID routing, missing sitemap route, unprotected API paths, blocked AI crawlers — then general SEO, each with the specific route handler pattern, template variable, or `express.static` config to fix it.

FAQ

Questions about the Express.js SEO audit

Does DeepSEOAnalysis detect Express.js-specific SEO issues?

Yes. The engine detects Express signatures — server-rendered HTML with Node.js template engine output (EJS, Pug, Handlebars, Nunjucks), Express route URL patterns, and `public/` static directory conventions — and surfaces framework-specific guidance alongside the 80+ general checks.

Is Express.js good for SEO?

Express.js is good for SEO when configured correctly. It renders HTML server-side via your chosen template engine (EJS, Pug, Handlebars, Nunjucks), so Googlebot receives complete page content on the first request with no JavaScript rendering step. The SEO challenges with Express are its unopinionated minimalism: it provides no built-in metadata management, no sitemap generator, and no canonical tag handling. Every SEO feature requires deliberate implementation in your route handlers and templates.

How do I generate a sitemap in an Express app?

Two approaches: (a) the `express-sitemap` npm package — configure it with your routes and it generates `/sitemap.xml` with optional `lastmod` values; (b) a manual route: `app.get("/sitemap.xml", async (req, res) => { const pages = await db.getPublishedPages(); res.header("Content-Type", "application/xml"); res.send(buildSitemapXML(pages)); })`. Use `lastmod` from your database `updated_at` column. Add a `Sitemap:` directive in `public/robots.txt`. The audit checks sitemap presence, page coverage, and the Sitemap directive.

Is the Express.js SEO audit free?

Yes. The complete audit is free with no signup and no email gate. Paid plans add saved history, scheduled monitoring, larger crawls (up to 1,000 pages), and agency workflows.