Extensions & Integrations
Ensure your content gets found by search engines and AI systems with comprehensive SEO management for ApostropheCMS. Essential meta fields, Google Analytics integration, automated robots.txt and llms.txt generation β everything you need to boost your search rankings, control AI training usage, and drive organic traffic.
Why ApostropheCMS SEO Tools?
- π― Complete SEO Control: Essential meta fields for titles, descriptions, and canonical URLs
- π Analytics Ready: Built-in Google Analytics, Tag Manager, and Site Verification integration
- π€ Smart Automation: Automatic robots.txt generation with granular control
- β‘ Performance Optimization: Critical font preloading improves Core Web Vitals scores
- π Search Engine Friendly: Proper canonical linking prevents duplicate content issues
- π Marketing Team Ready: Easy-to-use interface for non-technical content creators
- π° E-commerce Ready: Rich structured data for products, offers, and pricing
- π€ AI-Ready: Automatic llms.txt generation for AI policy transparency (proposed standard for forward-thinking SEO)
Compatibility
This version requires the latest ApostropheCMS. When adding this module to an existing project, run npm update to ensure all ApostropheCMS modules are up-to-date.
TL;DR: Quick Setup
Install the module:
npm install @apostrophecms/seoSet your base URL (
APOS_BASE_URL).Enable Google Analytics or Tag Manager in
@apostrophecms/global.Optionally install
@apostrophecms/sitemapfor XML sitemap generation.Configure
robots.txtandllms.txtvia global settings.Choose schema types per page in the SEO tab.
Validate your structured data using Googleβs Rich Results Test.
Table of Contents
- Why ApostropheCMS SEO Tools?
- TL;DR: Quick Setup
- Table of Contents
- Installation
- Before You Start
- Core Features
- Essential Configuration
- Structured Data & Schema Types
- AI & Search Strategy
- Advanced Configuration
- Implementation Guidelines for Developers
- Extending the SEO Module with Custom JSON-LD Schemas
- Performance Optimization
- 1. Place Your Fonts in a Moduleβs
public/Directory - 2. Configure the SEO Module to Preload Fonts
- Field Reference
- π Ready for AI-Powered SEO?
- π’ Managing Multiple Sites?
- Roadmap
Installation
npm install @apostrophecms/seo
Configure the module in your app.js file:
import apostrophe from 'apostrophe';
apostrophe({
root: import.meta,
shortName: 'my-project',
modules: {
'@apostrophecms/seo': {}
}
});
Important: For proper SEO functionality, you must also configure your site's base URL. See the Essential Configuration section below.
Before You Start
This module provides SEO functionality at two levels:
β Works Immediately (No Setup Required)
These features work out-of-the-box with any ApostropheCMS site:
- Essential meta tags: Title, description, robots
- Analytics: Google Analytics, Tag Manager, Site Verification
- Site control: Automated robots.txt and llms.txt generation
- Basic structured data: WebPage and CollectionPage schemas
You can start using these features right away - just install the module and configure your global SEO settings.
βοΈ Requires Content Structure Setup
Advanced structured data types need fields with specific names in your content types:
- Article, Review: Work best with author information
- Product, HowTo: Benefit from featured images for richer results
- Recipe: Requires a featured image (Google requirement)
- VideoObject: Requires thumbnail and upload date (Google requirement)
- JobPosting: Complex schema with many required fields for Google for Jobs
- Event, LocalBusiness: Need address/location fields
- FAQPage, QAPage: Need question and answer content
[!IMPORTANT]
β οΈ Important: Field Names Must Match Expected Names for Structured Data
Some structured data types require specific field names so that the SEO module can automatically find the right values in your documents. If your content types use different field names, and you donβt configure them accordingly, your JSON-LD may be missing required properties and fail validation in Googleβs tools.
The module also supports fallbacks and flexible formats (e.g., string vs. relationship fields) for authors, images, descriptions, and dates.
π For the full list of supported field names, fallbacks, and recommended patterns, see Field Flexibility.
You can also map existing field names to required field names using the
fieldMappingsoption of the module.If you plan to rely heavily on structured data (especially for products, recipes, jobs, or video), itβs a good idea to:
- Design your content types with these field names in mind, or
- Refactor existing types to match, before enabling those schema types in production.
Core Features
Automatic SEO Fields

