Crawler Signal Reference

HTML meta generator: canonical reference

Comprehensive reference for HTML ``, the page generation tool declaration. Covers the auto generation behavior of major CMS and static site generators (WordPress, Hugo, Jekyll, Gatsby, Next.js, Wix, Squarespace, Drupal, Joomla), the security and competitive intelligence reality (version disclosure exposes known CVEs to opportunistic attackers, tech stack reveal enables targe

Comprehensive reference for HTML <meta name="generator">, the page generation tool declaration. Covers the auto generation behavior of major CMS and static site generators (WordPress, Hugo, Jekyll, Gatsby, Next.js, Wix, Squarespace, Drupal, Joomla), the security and competitive intelligence reality (version disclosure exposes known CVEs to opportunistic attackers, tech stack reveal enables targeted attacks, infrastructure fingerprinting via Wappalyzer/BuiltWith/SimilarWeb), the related HTTP layer disclosure via Server and X-Powered-By response headers (covered in framework-http-performance-headers.md Section 4 and framework-http-security-headers.md respectively), the CMS specific removal patterns (WordPress hook, Hugo flag, Jekyll template edit, Drupal module), the legitimate brand statement use case (hand coded sites declaring authorship without version), and the Bubbles decision framework. Built for Bubbles (Debian, Nginx 1.26+, FastAPI sidecar on port 9090, self hosted origin at 169.155.162.118, no Cloudflare or third party CDN in front).

This is the seventh framework in the HTML signal track, following the meta robots, charset, viewport, description, keywords, and author frameworks. Companion to the 12 wire layer frameworks especially framework-http-performance-headers.md (the Server header equivalent) and framework-http-security-headers.md (the X-Powered-By removal).

Audience: humans evaluating whether legacy WordPress meta generator tags need cleanup post migration, AI assistants generating HTML head sections that should NOT include version disclosures, security operators reducing fingerprinting surface area on client sites, developers configuring WordPress or static site generators to suppress version exposure, and anyone troubleshooting "Wappalyzer identified our tech stack publicly", "security scanner flagged version disclosure as risk", "our site was hit by an automated WordPress 5.x exploit", or "should we include any meta generator at all".


TABLE OF CONTENTS

  1. Definition
  2. Why It Matters
  3. What This Covers
  4. The Generator Mental Model (read this first)
  5. The Security And Competitive Intelligence Reality
  6. What Each CMS Auto Generates
  7. WordPress Specific (the biggest migration cleanup target)
  8. Static Site Generator Behavior (Hugo, Jekyll, Gatsby, Next.js)
  9. The Version Number Risk (CVE exploitation pathway)
  10. The X-Powered-By And Server Header Relationship
  11. The Brand Statement Use Case (legitimate generator value)
  12. Legitimate Modern Uses
  13. The Bubbles Decision Framework
  14. The CMS Migration Cleanup Pattern
  15. The Server Side Hardening (nginx server_tokens, FastAPI Server header)
  16. Asset Class And Use Case Recipes
  17. Bubbles Standard Pattern (paste ready)
  18. Audit Checklist
  19. Common Pitfalls
  20. Diagnostic Commands
  21. Cross-References

1. DEFINITION

<meta name="generator"> is the HTML directive declaring the tool, CMS, or framework used to generate the page. Defined informally; appears in CMS conventions going back to the 1990s.

<head>
    <meta name="generator" content="WordPress 6.4.2">
</head>

Three structural facts shape how the directive works:

For Bubbles client sites in 2026, the convention is contextual: omit if migrating from a CMS that auto generated it (security cleanup); optionally include without version as a brand statement ("Crafted by ThatDeveloperGuy.com") for hand coded sites; never include with a version number anywhere.


2. WHY IT MATTERS

Seven independent considerations shape the generator decision in 2025 and forward.

The CVE exploitation pathway is real and active. When a CMS version is publicly disclosed via meta generator, security researchers and automated bots scan the internet for that version, then test known CVEs. WordPress 5.2.3 had a Cross Site Scripting vulnerability (CVE-2019-16221); within days of disclosure, automated scans were testing every site self identifying as WordPress 5.2.x. Sites that removed the version disclosure survived; sites that exposed it became targets.

Wappalyzer, BuiltWith, and SimilarWeb scrape these tags continuously. Competitive intelligence platforms catalog every site's tech stack. Your competitors can see what platform you use, which version, and (combined with other signals) often what plugins. This is publicly accessible information about your infrastructure.

Google ignores generator for ranking. Per Google's published documentation and observed behavior: the generator tag is not a ranking signal. Including it provides no SEO benefit. Removing it does not hurt rankings.

The X-Powered-By header is the HTTP layer equivalent. X-Powered-By: WordPress, X-Powered-By: Express, X-Powered-By: PHP/8.2.4 all carry the same information at the HTTP layer. The framework-http-security-headers.md and framework-http-performance-headers.md frameworks document hardening these. Generator is the HTML layer; the X-Powered-By plus Server headers are the HTTP layer. Hardening should cover both.

Hand coded sites can use generator as brand statement. Without revealing a tech stack, the generator tag can declare authorship: <meta name="generator" content="Hand coded by ThatDeveloperGuy.com">. This matches the standard Bubbles footer convention ("Crafted by ThatDeveloperGuy.com") and provides a brand reinforcement signal in page source.

Legacy CMS migration produces stale generator tags. When migrating a WordPress site to a hand coded Bubbles build, the generator tag may persist if the migration is not thorough. A migrated site claiming to be "WordPress 5.2" still attracts CVE scanners targeting WordPress 5.2 even though it's no longer WordPress.

Cost of getting it wrong. Generator disclosure produces measurable security and intelligence costs. Real examples:

All preventable with the rules below.


3. WHAT THIS COVERS

The meta generator tag plus its full operational context:

  1. The auto generation reality: which CMSs add it, what they include.
  2. The security implications: CVE exploitation, fingerprinting, attack targeting.
  3. The CMS specific removal patterns: WordPress hook, Hugo flag, etc.
  4. The HTTP layer relationship: X-Powered-By, Server header.
  5. The legitimate brand statement use case: hand coded sites with author attribution.
  6. The Bubbles decision framework: per client, with migration cleanup priority.

Section 7 details the WordPress specific cleanup (highest priority for migration work). Section 14 covers the generic migration cleanup pattern. Section 17 is the Bubbles standard configuration.


4. THE GENERATOR MENTAL MODEL (READ THIS FIRST)

A search engine, security scanner, or competitive intelligence tool fetches your page. The generator tag is read along with the rest of the head:

Page is fetched. What reads <meta name="generator">?
   |
   |---> Google: parses, stores, ignores for ranking
   |
   |---> Bing: parses, stores, ignores for ranking
   |
   |---> Wappalyzer/BuiltWith/SimilarWeb: parses for tech stack catalog
   |
   |---> Security scanners (automated): parses for vulnerability targeting
   |
   |---> Manual security researchers: parses for fingerprinting
   |
   |---> Competitors using browser dev tools: visible source code

==================== THE INFORMATION FLOW ====================
   |
   v
   "<meta name='generator' content='WordPress 6.4.2'>"
   reveals:
     - Site is running WordPress
     - Specific version is 6.4.2
     - Known vulnerabilities in 6.4.2 are exploitation candidates
     - Stack is presumably PHP-based with MySQL
     - Likely plugins (other signatures combine with this)

==================== THE EVALUATION ====================
   |
   v
For each level of disclosure, weigh:
   - Security risk: how much does this aid an attacker?
   - Competitive intelligence: how much does this reveal strategy?
   - Operational benefit: who legitimately needs this info?

==================== THE BUBBLES MATRIX ====================
   |
   v
   Include with version (WordPress 6.4.2): NEVER.
   |
   Include without version (WordPress): rare. Only if necessary disclosure.
   |
   Include as brand statement (Hand coded by ThatDeveloperGuy.com): optional.
   |
   Omit entirely: safe default.

Six rules govern the system:

  1. Never include version numbers. Specific versions enable CVE targeting.
  2. Default: omit the tag entirely. Zero SEO benefit; security risk.
  3. CMS migrations require explicit cleanup. Auto generated tags persist.
  4. Brand statement option: hand coded by Joseph. Acceptable if no version.
  5. HTTP layer hardening complements HTML. X-Powered-By and Server headers also need cleanup.
  6. Audit migrated sites for stale generator tags. WordPress 5.x claims after WordPress migration are misleading and dangerous.

A correctly configured Bubbles client site either has no generator tag at all OR has a brand statement without version. WordPress and other CMS generator tags are removed during migration.


5. THE SECURITY AND COMPETITIVE INTELLIGENCE REALITY

The dominant consideration for generator decisions is security. Generator disclosure is a documented attack enabler.

5.1 The CVE Exploitation Pathway

The standard attack pattern:

  1. Vulnerability disclosed: a CVE is published for WordPress 6.x or Drupal 9.x or another platform.
  2. Bots crawl the web: automated scanners look for sites matching the affected version.
  3. Meta generator is one of the strongest fingerprinting signals: faster than other methods.
  4. Target acquired: the site is added to an attack list.
  5. Exploitation attempt: bot tests the known CVE against the site.
  6. If unpatched: compromise.

Removing the generator tag does not patch vulnerabilities, but it dramatically reduces opportunistic targeting. Attackers move on to easier targets.

5.2 The Wappalyzer Reality

Wappalyzer (and BuiltWith, SimilarWeb, etc.) are browser extensions and APIs that identify the tech stack of any site. They use multiple signals:

Removing the meta generator significantly reduces the fingerprinting confidence but doesn't eliminate it (other signals remain). Defense in depth: address multiple signals.

5.3 The Targeted Attack vs Opportunistic Attack Difference

Two types of attacks:

For Bubbles clients (small businesses, not high profile targets), opportunistic attacks are 99% of the threat. Removing generator addresses this efficiently.

5.4 The Federal Subcontractor Implication

For SDVOSB federal subcontracting work, security hardening is contract critical:

For Joseph's SDVOSB work: meta generator with version is a hard fail.

5.5 The Bubbles Practical Impact

For typical Bubbles client sites (130+ live):

The cleanup is fast (remove a few lines from template); the security benefit is real.


6. WHAT EACH CMS AUTO GENERATES

Catalog of what major platforms put in the generator tag.

6.1 WordPress

<meta name="generator" content="WordPress 6.4.2">

Auto added by WordPress core. Removing requires a functions.php hook (Section 7).

6.2 Drupal

<meta name="Generator" content="Drupal 10 (https://www.drupal.org)">

Note: Drupal capitalizes "Generator". Behavior consistent with HTML5 case insensitive parsing but visually different.

6.3 Joomla

<meta name="generator" content="Joomla! - Open Source Content Management">

Joomla typically doesn't include the version in the generator tag itself, but uses other signatures.

6.4 Hugo (Static Site Generator)

<meta name="generator" content="Hugo 0.140.0">

Auto added by Hugo. Removed via --disableHugoGeneratorInject flag at build time or enableRobotsTXT = true (counterintuitively).

6.5 Jekyll

<meta name="generator" content="Jekyll v4.3.2">

Auto added by Jekyll's default layouts; removed by editing the template directly.

6.6 Gatsby

<meta name="generator" content="Gatsby 5.13.3">

Auto added to the head; removed via gatsby-config.js modifications or by replacing the default layout.

6.7 Next.js

Next.js does NOT auto add a generator tag by default. Sites built with Next.js can add it manually if desired (often as brand statement).

6.8 Astro

<meta name="generator" content="Astro v4.7.0">

Auto added by Astro.

6.9 Wix, Squarespace, Weebly

<!-- Wix -->
<meta name="generator" content="Wix.com Website Builder">

<!-- Squarespace -->
<meta name="generator" content="Squarespace">

<!-- Weebly -->
<meta name="generator" content="Weebly">

These are platform builders; the platform identification is visible.

6.10 Shopify

<meta name="generator" content="Shopify">

Shopify identifies itself in the generator tag.

6.11 Webflow

<meta name="generator" content="Webflow">

Webflow self identifies. Less version specific than WordPress.

6.12 Hand Coded (Bubbles)

<!-- Default: omit entirely -->

<!-- Optional brand statement: -->
<meta name="generator" content="Hand coded by ThatDeveloperGuy.com">

No version. Brand reinforcement.

6.13 The Pattern

Most platforms auto identify themselves; most include version when they identify. WordPress is the most aggressive version discloser (and consequently the most attacked).

For migrations: every platform's generator tag should be removed in the new build. For new Bubbles builds: optional brand statement.


7. WORDPRESS SPECIFIC (THE BIGGEST MIGRATION CLEANUP TARGET)

WordPress is the most common CMS Bubbles encounters during client migrations. The generator removal pattern is well documented.

7.1 The Default WordPress Behavior

By default, WordPress adds:

<meta name="generator" content="WordPress 6.4.2">

Plus various plugin generators (Yoast, RankMath, etc.) and HTML comments revealing versions.

7.2 The functions.php Removal Hook

// Add to active theme's functions.php
// Removes the WordPress generator meta tag from <head>

function remove_wordpress_generator() {
    return '';
}
add_filter('the_generator', 'remove_wordpress_generator');

// Or more specifically:
remove_action('wp_head', 'wp_generator');

After applying:

# Verify removal
curl -s https://example.com/ | grep -oE 'meta name="generator"[^>]+'
# Expected: no output

7.3 The Plugin Approach

If functions.php editing isn't possible (managed WordPress hosting, no developer access):

For Bubbles WordPress sites we maintain: code level (functions.php) preferred.

7.4 The Yoast Generator Tag

Yoast SEO adds its own generator:

<meta name="generator" content="WordPress 6.4.2">
<meta property="generator" content="Yoast SEO v21.7">

The property="generator" is non standard but Yoast emits it. Removing:

// Remove Yoast generator
add_filter('wpseo_debug_marker', '__return_empty_string');

7.5 The HTML Comment Disclosure

WordPress and plugins also add HTML comments that reveal versions:

<!-- All in One SEO Pro Pack 4.3.6 -->
<!-- WP Rocket 3.15.2 -->
<!-- Yoast SEO v21.7 - https://yoast.com/wordpress/plugins/seo/ -->

These are not meta tags but serve the same fingerprinting purpose. Removal requires per plugin configuration or code editing.

7.6 The Bulk WordPress Hardening

// functions.php additions for WordPress security hardening

// Remove WordPress version from head
remove_action('wp_head', 'wp_generator');

// Remove version from RSS feeds
add_filter('the_generator', '__return_empty_string');

// Remove version from scripts and stylesheets
function remove_version_query($src) {
    return remove_query_arg('ver', $src);
}
add_filter('style_loader_src', 'remove_version_query', 9999);
add_filter('script_loader_src', 'remove_version_query', 9999);

// Remove Yoast SEO generator marker
add_filter('wpseo_debug_marker', '__return_empty_string');

// Remove generator from RSS
foreach (['rss2_head', 'commentsrss2_head', 'rss_head', 'rdf_header', 'atom_head', 'comments_atom_head', 'opml_head', 'app_head'] as $hook) {
    remove_action($hook, 'the_generator');
}

7.7 The Migration Cleanup For WordPress to Bubbles

When migrating a WordPress site to hand coded Bubbles:

# 1. Audit the old WordPress for generator
curl -s https://oldwordpresssite.com/ | grep -oE 'meta name="generator"[^>]+\|<!-- [A-Z][^>]+ \d+\.\d+'

# 2. Build the new Bubbles site without any generator tag

# 3. After deploy, verify cleanup
curl -s https://newsite.com/ | grep -oE 'meta name="generator"[^>]+'
# Expected: no output OR only brand statement

# 4. Verify HTTP headers also cleaned (no X-Powered-By)
curl -sI https://newsite.com/ | grep -iE "x-powered-by|server"
# Expected: server: nginx (or hidden); no x-powered-by

8. STATIC SITE GENERATOR BEHAVIOR (HUGO, JEKYLL, GATSBY, NEXT.JS)

For Bubbles clients using static site generators (rare; Joseph hand codes), the removal patterns differ.

8.1 Hugo

<!-- Default Hugo output -->
<meta name="generator" content="Hugo 0.140.0">

Removal options:

Option A: command line flag at build time

hugo --disableHugoGeneratorInject

Option B: configuration file

# config.toml
disableHugoGeneratorInject = true

Option C: layout template

{# layouts/_default/baseof.html #}
<head>
    <meta charset="utf-8">
    <!-- Don't include {{ hugo.Generator }} -->
</head>

8.2 Jekyll

<!-- Default Jekyll output -->
<meta name="generator" content="Jekyll v4.3.2">

Removal: edit the layout template directly.

<!-- _layouts/default.html -->
<head>
    <meta charset="utf-8">
    <!-- Don't include {{ jekyll.environment }} or version -->
</head>

Note: Jekyll's default layouts may have the generator tag explicitly included; review and remove.

8.3 Gatsby

<!-- Default Gatsby output -->
<meta name="generator" content="Gatsby 5.13.3">

Removal: modify gatsby-config.js or override the default head plugin behavior.

// gatsby-config.js
module.exports = {
    // Add to plugins or modify head generation
    pathPrefix: '/',
    plugins: [
        // ... other plugins
    ],
    // Suppress generator (mechanism varies by Gatsby version)
};

8.4 Next.js

Next.js does NOT auto include a generator tag. Sites built with Next.js have full control over the head.

If a generator tag is desired (brand statement), it can be added manually:

// pages/_document.js
import Document, { Html, Head, Main, NextScript } from 'next/document';

export default class MyDocument extends Document {
    render() {
        return (
            <Html lang="en">
                <Head>
                    <meta name="generator" content="Hand coded by ThatDeveloperGuy.com" />
                </Head>
                <body>
                    <Main />
                    <NextScript />
                </body>
            </Html>
        );
    }
}

8.5 Astro

<!-- Default Astro output -->
<meta name="generator" content="Astro v4.7.0">

Removal: configure Astro to suppress (varies by version; consult current Astro docs).

8.6 The General SSG Pattern

Static site generators consistently auto add their generator tag. The removal pattern varies but follows the same template logic. For any new Bubbles client using an SSG: configure to suppress generator at build time.


9. THE VERSION NUMBER RISK (CVE EXPLOITATION PATHWAY)

The single most critical issue is version number disclosure. Each version disclosure is a CVE exploitation invitation.

9.1 The Public Vulnerability Database

CVEs (Common Vulnerabilities and Exposures) are publicly cataloged at:

Anyone can search for "WordPress 5.2" or "Drupal 9.3" and find published vulnerabilities. Attackers do this routinely.

9.2 The Attack Pattern

1. Vulnerability disclosed for WordPress 6.3.x.
2. NVD publishes CVE entry within 24 hours.
3. Security researchers publish PoC (proof of concept) within days.
4. Automated bots scan internet for "WordPress 6.3" in generator tags.
5. Sites matching are queued for attack.
6. CVE exploitation attempts begin.
7. Unpatched sites compromised.

Timeline: from CVE disclosure to active exploitation: 1-7 days.
Sites with version disclosure: priority targets.
Sites without version disclosure: lower priority (still attacked but later).

9.3 The Specific CMS Risks

WordPress version disclosure: - Most prominent target (largest install base). - Multiple known CVEs per year. - Automated exploitation tools widely available. - Removing generator is the single highest impact security improvement.

Drupal version disclosure: - High value target (government, enterprise). - "Drupalgeddon" series of major CVEs. - Removing version disclosure is documented security baseline.

Joomla version disclosure: - Smaller install base than WordPress but still attacked. - Auto removal less common; manual editing required.

Static site generator disclosure: - Less critical (the SSG doesn't run on the server post build). - Still reveals tech stack; minor competitive intel.

9.4 The Hardening Hierarchy

For Bubbles client sites:

  1. HIGHEST priority: remove WordPress/Drupal/Joomla version from generator tags.
  2. HIGH priority: remove X-Powered-By HTTP header (framework-http-security-headers.md).
  3. HIGH priority: hide nginx version with server_tokens off; (framework-http-performance-headers.md Section 4).
  4. MEDIUM priority: remove plugin version disclosures (Yoast, etc.).
  5. MEDIUM priority: remove HTML comments revealing versions.
  6. LOW priority: SSG version disclosure (less actively exploited).

9.5 The Bubbles Standard

Never include version numbers in any metadata visible to public requests.

This applies to:

If a tool or service insists on disclosing version, configure it to suppress or replace with generic value.


10. THE X-POWERED-BY AND SERVER HEADER RELATIONSHIP

The HTTP layer equivalent of the meta generator tag.

10.1 The X-Powered-By Header

HTTP/2 200 OK
X-Powered-By: PHP/8.2.4

Used by Express, PHP, ASP.NET, and others to declare the backend language or framework. Same security concerns as generator tag.

For Bubbles: never include X-Powered-By in nginx responses. FastAPI does not add X-Powered-By by default (verify per app).

10.2 The Server Header

HTTP/2 200 OK
Server: nginx/1.26.0

The Server header is set by nginx automatically. Disclosing the version enables CVE targeting against nginx itself.

# nginx.conf (http context)
server_tokens off;

After this directive:

HTTP/2 200 OK
Server: nginx

Only "nginx" is disclosed; the version is hidden.

Per framework-http-performance-headers.md Section 4, the server_tokens off; directive should be set on every Bubbles nginx installation.

10.3 The Full Hardening Pattern

# /etc/nginx/nginx.conf

http {
    # Hide nginx version
    server_tokens off;

    # Remove any X-Powered-By that upstream might set
    proxy_hide_header X-Powered-By;

    # ... other directives
}

Plus in FastAPI:

from fastapi import FastAPI
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.responses import Response

class HardenHeadersMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request, call_next):
        response = await call_next(request)
        # Remove version headers if any
        response.headers.pop("X-Powered-By", None)
        response.headers.pop("Server", None)
        return response

app = FastAPI()
app.add_middleware(HardenHeadersMiddleware)

10.4 The Aggregate Verification

# Check for any version disclosure in response
curl -sI https://example.com/ | grep -iE "server|x-powered|x-aspnet|x-runtime"

# Expected (Bubbles standard):
# server: nginx

# Not expected:
# server: nginx/1.26.0
# x-powered-by: PHP/8.2.4
# x-aspnet-version: 4.8.4515.0
# x-runtime: 0.012345

And for HTML:

# Check for any version disclosure in HTML
curl -s https://example.com/ | grep -iE 'name="generator"\|property="generator"\|version|powered'

# Expected: only brand statement (if any) without version numbers

11. THE BRAND STATEMENT USE CASE (LEGITIMATE GENERATOR VALUE)

For hand coded Bubbles sites, the generator tag can serve as a brand reinforcement signal.

11.1 The Pattern

<meta name="generator" content="Hand coded by ThatDeveloperGuy.com">

No version number. Brand attribution. Differentiation from platform sites.

11.2 The Marketing Reach

When Wappalyzer (or similar) catalogues a site, the generator tag is displayed. Sites that competitors check via Wappalyzer see:

Technology Stack:
- Custom (Hand coded by ThatDeveloperGuy.com)
- No WordPress, no platform
- Custom HTML/CSS/JavaScript

This functions as marketing for Joseph among competitive intelligence researchers. The cost is zero; the brand visibility is real.

11.3 The Bubbles Convention

The standard Bubbles footer convention "Crafted by ThatDeveloperGuy.com" extends naturally to the generator tag:

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Site Title</title>
    <meta name="description" content="...">

    <meta name="generator" content="Hand coded by ThatDeveloperGuy.com">

    <!-- ... -->
</head>
<body>
    <!-- ... content ... -->

    <footer>
        <p>Crafted by ThatDeveloperGuy.com</p>
    </footer>
</body>

The generator tag and footer reinforce the same message: this is hand coded, not platform built.

11.4 The Client Specific Variations

For client sites where Joseph wants to subtly disclose the developer:

<meta name="generator" content="Hand coded by ThatDeveloperGuy.com">

For client sites where the client prefers no developer attribution in metadata:

<!-- Omit the tag entirely -->

For client sites with their own brand statement:

<meta name="generator" content="Custom build for Client Name">

11.5 The Wikidata Approach (For Highest Authority Sites)

For sites where the Bubbles brand is heavily visible (Joseph's own sites, MEGAMIND project), the generator could reference the Wikidata identity:

<meta name="generator" content="Joseph Anady (Q138610626) at ThatDeveloperGuy.com">

This connects the generator signal to the verified identity pattern (covered in framework-html-meta-author.md Section 9).


12. LEGITIMATE MODERN USES

The narrow cases where generator has legitimate value.

12.1 Brand Statement (Hand Coded Sites)

As covered in Section 11. The strongest legitimate use case for new sites.

12.2 Internal Documentation

For internal documentation systems where tracking the generation tool matters:

<meta name="generator" content="Docusaurus v3.0">

For internal facing tools where security concerns are minimal and tooling identification matters for the team.

12.3 Academic Or Archival Indexing

Some academic indexing systems and the Wayback Machine track generator tags for historical preservation. The information helps reconstruct what tools were popular at a given time.

For most Bubbles client sites: not relevant.

12.4 Dublin Core Metadata Compliance

Dublin Core (the metadata standard underlying RSS and other web standards) recommends the generator concept. Sites integrating with academic indexing systems may benefit.

For most Bubbles client sites: not relevant.

12.5 Internal Development Reference

Some teams use generator to track which build pipeline produced a deployed asset:

<meta name="generator" content="bubbles-pipeline-v2.4">

For debugging deployments: useful. For public facing: avoid version disclosure.


13. THE BUBBLES DECISION FRAMEWORK

Each client site requires a deliberate decision.

13.1 The Decision Tree

Is this a new hand coded Bubbles build?
   |
   |---> YES: Optional brand statement.
   |         <meta name="generator" content="Hand coded by ThatDeveloperGuy.com">
   |         OR omit entirely.
   |
   |---> NO: Is this a migration from CMS?
            |
            |---> YES: AUDIT for stale generator tags. REMOVE.
            |
            |---> NO: Existing CMS site we're maintaining?
                     |
                     |---> Yes: Configure CMS to suppress generator.
                     |
                     |---> No (other): omit by default.

13.2 The Per Client Decision

ThatDeveloperGuy.com (Joseph's own site):

Include brand statement. Joseph wants visibility for his hand coding approach.

<meta name="generator" content="Hand coded by ThatDeveloperGuy.com">

Federal subcontractor sites (SDVOSB context):

Omit entirely. Federal security baselines discourage information disclosure.

<!-- Omit generator tag -->

Marshallese-Voices, Arkansas Counseling and Wellness (community/local clients):

Optional brand statement at client discretion. Default: include subtle brand statement.

<meta name="generator" content="Hand coded by ThatDeveloperGuy.com">

Handled Tax and Advisory (Amanda Emerdinger):

Optional brand statement. Note: do not associate any tax credential with the generator. The generator is about the website, not the tax service.

<meta name="generator" content="Hand coded by ThatDeveloperGuy.com">

Client migrations from WordPress/Squarespace/Wix:

Aggressive cleanup. Remove all generator tags from old platform. New Bubbles build follows the standard pattern (brand statement or omit).

Anonymous client sites (where developer attribution not wanted):

Omit entirely.

<!-- Omit generator tag -->

13.3 The Default

For most Bubbles work, the default is the brand statement pattern:

<meta name="generator" content="Hand coded by ThatDeveloperGuy.com">

Switching to "omit" requires client request or security baseline reason.


14. THE CMS MIGRATION CLEANUP PATTERN

The most important operational pattern for Bubbles: aggressive cleanup of CMS generator tags when migrating to hand coded.

14.1 The Pre Migration Audit

Before migrating, document what the old site discloses:

# Audit old WordPress site for all disclosures
SITE=https://oldwordpresssite.com

echo "=== Generator tag ==="
curl -s "$SITE/" | grep -oE 'meta name="generator"[^>]+'

echo "=== HTML comments revealing versions ==="
curl -s "$SITE/" | grep -oE '<!-- [A-Z][^>]+ \d+\.\d+[^>]*'

echo "=== HTTP X-Powered-By ==="
curl -sI "$SITE/" | grep -i "x-powered-by"

echo "=== HTTP Server ==="
curl -sI "$SITE/" | grep -i "^server"

echo "=== Plugin signatures in JS/CSS paths ==="
curl -s "$SITE/" | grep -oE 'wp-content/plugins/[^/]+' | sort -u

Document the findings; ensure none persist in the new build.

14.2 The Build Phase Verification

During new site development:

# Check the new template/build
SITE=https://staging.newsite.com

# Should NOT match old WordPress signatures
curl -s "$SITE/" | grep -ciE 'wordpress|wp-content|wp-admin'
# Expected: 0

# Generator: should be hand coded brand statement or absent
curl -s "$SITE/" | grep -oE 'meta name="generator"[^>]+'
# Expected: "Hand coded by ThatDeveloperGuy.com" or no output

14.3 The Post Deploy Audit

After production deploy:

SITE=https://newsite.com

# 1. No WordPress signatures
curl -s "$SITE/" | grep -ciE 'wordpress|wp-content|wp-admin'

# 2. No old CMS generator
curl -s "$SITE/" | grep -oE 'meta name="generator"[^>]+'

# 3. Only expected brand statement (if any)
CURRENT_GENERATOR=$(curl -s "$SITE/" | grep -oE 'meta name="generator"[^>]+')
echo "Current generator: $CURRENT_GENERATOR"

# 4. HTTP headers hardened
curl -sI "$SITE/" | grep -iE "server|x-powered"
# Expected: only "server: nginx" (no version, no x-powered-by)

14.4 The Bulk Migration Script

#!/bin/bash
# /usr/local/bin/migration-cleanup-audit.sh
# Audit all Bubbles client sites for stale CMS generator tags

for site_dir in /var/www/sites/*/; do
    SITE=$(basename "$site_dir")
    INDEX="$site_dir/index.html"

    if [ -f "$INDEX" ]; then
        # Check for CMS generator tags
        if grep -qiE 'meta name="generator" content="WordPress|Drupal|Joomla|Wix|Squarespace' "$INDEX"; then
            echo "STALE CMS GENERATOR: $SITE"
            echo "  $(grep -oE 'meta name="generator"[^>]+' "$INDEX" | head -1)"
        fi

        # Check for plugin generator markers
        if grep -qiE 'wp-content/plugins/' "$INDEX"; then
            echo "WP PLUGIN SIGNATURES: $SITE"
        fi
    fi
done

14.5 The Cleanup Command

# Remove specific generator tags from all HTML files in a site
SITE_DIR=/var/www/sites/example.com

# Remove WordPress generator
find "$SITE_DIR" -name "*.html" -type f -exec \
    sed -i '/meta name="generator" content="WordPress/d' {} \;

# Remove Yoast generator
find "$SITE_DIR" -name "*.html" -type f -exec \
    sed -i '/meta property="generator" content="Yoast/d' {} \;

# Remove all generator tags (most aggressive)
find "$SITE_DIR" -name "*.html" -type f -exec \
    sed -i '/meta name="generator"/d' {} \;

# Verify cleanup
curl -s https://example.com/ | grep -oE 'meta name="generator"[^>]+'

15. THE SERVER SIDE HARDENING (NGINX SERVER_TOKENS, FASTAPI SERVER HEADER)

The generator tag cleanup is incomplete without HTTP layer hardening.

15.1 The nginx server_tokens Directive

# /etc/nginx/nginx.conf (http context)

http {
    # Hide nginx version in error pages and Server header
    server_tokens off;

    # ... other directives
}

After applying:

nginx -t && systemctl reload nginx

# Verify
curl -sI https://example.com/ | grep -i "^server"
# Expected: server: nginx (no version)

Per framework-http-performance-headers.md Section 4, this directive is mandatory on every Bubbles installation.

15.2 Removing X-Powered-By From Upstream

If the FastAPI sidecar or any upstream emits X-Powered-By:

location / {
    proxy_pass http://127.0.0.1:9090;
    proxy_hide_header X-Powered-By;
}

This strips the header from nginx response even if upstream sets it.

15.3 FastAPI Header Cleanup

from fastapi import FastAPI
from starlette.middleware.base import BaseHTTPMiddleware

app = FastAPI()

class CleanupHeadersMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request, call_next):
        response = await call_next(request)
        # Remove headers that may leak information
        for header in ["X-Powered-By", "Server", "X-AspNet-Version"]:
            response.headers.pop(header, None)
        return response

app.add_middleware(CleanupHeadersMiddleware)

15.4 The Full Hardening Verification

# Single comprehensive check
SITE=https://example.com

echo "=== HTML disclosures ==="
curl -s "$SITE/" | grep -oE 'meta name="generator"[^>]+'

echo "=== HTTP disclosures ==="
curl -sI "$SITE/" | grep -iE "server:|x-powered-by|x-aspnet|x-runtime"

# For Bubbles standard:
# - HTML: only brand statement (or nothing)
# - HTTP: only "server: nginx" (no version, no x-powered-by)

15.5 The Audit Script

#!/bin/bash
# /usr/local/bin/disclosure-audit.sh
# Comprehensive information disclosure audit

URL=$1

echo "=== Disclosure audit for $URL ==="
echo ""

# HTML meta generator
META_GEN=$(curl -s "$URL" | grep -oE 'meta name="generator"[^>]+' | head -1)
echo "HTML meta generator: ${META_GEN:-(none)}"

# Check for version in meta generator
if echo "$META_GEN" | grep -qE '[0-9]+\.[0-9]+'; then
    echo "  WARNING: version number disclosed in meta generator"
fi

# HTML comments
HTML_COMMENTS=$(curl -s "$URL" | grep -oE '<!--[^>]*[0-9]+\.[0-9]+[^>]*-->' | head -3)
if [ -n "$HTML_COMMENTS" ]; then
    echo "HTML comments with versions:"
    echo "$HTML_COMMENTS"
fi

# HTTP Server
SERVER=$(curl -sI "$URL" | grep -i "^server" | tr -d '\r')
echo "HTTP Server: $SERVER"
if echo "$SERVER" | grep -qE '/[0-9]+\.[0-9]+'; then
    echo "  WARNING: nginx version disclosed; set server_tokens off"
fi

# X-Powered-By
XPOWERED=$(curl -sI "$URL" | grep -i "x-powered-by")
if [ -n "$XPOWERED" ]; then
    echo "X-Powered-By: $XPOWERED"
    echo "  WARNING: backend framework disclosed"
fi

echo ""
echo "=== End audit ==="

16. ASSET CLASS AND USE CASE RECIPES

Paste ready snippets per scenario.

16.1 Canonical Bubbles head with brand statement

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>Page Title | ThatDeveloperGuy</title>
    <meta name="description" content="...">

    <meta name="generator" content="Hand coded by ThatDeveloperGuy.com">

    <!-- ... -->
</head>

16.2 Canonical Bubbles head without generator (federal/anonymous)

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>Page Title</title>
    <meta name="description" content="...">

    <!-- NO meta generator (federal/anonymous client preference) -->

    <!-- ... -->
</head>

16.3 WordPress generator removal (functions.php)

// /wp-content/themes/active-theme/functions.php

// Remove WordPress generator from <head>
remove_action('wp_head', 'wp_generator');

// Remove generator from RSS feeds
foreach (['rss2_head', 'commentsrss2_head', 'rss_head', 'rdf_header', 'atom_head', 'comments_atom_head', 'opml_head', 'app_head'] as $hook) {
    remove_action($hook, 'the_generator');
}

// Remove Yoast generator
add_filter('wpseo_debug_marker', '__return_empty_string');

// Remove version query string from scripts and styles
function remove_version_query($src) {
    return remove_query_arg('ver', $src);
}
add_filter('style_loader_src', 'remove_version_query', 9999);
add_filter('script_loader_src', 'remove_version_query', 9999);

16.4 Hugo generator removal

# config.toml
disableHugoGeneratorInject = true

Or:

# Build with the flag
hugo --disableHugoGeneratorInject

Or in the template:

<!-- layouts/_default/baseof.html -->
<head>
    <meta charset="utf-8">
    <!-- Don't include {{ hugo.Generator }} -->
</head>

16.5 Jekyll generator removal

<!-- _layouts/default.html -->
<head>
    <meta charset="utf-8">
    <!-- Don't include any reference to Jekyll version -->
</head>

16.6 nginx server_tokens hardening

# /etc/nginx/nginx.conf

http {
    server_tokens off;

    # ... other directives
}

Apply:

nginx -t && systemctl reload nginx

# Verify
curl -sI https://example.com/ | grep -i "^server"
# Expected: server: nginx (no version)

16.7 nginx remove X-Powered-By from proxy

location / {
    proxy_pass http://127.0.0.1:9090;
    proxy_hide_header X-Powered-By;
    proxy_hide_header Server;
}

16.8 FastAPI cleanup middleware

from fastapi import FastAPI
from starlette.middleware.base import BaseHTTPMiddleware

app = FastAPI()

class CleanupHeadersMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request, call_next):
        response = await call_next(request)
        for header in ["X-Powered-By", "Server", "X-AspNet-Version"]:
            response.headers.pop(header, None)
        return response

app.add_middleware(CleanupHeadersMiddleware)

16.9 Bulk audit across all client sites

#!/bin/bash
# /usr/local/bin/generator-audit.sh

echo "=== Bubbles generator tag audit ==="

for site_dir in /var/www/sites/*/; do
    SITE=$(basename "$site_dir")
    INDEX="$site_dir/index.html"

    if [ -f "$INDEX" ]; then
        # Find any generator tag
        GENERATOR=$(grep -oE 'meta name="generator"[^>]+' "$INDEX" | head -1)

        if [ -n "$GENERATOR" ]; then
            # Check if it has a version number
            if echo "$GENERATOR" | grep -qE '[0-9]+\.[0-9]+'; then
                echo "VERSION DISCLOSED: $SITE"
                echo "  $GENERATOR"
            elif echo "$GENERATOR" | grep -qiE 'WordPress|Drupal|Joomla|Wix|Squarespace'; then
                echo "CMS DISCLOSED: $SITE"
                echo "  $GENERATOR"
            else
                # Brand statement, no version. OK.
                : # echo "OK: $SITE - $GENERATOR"
            fi
        fi
    fi
done

echo ""
echo "=== End audit ==="

16.10 The pre deploy check

#!/bin/bash
# /usr/local/bin/predeploy-generator-check.sh

SITE_DIR=$1
FAILED=0

find "$SITE_DIR" -name "*.html" -type f | while read f; do
    GENERATOR=$(grep -oE 'meta name="generator"[^>]+' "$f" | head -1)

    if [ -n "$GENERATOR" ]; then
        # No version numbers allowed
        if echo "$GENERATOR" | grep -qE '[0-9]+\.[0-9]+'; then
            echo "FAIL: $f has version in generator"
            echo "  $GENERATOR"
            FAILED=1
        fi

        # No CMS names allowed (suggests migration leftover)
        if echo "$GENERATOR" | grep -qiE 'WordPress|Drupal|Joomla|Wix|Squarespace'; then
            echo "FAIL: $f has CMS name in generator (likely migration leftover)"
            echo "  $GENERATOR"
            FAILED=1
        fi
    fi
done

if [ $FAILED -eq 1 ]; then
    echo ""
    echo "Pre deploy check failed. Clean up generator tags before deploying."
    exit 1
fi

echo "OK: pre deploy generator check passed"

17. BUBBLES STANDARD PATTERN (PASTE READY)

The canonical Bubbles configuration for generator handling.

17.1 The Template Default

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>{title}</title>
    <meta name="description" content="{description}">
    <meta name="author" content="{author}">

    <meta name="generator" content="Hand coded by ThatDeveloperGuy.com">

    <!-- block head_extra -->
</head>

17.2 The nginx Configuration

# /etc/nginx/nginx.conf (http context)

http {
    # Hide nginx version
    server_tokens off;

    # Hide X-Powered-By from upstreams
    proxy_hide_header X-Powered-By;

    # ... other directives
}

17.3 The FastAPI Middleware

from fastapi import FastAPI
from starlette.middleware.base import BaseHTTPMiddleware

app = FastAPI()

class HardenHeadersMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request, call_next):
        response = await call_next(request)
        for header in ["X-Powered-By", "Server", "X-AspNet-Version", "X-Runtime"]:
            response.headers.pop(header, None)
        return response

app.add_middleware(HardenHeadersMiddleware)

17.4 The Cleanup Audit Workflow

Weekly or before any major deployment:

sudo /usr/local/bin/generator-audit.sh
sudo /usr/local/bin/disclosure-audit.sh https://example.com/

# Apply any fixes
find /var/www/sites/example.com/ -name "*.html" -type f -exec sed -i '/meta name="generator" content="WordPress/d' {} \;
find /var/www/sites/example.com/ -name "*.html" -type f -exec sed -i '/meta property="generator" content="Yoast/d' {} \;

# Reload nginx if configuration changed
nginx -t && systemctl reload nginx

17.5 The Client Migration Checklist

For every WordPress (or other CMS) migration:

  1. [ ] Pre migration: document existing generator tags.
  2. [ ] Build phase: new templates use only Bubbles brand statement.
  3. [ ] Pre deploy: run predeploy-generator-check.sh.
  4. [ ] Post deploy: run disclosure-audit.sh.
  5. [ ] Verify HTTP headers: no X-Powered-By, no Server version.
  6. [ ] Verify HTML: no CMS generator, no plugin signatures.

18. AUDIT CHECKLIST

Run through these 40 items for any production deployment.

Core meta generator

  1. [ ] No <meta name="generator"> with version number on any indexable URL.
  2. [ ] No <meta name="generator"> with "WordPress", "Drupal", "Joomla", "Wix", "Squarespace" as content.
  3. [ ] If generator tag present, it is a brand statement only (e.g., "Hand coded by ThatDeveloperGuy.com").
  4. [ ] No <meta property="generator"> (Yoast specific) on any page.

Migration cleanup

  1. [ ] Migrated sites have all old CMS generator tags removed.
  2. [ ] No wp-content paths in HTML source.
  3. [ ] No CMS specific signatures (plugin paths, theme paths).
  4. [ ] No HTML comments revealing CMS versions.

HTTP layer

  1. [ ] server_tokens off; in nginx.conf.
  2. [ ] Server: nginx (no version) in response headers.
  3. [ ] No X-Powered-By header in responses.
  4. [ ] No X-AspNet-Version header.
  5. [ ] No X-Runtime header.

FastAPI specific

  1. [ ] CleanupHeadersMiddleware applied (removes leaked headers).
  2. [ ] No version disclosure in FastAPI default behavior.

Site type specific

  1. [ ] Hand coded Bubbles sites: brand statement OR omit.
  2. [ ] Federal subcontractor sites: omit entirely.
  3. [ ] Anonymous client sites: omit entirely.
  4. [ ] Client preference sites: per client preference.

CMS configuration (where still managed)

  1. [ ] WordPress sites: functions.php hook removes generator.
  2. [ ] Hugo sites: disableHugoGeneratorInject = true.
  3. [ ] Jekyll sites: layout template doesn't include generator.
  4. [ ] Gatsby sites: gatsby-config.js suppresses generator.

Verification commands tested

  1. [ ] curl -s URL | grep generator returns expected (brand statement or empty).
  2. [ ] curl -sI URL | grep -i "x-powered" returns nothing.
  3. [ ] curl -sI URL | grep -i "^server" returns "server: nginx" only.

Bulk audit

  1. [ ] generator-audit.sh shows no version disclosures across all client sites.
  2. [ ] disclosure-audit.sh clean for each client site.

Pre deploy gates

  1. [ ] CI/CD or manual pre deploy check verifies no version in generator.
  2. [ ] Template review rejects new version disclosures.

Documentation

  1. [ ] Per client generator decision documented.
  2. [ ] Migration cleanup process documented.
  3. [ ] WordPress hardening hooks documented.

Cross cutting

  1. [ ] No version disclosure in robots.txt comments.
  2. [ ] No version disclosure in sitemap comments.
  3. [ ] No version disclosure in HTML comments.
  4. [ ] No version disclosure in JavaScript console output.
  5. [ ] No version disclosure in CSS comments.

Quarterly review

  1. [ ] Quarterly audit run across all client sites.
  2. [ ] New CVEs reviewed; sites with version disclosure prioritized for cleanup if any remain.

A site that passes all 40 has correctly handled generator disclosure: no version numbers, no CMS revelations, only brand statements where appropriate, with HTTP layer hardening in place.


19. COMMON PITFALLS

Twelve patterns to recognize and avoid.

Pitfall 1: Migrated WordPress site still has WordPress generator. Symptom: page source shows <meta name="generator" content="WordPress 6.4.2"> despite being hand coded. Why it breaks: migration didn't strip the old tag. Fix: remove via sed across all HTML files; verify with grep.

Pitfall 2: Generator tag with version number. Symptom: <meta name="generator" content="WordPress 6.4.2"> or <meta name="generator" content="Hugo 0.140.0">. Why it breaks: enables CVE targeting. Fix: remove version; either omit entirely or use brand statement without version.

Pitfall 3: nginx server_tokens not set. Symptom: Server: nginx/1.26.0 in response headers. Why it breaks: nginx version disclosed; vulnerable to CVE targeting. Fix: server_tokens off; in nginx.conf.

Pitfall 4: X-Powered-By header present. Symptom: X-Powered-By: PHP/8.2.4 or X-Powered-By: Express in responses. Why it breaks: backend framework and version disclosed. Fix: remove via FastAPI middleware or nginx proxy_hide_header X-Powered-By.

Pitfall 5: HTML comments revealing plugin versions. Symptom: <!-- Yoast SEO v21.7 --> in HTML. Why it breaks: plugin version disclosure; specific vulnerability targeting. Fix: remove via WordPress functions.php hooks or post processing.

Pitfall 6: Yoast <meta property="generator"> not removed. Symptom: <meta property="generator" content="Yoast SEO v21.7"> (uses property not name). Why it breaks: separate from name="generator"; needs separate removal. Fix: add_filter('wpseo_debug_marker', '__return_empty_string');

Pitfall 7: Generator tag in noindex pages. Symptom: login pages, admin pages have generator tags. Why it breaks: unnecessary disclosure; still scrapable. Fix: omit from all pages, even noindex.

Pitfall 8: Generator as keywords stuffing alternative. Symptom: <meta name="generator" content="WordPress, Yoast, SEO optimized site">. Why it breaks: misuse of the field; doesn't help with SEO. Fix: use generator only for actual tool identification or brand statement.

Pitfall 9: Federal site has generator with CMS identification. Symptom: federal subcontractor site discloses Drupal version. Why it breaks: Section 508/NIST 800-53 audit finding. Fix: omit generator entirely for federal sites.

Pitfall 10: Generator differs across pages on same site. Symptom: homepage has generator A, blog has generator B. Why it breaks: inconsistent signal; suggests template fragmentation. Fix: consistent generator (or none) across all templates.

Pitfall 11: WordPress plugins overriding the wp_generator removal. Symptom: wp_generator removed but generator tag still appears. Why it breaks: another plugin or theme is re adding it. Fix: track down the source; remove with higher priority filter or block specifically.

Pitfall 12: Generator brand statement contains contact info. Symptom: <meta name="generator" content="Joseph Anady joseph@thatdeveloperguy.com 505-512-3662">. Why it breaks: privacy concern; spam harvesting; clutter. Fix: brand name only. Contact info goes in body content or schema.


20. DIAGNOSTIC COMMANDS

Reference of commands useful for generator investigation.

Inspect a single URL

# Extract meta generator
curl -s https://example.com/ | grep -oE 'meta name="generator"[^>]+' | head -1

# Extract Yoast property generator
curl -s https://example.com/ | grep -oE 'meta property="generator"[^>]+' | head -1

# Check for version in generator
curl -s https://example.com/ | grep -oE 'meta name="generator"[^>]+' | grep -E '[0-9]+\.[0-9]+'

# Check for CMS name in generator
curl -s https://example.com/ | grep -ioE 'meta name="generator"[^>]+' | grep -iE 'wordpress|drupal|joomla|wix|squarespace'

Inspect HTTP layer

# Check Server header
curl -sI https://example.com/ | grep -i "^server"

# Check X-Powered-By
curl -sI https://example.com/ | grep -i "x-powered-by"

# Comprehensive disclosure audit
curl -sI https://example.com/ | grep -iE "server:|x-powered|x-aspnet|x-runtime"

Bulk audit a sitemap

curl -s https://example.com/sitemap.xml | grep -oE "<loc>[^<]+</loc>" | sed 's/<[^>]*>//g' | while read url; do
    GENERATOR=$(curl -s "$url" 2>/dev/null | grep -oE 'meta name="generator"[^>]+' | head -1)
    if [ -n "$GENERATOR" ]; then
        if echo "$GENERATOR" | grep -qE '[0-9]+\.[0-9]+'; then
            echo "VERSION DISCLOSED: $url"
            echo "  $GENERATOR"
        fi
    fi
done

Cross site audit

generator-audit.sh

Detailed disclosure audit per URL

disclosure-audit.sh https://example.com/

After cleanup

# Apply changes (HTML)
nginx -t && systemctl reload nginx

# Or restart FastAPI
systemctl restart fastapi-sidecar

# Verify
disclosure-audit.sh https://example.com/

WordPress specific

# Check WordPress site for all version disclosures
SITE=https://example-wp.com

echo "=== Generator ==="
curl -s "$SITE/" | grep -oE 'meta name="generator"[^>]+\|meta property="generator"[^>]+'

echo "=== HTML comments ==="
curl -s "$SITE/" | grep -oE '<!--[^>]*[0-9]+\.[0-9]+[^>]*-->' | head -5

echo "=== Plugin signatures ==="
curl -s "$SITE/" | grep -oE 'wp-content/plugins/[^/]+' | sort -u | head -10

echo "=== HTTP headers ==="
curl -sI "$SITE/" | grep -iE "server|x-powered"

Browser based check

In Chrome DevTools:

  1. Open the page.
  2. View Source (Cmd/Ctrl + U).
  3. Search (Cmd/Ctrl + F) for "generator".
  4. Search for version patterns: regex \d+\.\d+.
  5. Network tab: inspect Response Headers for Server, X-Powered-By.

Use Wappalyzer browser extension: - Install Wappalyzer. - Visit the site. - See what tech is detected. - For Bubbles sites: should show "Custom" or no platform.


21. CROSS-REFERENCES


APPENDIX A: ONE PAGE QUICK REFERENCE

For the person who just wants the answer.

The Bubbles rule

Never include version numbers in generator. Default: brand statement OR omit.

The canonical patterns

<!-- Default (hand coded Bubbles site): -->
<meta name="generator" content="Hand coded by ThatDeveloperGuy.com">

<!-- For federal/anonymous clients: omit entirely -->
<!-- (no meta generator tag) -->

The forbidden patterns

<!-- NEVER USE (version disclosure) -->
<meta name="generator" content="WordPress 6.4.2">
<meta name="generator" content="Hugo 0.140.0">
<meta name="generator" content="Drupal 10.1.5">

The HTTP layer companion

# Required for every Bubbles nginx installation
server_tokens off;
# Required for every Bubbles FastAPI sidecar
class HardenHeadersMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request, call_next):
        response = await call_next(request)
        response.headers.pop("X-Powered-By", None)
        return response

Five rules to memorize

  1. Never disclose versions in generator (CVE exploitation invitation).
  2. WordPress migrations require explicit cleanup of WordPress generator.
  3. Brand statement "Hand coded by ThatDeveloperGuy.com" is optional but useful.
  4. HTTP layer hardening (server_tokens off, X-Powered-By removed) is mandatory.
  5. Federal subcontracting work: omit generator entirely (security baseline).

Five commands every operator should know

# 1. Check meta generator
curl -s URL | grep -oE 'meta name="generator"[^>]+'

# 2. Check HTTP disclosures
curl -sI URL | grep -iE "server|x-powered"

# 3. Bulk audit Bubbles client sites
generator-audit.sh

# 4. Remove WordPress generator from all HTML files
find /var/www/sites/example.com/ -name "*.html" -exec sed -i '/meta name="generator" content="WordPress/d' {} \;

# 5. Apply nginx changes
nginx -t && systemctl reload nginx

Three end to end tests

# 1. No version disclosed in generator
GEN=$(curl -s https://example.com/ | grep -oE 'meta name="generator"[^>]+' | head -1)
echo "$GEN" | grep -qE '[0-9]+\.[0-9]+' && echo "FAIL: version disclosed" || echo "OK"

# 2. No CMS disclosed in generator
echo "$GEN" | grep -qiE 'WordPress|Drupal|Joomla|Wix|Squarespace' && echo "FAIL: CMS disclosed" || echo "OK"

# 3. HTTP hardened
HTTP_DISCL=$(curl -sI https://example.com/ | grep -iE "x-powered|server: nginx/")
[ -z "$HTTP_DISCL" ] && echo "OK: HTTP hardened" || echo "FAIL: $HTTP_DISCL"

If all three pass AND disclosure-audit.sh reports clean AND nginx logs show no targeted CVE scan attempts, the generator layer is correctly handled.


End of framework-html-meta-generator.md.