The module automatically adds an "SEO" tab to all page and piece editors containing:
- Title Tag: Custom titles for search results (falls back to page title)
- Meta Description: Compelling descriptions that appear in search results
- Robots Meta Tag: Control search engine indexing and following behavior
- Canonical URLs: Prevent duplicate content penalties
- Schema Type Selection: Choose the appropriate structured data type for your content
Google Analytics & Tag Manager
Built-in integration with Google Analytics, Google Tag Manager, and Google Site Verification. Simply enable the options you need and add your tracking IDs through the global configuration interface.
Supported integrations:
- Google Analytics (GA4) tracking
- Google Tag Manager for advanced marketing campaigns
- Google Site Verification for Search Console
See Essential Configuration below for setup instructions.
Automated Robots.txt
The module automatically provides a /robots.txt route with strategic control over both traditional search engines and AI crawlers. Configure through global settings with five control modes:
Available Modes:
- Allow All (Search + AI) - Default open access for all crawlers
- Allow Search, Block AI Training - Maintains search rankings while protecting content from AI training by AI agents that choose to respect this standard
- Selective AI Crawlers - Granular control over individual AI crawlers that support this standard
- Block All - Prevents all indexing
- Custom - Write your own robots.txt content
β οΈ Make sure that if you block all indexing during development, make sure to change the policy when you launch your final site.
Selective Mode Crawlers: For fine-grained control, use Selective mode to choose specific AI crawlers. These crawlers currently indicate they honor robots.txt directives.
- GPTBot (OpenAI ChatGPT training)
- ChatGPT-User (OpenAI real-time browsing)
- Google-Extended (Google AI training)
- ClaudeBot (Anthropic AI training)
- Claude-User (Anthropic real-time browsing)
- PerplexityBot (Perplexity AI)
- CCBot (Common Crawl datasets)
- Applebot-Extended (Apple Intelligence)
- FacebookBot (Meta AI)
- anthropic-ai (Anthropic general)
Traditional search engines (Googlebot, Bingbot) are always allowed unless using "Block All" mode.
β οΈ Important Note About Robots.txt Enforcement
The
robots.txtstandard is a voluntary convention, not a security mechanism. While the crawlers listed here currently state that they honorrobots.txt, real-world behavior can differ:
- Some crawlers only respect
robots.txtin certain contexts (e.g., indexing vs. real-time browsing).- User-agent policies may change over time.
- New crawlers may appear that do not publicly disclose their behavior.
ApostropheCMS provides fine-grained controls for compliant crawlers, but it cannot guarantee enforcement against bots that ignore
robots.txtor do not implement the standard. We recommend periodically reviewing crawler policies to ensure ongoing compliance.
Technical Notes:
- A physical
robots.txtfile in yourpublic/directory for a single-site project, orsites/publicanddashboard/publicdirectories for multisite will override these settings - All modes preserve traditional search engine access (except "Block All")
- See AI & Search Strategy for detailed configuration guidance
Related: This module also provides automated llms.txt generation for policy communication.
Note: A global
Disallow: /inrobots.txtmay cause some AI crawlers to skip readingllms.txt, depending on their behavior, but the file remains publicly accessible.
AI Crawler Control (llms.txt)
The module automatically provides an /llms.txt route to communicate your AI usage policies. This is complementary to robots.txt:
- robots.txt: Enforceable crawler access control (blocks/allows bots)
- llms.txt: Informational policy declaration (informs AI systems about usage terms)
β οΈ Important Note:
llms.txtis a proposed standard that is not yet widely adopted. As of this writing, most LLMs and AI systems do not respect or readllms.txtfiles. This feature is included for forward-thinking SEO strategies and may gain broader adoption in the future. For enforceable crawler control, rely onrobots.txtsettings.
Configuration options:
Allow AI Crawling (Default): Generates a comprehensive
llms.txtfile that permits responsible AI crawling with site structure informationDisallow AI Training: States content should NOT be used for AI training datasets but permits real-time search and retrieval with attribution
Custom Content: Write your own
llms.txtpolicies from scratchDisabled: Returns 404 for
/llms.txtrequests
Best Practice: Combine both tools strategically:
- Use robots.txt (Allow Search, Block AI Training mode) to technically enforce access
- Use llms.txt (Disallow AI Training mode) to clearly communicate your policies
- This dual approach provides both technical enforcement and clear policy communication
What's included in the generated file:
- Site name and description
- AI training policy (based on your selection)
- Organization information
- Links to main pages with descriptions
- Available content types
- Technical details about structured data
- Sitemap reference (if @apostrophecms/sitemap is installed)
Sitemap Integration
Works seamlessly with @apostrophecms/sitemap to generate XML sitemaps that help search engines discover and index your content. The sitemap is automatically referenced in the /llms.txt file for AI crawlers.
Performance Optimization
Critical Font Preloading - Automatically preload critical fonts to improve Core Web Vitals scores and SEO performance:
- Reduces Cumulative Layout Shift (CLS) by preventing font-loading jank
- Improves Largest Contentful Paint (LCP) with faster font rendering
- Optimizes First Contentful Paint (FCP) by eliminating render-blocking requests
Configure once in your app.js and the module handles the rest. See Performance Optimization for details.
Essential Configuration
Setting the Base URL
This step is required for proper canonical link generation and SEO performance. If using ApostropheCMS hosting, this is set automatically.
Via environment variable (recommended):
export APOS_BASE_URL=https://yoursite.com
Via configuration file:
// data/local.js
export default {
baseUrl: 'https://yoursite.com',
modules: {
// other module configuration
}
};
For multisite projects using ApostropheCMS Assembly:
The base URL is automatically configured through the baseUrlDomains option. Learn more about Assembly multisite hosting.
Google Analytics Integration
Enable Google Analytics tracking:
import apostrophe from 'apostrophe';
apostrophe({
root: import.meta,
shortName: 'my-project',
modules: {
'@apostrophecms/seo': {},
'@apostrophecms/global': {
options: {
seoGoogleAnalytics: true
}
}
}
});
This adds a field in the global configuration for your Google Analytics Measurement ID (e.g., G-XXXXXXXXXX).
Google Tag Manager Integration
For advanced tracking and marketing campaigns:
import apostrophe from 'apostrophe';
apostrophe({
root: import.meta,
shortName: 'my-project',
modules: {
'@apostrophecms/seo': {},
'@apostrophecms/global': {
options: {
seoGoogleTagManager: true
}
}
}
});
Add your GTM container ID (e.g., GTM-XXXXXXX) in the global configuration.
Google Site Verification
Verify site ownership for Google Search Console:
import apostrophe from 'apostrophe';
apostrophe({
root: import.meta,
shortName: 'my-project',
modules: {
'@apostrophecms/seo': {},
'@apostrophecms/global': {
options: {
seoGoogleVerification: true
}
}
}
});
Enter your verification meta tag content from Google Search Console in the global settings.
Sitemap Installation
[!TIP] Installations of the sitemap module is optional, but highly recommended for better search rankings
Install the companion sitemap module for XML sitemap generation:
npm install @apostrophecms/sitemap
import apostrophe from 'apostrophe';
apostrophe({
root: import.meta,
shortName: 'my-project',
modules: {
'@apostrophecms/seo': {},
'@apostrophecms/sitemap': {}
}
});
Structured Data & Schema Types
This module generates rich structured data (JSON-LD) that helps search engines understand your content. All structured data is output in a single <script type="application/ld+json"> tag with a @graph array for optimal performance.
How It Works
The module automatically generates appropriate Schema.org markup based on the schema type you select in the SEO tab of your editor:
- Sitewide schemas: WebSite and Organization data from your Global settings appear on every page
- Page-level schemas: WebPage, CollectionPage, or your chosen primary entity type
- Primary entities: Article, Product, Event, Person, LocalBusiness, and more for detail pages
- Item listings: Automatic ItemList generation for index/listing pages
Choosing the Right Schema
Select the schema type in the SEO tab of any page or piece editor.
Quick Schema Selection Guide
See details for any schema below the table.
| Content Type | Recommended Schema |
|---|---|
| Standard pages | WebPage |
| Blog index, Category pages | CollectionPage |
| Blog posts, Articles | Article |
| Product pages | Product |
| Single offers, service packages | Offer |
| Products with variants, tiered pricing | AggregateOffer |
| Event listings | Event |
| Author bios, Team pages | Person |
| Business locations | LocalBusiness |
| Job postings | JobPosting |
| Help/Support pages | FAQPage |
| Video pages | VideoObject |
| Tutorials, Guides | HowTo |
| Review articles | Review |
| Recipes | Recipe |
| Online courses | Course |
Web Page
Use for standard pages like About, Contact, or general information pages.
Best for: About pages, contact pages, general information pages, landing pages
Collection Page
Use for index and listing pages that display multiple items.
Required fields:
- Page title and description
Features:
- Automatically generates ItemList of visible items
- Toggle "Include ItemList in JSON-LD" to control ItemList output
Best for: Blog indexes, product catalogs, category pages, archives, search results
ItemList for Collection Pages
Collection pages (listing pages) can optionally include an ItemList schema in JSON-LD. This describes the items displayed on the page (articles, products, events, etc.) and can improve how search engines understand category, listing, or archive pages.
You can enable or disable this using the βInclude ItemList in JSON-LDβ toggle in the SEO tab.
When you should enable ItemList
- The page is a true index/list of items
- Blog indexes, product category pages, news archives
- You want richer structured data for list/search pages
When you should NOT enable ItemList
- The page mixes unrelated content types
- The page is heavily personalized per user
- The listing is extremely large (hundreds+ items)
- The content shown changes frequently based on filters or user input
- Itβs not actually a listing page (e.g., About, Contact)
ItemList content is generated automatically based on the items displayed by the pageβs piece-page-type query (no developer configuration required).
Article
For blog posts, news articles, and editorial content.
Required fields:
- Title (from
seoTitleor document title) - Publication date
Recommended fields:
- Author
- Featured image
- Meta description
Best for: Blog posts, news articles, editorial content, press releases
Product
For e-commerce product pages with pricing and availability.
Required fields:
- Product name
- Price and currency
Optional fields:
- Brand, SKU, GTIN (barcode)
- Product condition (new, used, refurbished)
- Availability status
- Aggregate rating and review count
- Product description
Best for: E-commerce product pages, marketplace listings, service offerings with pricing
Offer
For single-price items, services, or standalone offers.
Required fields:
- Offer name
- Price and currency
Optional fields:
- Availability status (In Stock, Out of Stock, Pre-order, Discontinued, etc.)
- Valid date ranges (validFrom, priceValidUntil)
- Item condition (New, Used, Refurbished, Damaged)
- Seller information (defaults to your Organization)
- Offer URL
- Shipping details (rate, destination, delivery time)
Best for:
- Individual service packages with fixed pricing
- Event tickets
- Subscription plans
- One-time purchase offers
- Limited-time deals
SEO Impact: Enables rich snippets showing pricing, availability, and seller info directly in search results. Improves visibility for Google Merchant Center and Shopping listings.
Example use case: A web design agency offering a "Starter Website Package" for $2,999 with a 30-day delivery time.
Aggregate Offer
For items with multiple price points, variants, or marketplace scenarios.
Required fields:
- Offer name
- Low price and high price
- Currency
Optional fields:
- Offer count (number of variants)
- Individual offers array (each with name, price, availability, URL)
- Common availability status
- Seller information
Best for:
- Products with size/color/material variants
- Marketplace listings from multiple sellers
- Tiered service packages (Basic, Pro, Enterprise)
- Hotel rooms with different rates
- Course offerings at different price levels
- Bulk pricing structures
SEO Impact: Shows price ranges in search results, helping users understand pricing options before clicking. Essential for marketplaces and products with variants.
Example use case: A SaaS product with Basic ($29/mo), Professional ($99/mo), and Enterprise ($299/mo) tiers.
Event
For concerts, webinars, conferences, and any scheduled events.
Required fields:
- Event name
- Start date
Optional fields:
- End date
- Location (name and address)
- Event description
Best for: Conferences, webinars, concerts, workshops, meetups, online events
Person
For author profiles, team member bios, and individual profiles.
Required fields:
- Person name
Optional fields:
- Job title
- Organization/employer
- Bio/description
Best for: Author pages, team member profiles, speaker bios, personal websites
Local Business
For brick-and-mortar businesses with physical locations.
Required fields:
- Business name
Optional fields:
- Address (street, city, state, zip, country)
- Phone number
- Opening hours
- Business description
Best for: Restaurants, retail stores, service providers, medical offices, salons
Job Posting
For job listings and career pages. Essential for appearing in Google for Jobs.
Required fields:
- Job title
- Expiration date
- Hiring organization name (falls back to global organization)
- Location (physical address or remote designation)
Optional fields:
- Employment type (full-time, part-time, contract, etc.)
- Salary information (range or fixed amount)
- Experience requirements (months of experience)
- Education requirements
- Skills, qualifications, responsibilities
- Benefits
- Industry and occupational category
- Work hours
- Direct apply toggle
Best for: Job boards, careers pages, recruitment sites, staffing agencies
SEO impact: Jobs appear in Google for Jobs search results with rich snippets showing salary, location, and employment type.
FAQ Page
For frequently asked questions pages.
Required fields:
- At least one question-and-answer pair
How to use:
- Select "FAQ Page" as the schema type in the SEO tab.
- In the FAQ Details section, add each question and its corresponding answer.
- Each entry automatically generates structured data compliant with Googleβs FAQPage schema.
Best for: Help centers, knowledge bases, product FAQ pages
SEO impact: Enables rich FAQ snippets in Google Search results, improving click-through rates.
QA Page
For question and answer pages where a single question has one or more answers (like Stack Overflow, forums, or community Q&A).
[!TIP] The
FAQschema is valuable for SEO and AEO, but Google no longer shows rich search fragments from this structured data unless you are a governmental or recognized health site.
Required fields:
- Question title
Optional fields:
- Question details/body text
- Question author name
- Question date posted
- Question upvote count
- Accepted Answer: The answer marked as correct/most helpful
- Answer text (required if providing accepted answer)
- Answer author
- Answer date
- Answer upvote count
- Other Answers: Additional suggested answers
- Each with text, author, date, and upvote count
Best for: Community forums, support forums, Q&A platforms, discussion boards, knowledge bases with user-contributed answers
Difference from FAQ:
- FAQPage is for curated, official FAQs written by your organization
- QAPage is for community-driven Q&A with voting, multiple answers, and user attribution
SEO Impact: Can appear in Google's Q&A rich results with voting counts, accepted answers highlighted, and author information. Helps establish expertise and community engagement.
Example use cases:
- Technical support forum: "How do I reset my password?" with 5 community answers
- Programming Q&A: "What's the difference between var and let in JavaScript?" with accepted answer
- Product support: Customer questions with manufacturer responses
Best practices:
- Always provide the accepted answer when one exists
- Include upvote counts to signal answer quality
- Add author names for credibility
- Use for pages with single questions only (not question listings)
Note: For question listing/index pages, use the CollectionPage schema instead.
Video Object
For video content pages, including educational videos and tutorials.
Required fields:
- Video name
Optional fields:
- Video description
- Upload date
- Duration (ISO 8601 format: "PT1M30S" for 1 minute 30 seconds)
- Thumbnail image (uses
_featuredImagerelationship if not specified) - Content URL (direct video file)
- Embed URL (YouTube/Vimeo embed)
Educational video fields: When "Is Educational Video" is checked, additional fields become available:
- Educational Use: How the video is used (assignment, professional development, continuing education, vocational training)
- Learning Resource Type: Type of educational content (lecture, tutorial, demonstration, presentation, exercise)
Best for: Video landing pages, video galleries, tutorial videos, webinar recordings, online courses, training materials
SEO Impact: Educational videos can appear in Google's learning-specific search features and video carousels with enhanced metadata.
Example use cases:
- Software tutorial: "How to Use Photoshop Layers" (learningResourceType: tutorial)
- University lecture: "Introduction to Calculus" (educationalUse: assignment)
- Professional training: "Project Management Fundamentals" (educationalUse: professional development)
How To
For step-by-step guides and tutorials.
Required fields:
- Guide name
- At least one step with name and instructions
Optional fields:
- Total time (ISO 8601 format: "PT30M" for 30 minutes)
- Supply list (materials needed)
- Tool list (tools required)
- Step images
- Guide description
Best for: DIY tutorials, cooking instructions, repair guides, software walkthroughs
Example: "How to Change a Tire," "How to Bake Sourdough Bread," "How to Install WordPress"
Review
For product reviews, service reviews, and editorial reviews.
Required fields:
- Item being reviewed (name)
Optional fields:
- Item type (Product, Book, Movie, Restaurant, Service)
- Rating (1-5 scale)
- Review body/text
- Author name
- Review date
Best for: Product review pages, service reviews, book reviews, restaurant reviews
Recipe
For cooking recipes and food content.
Required fields:
- Recipe name
- Ingredients list
- Cooking instructions
Optional fields:
- Author
- Prep time, cook time, total time (ISO 8601 format)
- Yield (servings)
- Recipe category (e.g., "Dessert," "Main Course")
- Cuisine type (e.g., "Italian," "Mexican")
- Nutrition information (calories, carbs, protein, fat)
- Aggregate rating and review count
- Recipe image
Best for: Food blogs, cooking websites, restaurant recipe pages
Time format examples:
- "PT30M" = 30 minutes
- "PT1H" = 1 hour
- "PT1H30M" = 1 hour 30 minutes
Course
For online courses and training programs.
Required fields:
- Course name
- Course description
Optional fields:
- Course provider (defaults to site organization)
- Course code (e.g., "CS101")
- Educational level (Beginner, Intermediate, Advanced)
- Price and currency
- Availability
- Aggregate rating and review count
Best for: Online learning platforms, training programs, educational institutions, certification courses
Best Practices
Test your markup: Use Google's Rich Results Test to validate your structured data
For product catalogs:
- Use Product schema on individual product detail pages
- Use Offer or AggregateOffer as the offer type within Product schema when appropriate
- Use CollectionPage with ItemList on category/listing pages
For service businesses:
- Use Offer schema for individual service packages
- Use AggregateOffer for tiered service offerings
- Combine with LocalBusiness for location-based services
For marketplaces:
- Use AggregateOffer to show price ranges across sellers
- Include individual offers array for each seller/variant
- Ensure seller information is populated for trust signals
Pricing and Offers Tips:
- Always include availability status for accurate search results
- Use priceValidUntil for time-limited offers
- Include shipping information for physical products
- Seller information automatically falls back to your global Organization settings
AI & Search Strategy
Modern search and AI systems use different types of crawlers for different purposes. Understanding these differences helps you make informed decisions about your content's visibility and protection.
Understanding Crawler Types
Training Crawlers (GPTBot, ClaudeBot, Google-Extended, CCBot):
- Build AI training datasets from your content
- Used to improve AI models
- Your content may be synthesized into AI responses without attribution
Browsing Crawlers (ChatGPT-User, Claude-User, PerplexityBot):
- Serve real-time user queries
- Typically provide attribution and links back to your site
- Drive referral traffic
Traditional Search (Googlebot, Bingbot):
- Power traditional search engines
- Include AI-enhanced features (Google AI Overview, Bing Chat)
- Essential for search rankings and organic traffic
Recommended Configuration for Most Sites
For optimal search visibility while protecting intellectual property:
robots.txt Settings:
- Mode: "Allow Search, Block AI Training"
llms.txt Settings:
- Mode: "Disallow AI Training"
Why This Works:
- β Traditional search engines continue normal indexing
- β AI Overview and AI-powered search features remain functional
- β Real-time AI browsing for user queries still works
- β Your content drives referral traffic from AI systems
- β Your content is protected from AI training datasets
- β No contribution to training commercial AI models
Impact on Rankings:
- No negative impact on Google Search rankings (confirmed by Google)
- Blocking Google-Extended does not affect Google Search
- AI training crawler access is completely separate from search indexing
For Maximum AI Visibility
If you want your content widely used by AI systems for training and responses:
robots.txt Settings:
- Mode: "Allow All (Search + AI)"
llms.txt Settings:
- Mode: "Allow AI Crawling"
Use this when:
- You want maximum exposure in AI-generated content
- Your business model benefits from AI-driven traffic
- You're comfortable with your content training AI models
- You want to contribute to open AI datasets
For Maximum Privacy/Protection
If you want to restrict most or all AI access:
robots.txt Settings:
- Mode: "Selective AI Crawlers"
- Check only: ChatGPT-User, Claude-User (optional - allows real-time queries)
- Or use "Block All" for complete restriction
llms.txt Settings:
- Mode: "Disabled"
Use this when:
- You have proprietary or competitive content
- Legal/compliance restrictions on AI training
- You want maximum control over content usage
- Privacy is a primary concern
Understanding robots.txt vs llms.txt
Both tools work together but serve different purposes:
| Feature | robots.txt | llms.txt |
|---|---|---|
| Purpose | Enforceable crawler access control | Policy communication & transparency |
| Technical | Bots must respect (standard protocol) | Informational guidelines only |
| Adoption | Universal web standard since 1994 | Proposed standard, limited adoption |
| Enforcement | Technical blocking mechanism | Voluntary compliance only |
| Controls | Which bots can crawl your site | How content may be used if crawled |
| Best for | Technical access restrictions | Terms of use & AI transparency |
| Required? | Yes (web standard since 1994) | Optional (emerging standard) |
| AI Support | Most AI crawlers respect robots.txt | Most AI systems do not read llms.txt |
| Example | "Block GPTBot from accessing /api/*" | "Content may be used for search, not training" |
β οΈ Important: While
llms.txtrepresents forward-thinking SEO strategy, it should not be relied upon for actual crawler control. Userobots.txtfor enforceable policies. Thellms.txtfile serves as a policy statement and may gain broader adoption over time.
Recommended approach: Use both together:
- robots.txt provides technical enforcement (works now)
- llms.txt clearly communicates your policies (may work in the future)
Site Search Query Parameter
Configure the query parameter your site uses for internal search. This enables the SearchAction structured data in your site's WebSite schema.
Configuration: Set this in your global SEO settings. Common values:
q(most common) - for URLs like/search?q=querysearch- for URLs like/search?search=queryquery- for URLs like/search?query=querys(WordPress default) - for URLs like/?s=query
Example in global settings:
"seoSearchQueryParam": "q"
SEO Impact: This creates a SearchAction schema that:
- Helps search engines understand your site search
- May enable a "Search this site" box in Google results
- Improves your site's appearance as an authoritative source
Note: This should match whatever parameter your actual search functionality uses. Check your site's search URL to determine the correct value.
Advanced Configuration
Disabling SEO Fields
Disable SEO fields for specific page or piece types:
// modules/my-piece-type/index.js
export default {
extend: '@apostrophecms/piece-type',
options: {
seoFields: false
}
};
The following modules disable SEO fields by default:
@apostrophecms/global@apostrophecms/user@apostrophecms/image@apostrophecms/image-tag@apostrophecms/file@apostrophecms/file-tag
Setting Default Schema Types
You can configure a default schema type for any piece or page type using the seoSchemaType option. When set, this type will be pre-selected and locked for all content of that type.
Example configuration:
// modules/product/index.js
export default {
extend: '@apostrophecms/piece-type',
options: {
label: 'Product',
seoSchemaType: 'Product' // Always use Product schema
}
};
Pre-configured defaults:
The following ApostropheCMS extensions automatically set appropriate schema types:
@apostrophecms/blog: Defaults toArticleschema for blog posts@apostrophecms/event: Defaults toEventschema for events- The pages for each of these modules defaults the
CollectionPageschema
When a default is configured, the schema type selector becomes read-only in the editor UI, ensuring consistency across all content of that type. These can be overridden at project level.
When to use defaults:
- Content types with a clear, single schema purpose (products, events, recipes)
- Ensuring editors can't accidentally select the wrong schema type
- Maintaining consistency across large content collections
- Integration with specific Schema.org requirements (e.g., job boards must use JobPosting)
Canonical Link Configuration
Configure canonical URL options for pieces by specifying which document types editors can reference:
// modules/article/index.js
export default {
extend: '@apostrophecms/piece-type',
options: {
label: 'Article',
seoCanonicalTypes: [ '@apostrophecms/page', 'topic' ]
}
};
This allows editors to designate another page or piece as the canonical source for search engines, helping prevent duplicate content penalties.
What are canonical links? As described on Moz.com: "A canonical tag tells search engines which version of a URL you want to appear in search results." This prevents problems when identical content appears on multiple URLs.
Pagination Support
The module automatically adds rel="prev" and rel="next" link tags for paginated content. No manual configuration required.
Automatic detection works for:
Index pages (piece-page-type listing pages):
- Uses ApostropheCMS's built-in
req.data.currentPageandreq.data.totalPages - Automatically detects pagination from standard piece-page-type queries
- Page 1 gets clean URLs (no
?page=1query string)
- Uses ApostropheCMS's built-in
Show pages (individual pieces with navigation):
- Uses
req.data.nextandreq.data.previouswhen configured - Works when you enable
next: trueandprevious: trueoptions on your piece-page-type
- Uses
Example piece-page-type with next/previous:
// modules/article-page/index.js
export default {
extend: '@apostrophecms/piece-page-type',
options: {
// Enable automatic next/previous navigation
next: true,
previous: true
}
};
Manual override (backwards compatibility):
If you need custom pagination logic, you can still manually set req.data.pagination:
// In your route handler (only if you need custom behavior)
module.exports = {
async index(req) {
req.data.pagination = {
currentPage: customPage,
totalPages: customTotal,
baseUrl: customBaseUrl
};
return {};
}
};
This helps search engines understand pagination relationships and prevents duplicate content issues.
Custom 404 Tracking
Track 404 errors in Google Analytics by adding this to your notFound.html template:
{% block extraBody %}
{{ super() }}
{% include "@apostrophecms/seo:404.html" %}
{% endblock %}
This automatically sends 404 events when a tracking ID is configured, helping you identify broken links.
Paywalled Content
If you mark content as paywalled, your templates must use consistent CSS classes or IDs to wrap premium content. The module needs to know which HTML element contains the paywalled content.
How it works:
- Add a wrapper element around your paywalled content in your template
- Configure the CSS selector in the SEO settings to match your wrapper
Example template implementation:
{# views/show.html #}
<article>
<h1>{{ data.piece.title }}</h1>
{# Free preview content #}
<div class="article-preview">
{{ data.piece.excerpt }}
</div>
{# Paywalled content - note the class name #}
<div class="paywall">
{% if data.user %}
{# Show full content to subscribers #}
{{ data.piece.body }}
{% else %}
{# Show paywall message to non-subscribers #}
<div class="paywall-notice">
<p>Subscribe to read more...</p>
</div>
{% endif %}
</div>
</article>
Common CSS selector patterns:
/* By class (most common) */
.paywall
.premium-content
.members-only
/* By ID */
#paywalled-content
/* By data attribute */
[data-paywall="true"]
/* Multiple classes */
.article-body.premium
In the SEO settings, set the "Paywall CSS Selector" field to match your implementation (e.g., .paywall).
Why this matters: Google requires you to explicitly mark which parts of your page require payment. The CSS selector tells search engines exactly where the paywall boundary is, helping them show appropriate content previews without penalties.
Custom Field Mappings
If your project uses different field names than the SEO module's defaults, you can configure custom field mappings to avoid refactoring existing content types:
// app.js
modules: {
'@apostrophecms/seo': {
options: {
fieldMappings: {
author: 'authorName', // Use authorName instead of author/_author
image: 'heroImage', // Use heroImage instead of _featuredImage
description: 'summary', // Use summary instead of description/excerpt
publishedAt: 'publicationDate' // Use publicationDate instead of publishedAt
}
}
}
}
The module checks your custom field names first, then falls back to standard field names if the custom field is empty. This works with all field formats (strings, relationships, objects, and attachments), making it ideal for migrating from another CMS or maintaining project-specific naming conventions. Enable debug mode (APOS_SEO_DEBUG=1) to see which fields are being used.
Note: Field mappings are global and apply to all content types. If different content types use different field names (e.g., articles use
authorNamebut products usecreatedBy), use theregisterSchema()method to create custom schema generators for those specific types instead.
Implementation Guidelines for Developers
When using this SEO module, you have flexibility in how you structure your fields. The module supports multiple field formats through an intelligent fallback system. This section documents both the simple and advanced approaches you can take.
Field Flexibility
The SEO module provides flexible field formats to accommodate different project needs. Whether you're building a simple blog or a complex application, you can choose the field structure that works best for your use case.
Flexible Field Formats
Author Information
Provide author information in any of these formats:
Simple string field (easiest):
fields: {
add: {
author: {
type: 'string',
label: 'Author Name',
def: 'Editorial Team'
}
}
}
Author relationship (full featured):
fields: {
add: {
_author: {
type: 'relationship',
withType: 'author',
max: 1
}
}
}
Automatic fallback: When neither is provided, the module falls back to the user that last updated (if available).
Images
Images can be provided as:
Simple object (for external images):
fields: {
add: {
featuredImage: {
type: 'object',
fields: {
add: {
url: { type: 'url', required: true },
alt: { type: 'string' },
width: { type: 'integer' },
height: { type: 'integer' }
}
}
}
}
}
ApostropheCMS image relationship (for uploaded images):
fields: {
add: {
_featuredImage: {
type: 'relationship',
withType: '@apostrophecms/image',
max: 1
}
}
}
The module checks multiple field names: _image, _featuredImage, attachment, featuredImage, and image
Descriptions
Descriptions are automatically sourced from the first available field:
- Schema-specific description (e.g.,
product.description) seoDescription(SEO-optimized content)excerpt(content preview)description(general description)
This means you don't need to duplicate content across multiple fields.
Publication Dates
The module accepts multiple date field names:
publishedAt(standard ApostropheCMS field)publicationDatedatePublished- Automatically falls back to
createdAtif none are provided
fields: {
add: {
publicationDate: {
type: 'date',
label: 'Publication Date'
}
}
}
Debug Mode
Enable debug mode to see which fallback fields are being used:
export APOS_SEO_DEBUG=1
npm run dev
You'll see helpful log messages like:
[SEO] Author fallback used: document.author = "John Doe"
[SEO] Image fallback used: document.featuredImage
[SEO] Description fallback used: document.excerpt
Featured Images
Several schema types rely on a _featuredImage relationship field being present on your document.
Schema types that use featured images:
- Product - Product image
- Recipe - Recipe photo
- How To - Guide illustration
- Video Object - Video thumbnail (falls back to featured image)
Example implementation:
// modules/article/index.js
export default {
extend: '@apostrophecms/piece-type',
options: {
label: 'Article'
},
fields: {
add: {
_featuredImage: {
label: 'Featured Image',
type: 'relationship',
withType: '@apostrophecms/image',
max: 1,
required: true // Make required if using Product or Recipe schemas
},
},
group: {
basics: {
fields: ['title', '_featuredImage']
}
}
}
};
Note: The field name must be _featuredImage (with the leading underscore) for the SEO module to find it automatically.
Author Information
For Article, Recipe, and Review schemas, you can provide author information in multiple formats.
- A simple string field named
authorin the SEO schema - A simple string field named
authorin the document schema - A
relationshipfield named_authorthat points to an βauthor-likeβ piece-type (for example:author,person,staff-member, etc.). The module reads the first relationship documentβstitle(preferred), thennameto determine the author name.
Resolution order:
schema.authorstring (if present and non-empty)document.authorstring_authorrelationship: the first joined doc ondocument._authorupdatedByuser on the document:title, thenname
For full details on how author fields are resolved and mapped into structured data (including fallbacks), see Author Information.
URL Requirements
The _url property is automatically provided by ApostropheCMS for:
- All pages
- Pieces displayed through piece-page-types
No configuration required - the SEO module uses these URLs automatically for structured data.
Date Fields
Several schema types use date information. The module looks for these fields in priority order:
- Publication dates:
publishedAt, thencreatedAt - Modification dates:
updatedAt, thencreatedAt
Best practice: Add a publishedAt field to content types that use Article schema:
fields: {
add: {
publishedAt: {
label: 'Publication Date',
type: 'date',
def: null
}
}
}
Listing Pages (Item List)
For Collection Page schema with Item List generation, the module automatically detects listing items from these request data properties:
req.data.piecesreq.data.itemsreq.data._piecesreq.data.docs
Each item must have:
- A URL:
_urlorurlproperty - A title:
seoTitleortitleproperty
Standard piece-page-type index pages work automatically without additional configuration.
Summary: Key Document-Level Fields by Schema Type
Most schema types are driven primarily by their seoJsonLd* configuration fields
(e.g., seoJsonLdArticle, seoJsonLdProduct, seoJsonLdRecipe).
In addition, the SEO module can reuse certain document-level fields (outside the
seoJsonLd* objects) and, for some schema types, those fields are effectively
required for rich results.
The table below only lists document-level fields you may want to add to your
piece/page schemas. Fields that live inside the seoJsonLd* blocks (such as
review.reviewRating, course.provider, job.baseSalary, etc.) are documented
with each schema and are not repeated here.
| Schema Type | Suggested Document-level fields | Requirement level |
|---|---|---|
| Article | Author β author (string) or _author (relationship)Dates β publishedAt (or other mapped published date), updatedAt |
Recommended for rich Article results (author + published date are strongly recommended) |
| Product | Primary image β _featuredImage (relationship), or a mapped image field used by the image fallback helper |
Strongly recommended β module logs a debug warning if missing; many product rich results expect an image |
| Recipe | Primary image β _featuredImage (relationship) or mapped image field (resolved via image fallback helper)Author β author or _authorDates β publishedAt (or mapped) |
Image is required by the module (no JSON-LD is emitted without one); author + published date are recommended for rich Recipe results |
| VideoObject | Thumbnail fallback β _featuredImage relationship (used if seoJsonLdVideo._thumbnail is missing)Dates β publishedAt / createdAt (upload date fallback) |
Thumbnail and upload date are treated as required by the module; _featuredImage is a practical requirement if you donβt always set _thumbnail |
| HowTo | Top-level image β _featuredImage relationship or mapped image field (via image fallback helper) |
Optional in code but strongly recommended for rich HowTo results; step images are configured inside seoJsonLdHowTo.steps |
| Review | Author β author or _authorDate β publishedAt (or mapped) |
Recommended β author and date are important for Review rich results |
Images and rich results: The module enforces images for Recipe and VideoObject (no schema is output if none can be resolved). For Product and HowTo, images are not strictly required in code, but search engines commonly treat them as required for rich results. In practice, you should treat a
_featuredImagerelationship (or a custom-mapped image field) as required for those schema types.
Schema Types That Require No Developer Fields
The following schema types do not depend on project-level fields. They are generated entirely from the SEO UI and built-in ApostropheCMS fields (title, URL, SEO tab configuration):
- WebPage β basic page metadata
- CollectionPage β ItemList is autogenerated from
req.data.pieces/items - FAQPage β uses fields in the
seoJsonLdFAQPageUI group - QAPage β uses fields in the
seoJsonLdQAPageUI group
Debugging Structured Data
Set the environment variable APOS_SEO_DEBUG=1 to print JSON-LD generation diagnostics to your server logs during development. When enabled, any errors or malformed data encountered during schema generation will be logged to your server console along with the offending data payload.
This is particularly useful when testing new schema types or diagnosing missing fields in custom templates.
Important: Not all schema types show rich results in Google Search Console's URL Inspection Tool. The following schemas are valid and will be indexed, but may not appear in the rich results preview:
- HowTo - Valid schema, but not shown in URL Inspection Tool
- QAPage - Valid schema, but not shown in URL Inspection Tool
- Learning Video - Extension of VideoObject, shown as standard Video
Use the Rich Results Test and Schema Markup Validator for comprehensive testing of all schema types.
Extending the SEO Module with Custom JSON-LD Schemas
You can add custom Schema.org types to the SEO module without modifying this package. At a high level, you will:
- Register a new JSON-LD schema type on
@apostrophecms/seo. - Expose that type in the schema type dropdown via
@apostrophecms/seo-fields-doc-type. - Add the fields that power your schema on
@apostrophecms/doc-type.
Apostrophe automatically merges project-level modules with the same name as core/pro modules, so you only need the right folder structure.
1. Register a Custom Schema on @apostrophecms/seo
Create a project-level module to register your custom Schema.org type. In this example, weβll add a Book schema.
// modules/@apostrophecms/seo/index.js
export default {
init(self) {
self.registerSchema('Book', (data) => {
const { piece, page } = data;
const doc = piece || page;
const book = doc?.seoJsonLdBook;
// Require a document and a title to emit the schema
if (!doc || !book || !book.title?.trim()) {
return null;
}
const schema = {
'@type': 'Book',
name: book.title
};
// URL / @id
if (doc._url) {
schema['@id'] = doc._url;
schema.url = doc._url;
}
// Author
if (book.author?.trim()) {
schema.author = {
'@type': 'Person',
name: book.author
};
}
// ISBN
if (book.isbn?.trim()) {
schema.isbn = book.isbn;
}
return schema;
});
}
};
The handler receives the same data object used to render SEO tags (page, piece, global, req, etc.). Return a JSON-LD object or null to skip output.
2. Add the Type to the Schema Dropdown (@apostrophecms/seo-fields-doc-type)
Next, make the new type available in the schema type selector. Use extendMethods on the @apostrophecms/seo-fields-doc-type module.
// modules/@apostrophecms/seo-fields-doc-type/index.js
export default {
extendMethods(self) {
return {
getSchemaTypeChoices(_super) {
return function () {
const baseChoices = _super();
return [
...baseChoices,
{
label: 'Book',
value: 'Book'
}
];
};
}
};
}
};
This appends a Book option to the existing schema type choices. When editors select Book in the SEO tab, you can show Book-specific fields.
3. Add Fields on @apostrophecms/doc-type
Finally, define the fields that power the Book schema on @apostrophecms/doc-type. These fields will be available to all page and piece types.
// modules/@apostrophecms/doc-type/index.js
export default {
fields(self, options) {
return {
add: {
seoJsonLdBook: {
label: 'Book Details',
type: 'object',
help: 'Structured data fields for the Book schema.',
if: {
// Only show when the JSON-LD schema type is `Book`
seoJsonLdType: 'Book'
},
fields: {
add: {
title: {
label: 'Book Title',
type: 'string',
required: true
},
author: {
label: 'Author',
type: 'string'
},
isbn: {
label: 'ISBN',
type: 'string'
}
}
}
}
},
group: {
seo: {
// Add our field to the existing SEO group
fields: [ 'seoJsonLdBook' ]
}
}
};
}
};
With these three pieces in place:
- Editors can select Book as a JSON-LD type in the SEO tab.
- A Book-specific fieldset appears when that type is selected.
- The SEO module outputs a valid
BookJSON-LD object (includingisbn) based on those fields.
Performance Optimization
Critical Font Preloading
Preload critical fonts to improve Core Web Vitals scores and SEO performance. Font loading directly impacts:
- Cumulative Layout Shift (CLS): Prevents layout shift when custom fonts load
- Largest Contentful Paint (LCP): Faster font loading improves render time
- First Contentful Paint (FCP): Reduces render-blocking font requests
1. Place Your Fonts in a Moduleβs public/ Directory
For cloud deployments (UploadFS β S3, GCS, etc.), fonts must be stored inside a module, for example:
modules/my-fonts/public/fonts/inter-variable.woff2
modules/my-fonts/public/fonts/geist-mono.woff
Apostrophe uploads assets only from module public/ directories. These files become available at URLs like:
/modules/my-fonts/fonts/inter-variable.woff2
2. Configure the SEO Module to Preload Fonts
Configure critical fonts as a developer-level option in your app.js:
import apostrophe from 'apostrophe';
apostrophe({
root: import.meta,
shortName: 'my-project',
modules: {
'@apostrophecms/seo': {
options: {
criticalFonts: [
{
url: '/modules/my-fonts/fonts/inter-variable.woff2',
type: 'font/woff2' // Optional, defaults to 'font/woff2'
},
{
url: 'https://cdn.yoursite.com/fonts/geist-mono.woff',
type: 'font/woff'
}
]
}
}
}
});
The module automatically generates <link rel="preload"> tags for each configured font.
Important: Font preloading is complementary to your existing CSS - it doesn't replace it. You must still include your @font-face rules:
/* Your existing CSS - keep this! */
@font-face {
font-family: 'Inter';
src: url('/modules/my-fonts/fonts/inter-variable.woff2') format('woff2');
font-display: swap;
}
How it works:
- Without preload: Browser parses HTML β parses CSS β discovers font β starts download (delayed)
- With preload: Browser starts downloading font immediately β when CSS loads, font is already ready
The crossorigin attribute is automatically added for absolute URLs (CDN/external fonts) and omitted for relative URLs (self-hosted fonts).
Where to store fonts:
Simple single-server deployments: Place font files in
modules/my-fonts/public/fonts/and reference them as/modules/my-fonts/fonts/filename.woff2- No CORS configuration needed for same-origin fonts
- Works in both local and cloud/UploadFS deployments
- Example:
{ url: '/modules/my-fonts/fonts/inter.woff2' }
CDN/S3: Use full URLs with proper CORS headers configured on your CDN
- Better caching and global performance
- Requires CORS:
Access-Control-Allow-Origin: * - Example:
{ url: 'https://cdn.yoursite.com/fonts/inter.woff2' }
Self-host Google Fonts instead of using their CDN:
- Google's CDN uses hashed URLs that make preloading impossible
- Download Google Fonts and self-host them to enable preloading
- Tools: google-webfonts-helper or Google Fonts Helper
- Once self-hosted, preload them like any other font
Advanced options:
criticalFonts: [
{
url: '/fonts/local.woff2'
// No crossorigin (relative URL)
},
{
url: 'https://cdn.example.com/font.woff2'
// Automatic crossorigin="anonymous" (absolute URL)
},
{
url: 'https://cdn.example.com/font.woff2',
crossorigin: false // Explicitly disable crossorigin if needed
},
{
url: 'https://private-cdn.example.com/font.woff2',
crossorigin: 'use-credentials' // For authenticated CDN requests
}
]
Best practices:
- Only preload fonts used above the fold (typically 1-2 fonts maximum)
- Use
woff2format for best compression (supported by all modern browsers) - Ensure font files are actually available at the specified URLs before deployment
- Test with Google PageSpeed Insights to verify Core Web Vitals improvements
- Keep your existing
@font-faceCSS - preloading enhances it, doesn't replace it
Example project structure:
my-project/
βββ public/
β βββ fonts/
β βββ inter-variable.woff2
β βββ headings.woff2
βββ modules/
βββ asset/
βββ ui/
βββ src/
βββ index.scss # Reference fonts here with @font-face
Mobile Optimization
The module automatically includes:
- Viewport meta tag for responsive design
- Optional theme-color meta tag for PWA compatibility
Theme Color for Mobile Browsers
Set a theme color for mobile browsers. Supports:
- Single color mode (one color for all)
- Light/Dark mode variants
Example configuration:
{
"mode": "lightDark",
"light": "#ffffff",
"dark": "#121212"
}
Field Reference
| Name | Description | Module Affected | Module Option |
|---|---|---|---|
seoTitle |
Title attribute for search results | @apostrophecms/doc-type |
Enabled by default |
seoDescription |
Description for search results | @apostrophecms/doc-type |
Enabled by default |
seoRobots |
Robots indexing behavior | @apostrophecms/doc-type |
Enabled by default |
_seoCanonical |
Canonical URL reference | @apostrophecms/page-type |
Enabled by default |
seoGoogleTagManager |
Google Tag Manager Container ID | @apostrophecms/global |
seoGoogleTagManager: true |
seoGoogleTrackingId |
Google Analytics Measurement ID | @apostrophecms/global |
seoGoogleAnalytics: true |
seoGoogleVerificationId |
Google Site Verification ID | @apostrophecms/global |
seoGoogleVerification: true |
seoJsonLdType |
Schema.org type for this document | @apostrophecms/doc-type |
Enabled by default |
seoJsonLdProduct |
Product schema fields | @apostrophecms/doc-type |
Shown when seoJsonLdType: 'Product' |
seoJsonLdOffer |
Offer schema fields (price, availability, seller, shipping) | @apostrophecms/doc-type |
Shown when seoJsonLdType: 'Offer' |
seoJsonLdAggregateOffer |
AggregateOffer schema fields (price range, variants) | @apostrophecms/doc-type |
Shown when seoJsonLdType: 'AggregateOffer' |
seoJsonLdEvent |
Event schema fields | @apostrophecms/doc-type |
Shown when seoJsonLdType: 'Event' |
seoJsonLdPerson |
Person schema fields | @apostrophecms/doc-type |
Shown when seoJsonLdType: 'Person' |
seoJsonLdBusiness |
LocalBusiness schema fields | @apostrophecms/doc-type |
Shown when seoJsonLdType: 'LocalBusiness' |
seoJsonLdJobPosting |
JobPosting schema fields | @apostrophecms/doc-type |
Shown when seoJsonLdType: 'JobPosting' |
seoJsonLdHowTo |
How-To guide schema fields | @apostrophecms/doc-type |
Shown when seoJsonLdType: 'HowTo' |
seoJsonLdReview |
Review schema fields | @apostrophecms/doc-type |
Shown when seoJsonLdType: 'Review' |
seoJsonLdRecipe |
Recipe schema fields | @apostrophecms/doc-type |
Shown when seoJsonLdType: 'Recipe' |
seoJsonLdCourse |
Course schema fields | @apostrophecms/doc-type |
Shown when seoJsonLdType: 'Course' |
seoIncludeItemList |
Toggle ItemList in structured data | @apostrophecms/doc-type |
Shown when seoJsonLdType: 'CollectionPage' |
seoSiteName |
Site name for WebSite schema | @apostrophecms/global |
Enabled by default |
seoSiteDescription |
Site description for WebSite schema | @apostrophecms/global |
Enabled by default |
seoSiteCanonicalUrl |
Base URL for structured data | @apostrophecms/global |
Enabled by default |
seoJsonLdOrganization |
Organization schema settings | @apostrophecms/global |
Enabled by default |
π Ready for AI-Powered SEO?
Want to supercharge your SEO workflow? Create an account on Apostrophe Workspaces and upgrade to ApostropheCMS Pro. Get access to the SEO Assistant with AI-powered content optimization:
β¨ SEO Assistant Pro Features
- π€ AI-Generated Meta Titles: Compelling, keyword-optimized titles generated automatically
- π Smart Meta Descriptions: AI-crafted descriptions that drive clicks
- π― Content Analysis: Get suggestions based on your actual page content
- β‘ One-Click Optimization: Generate, review, and apply SEO improvements instantly
- π Multiple Suggestions: Try different approaches with regeneration options
- βοΈ Custom Prompts: Fine-tune AI behavior for your brand voice
The SEO Assistant analyzes your page content and generates optimized meta titles and descriptions using advanced AI, making professional SEO accessible to content creators of all skill levels.
Contact us to learn more about ApostropheCMS Pro.
π’ Managing Multiple Sites?
Running multiple websites with shared content and branding? Consider ApostropheCMS Assembly for enterprise multisite management:
β¨ Assembly Multisite Features
- ποΈ Centralized Management: Control multiple sites from a single dashboard
- π Shared Codebase: Deploy updates across all sites simultaneously
- π Multi-Domain Support: Each site gets its own domain with automatic SSL
- βοΈ Automatic SEO Configuration: Base URLs and canonical links configured automatically
- π¨ Per-Site Customization: Individual themes, content, and settings per site
- π Unified Analytics: Track performance across your entire site network
Perfect for agencies, franchises, or organizations managing multiple branded websites.
Learn more about Assembly or contact our team.
Roadmap
| Feature | Status |
|---|---|
| SEO Meta fields for pages and pieces | β Implemented |
| Google Analytics & Tag Manager integration | β Implemented |
| Automated robots.txt generation | β Implemented |
| Structured data (JSON-LD) for all major schema types | β Implemented |
| Offer and AggregateOffer schemas | β Implemented |
| SEO Assistant (AI-powered) | π Available in Pro |
| SEO Page Scanner | π§ Under development |
Made with β€οΈ by the ApostropheCMS team. Found this useful? Give us a star on GitHub! β