{"id":18561197,"url":"https://github.com/apostrophecms/seo","last_synced_at":"2025-04-10T02:31:29.740Z","repository":{"id":45316158,"uuid":"374726629","full_name":"apostrophecms/seo","owner":"apostrophecms","description":"Add and manage SEO meta fields to all documents in ApostropheCMS.","archived":false,"fork":false,"pushed_at":"2025-04-02T08:57:23.000Z","size":117,"stargazers_count":4,"open_issues_count":2,"forks_count":7,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-02T09:38:58.948Z","etag":null,"topics":["apostrophe3","hacktoberfest"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/apostrophecms.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-06-07T16:08:18.000Z","updated_at":"2024-10-31T13:32:08.000Z","dependencies_parsed_at":"2024-06-19T22:50:17.425Z","dependency_job_id":"4e01bb7a-f6f8-44f2-a23a-36e01f578dee","html_url":"https://github.com/apostrophecms/seo","commit_stats":{"total_commits":50,"total_committers":10,"mean_commits":5.0,"dds":0.5800000000000001,"last_synced_commit":"176b72da544384103a436d1ce1e6dd2eec8c5357"},"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/apostrophecms%2Fseo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/apostrophecms%2Fseo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/apostrophecms%2Fseo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/apostrophecms%2Fseo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/apostrophecms","download_url":"https://codeload.github.com/apostrophecms/seo/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248144209,"owners_count":21054886,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["apostrophe3","hacktoberfest"],"created_at":"2024-11-06T22:06:09.649Z","updated_at":"2025-04-10T02:31:29.364Z","avatar_url":"https://github.com/apostrophecms.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/apostrophecms/apostrophe/main/logo.svg\" alt=\"ApostropheCMS logo\" width=\"80\" height=\"80\"\u003e\n\n  \u003ch1\u003eSEO tools for ApostropheCMS\u003c/h1\u003e\n  \u003cp\u003e\n    \u003ca aria-label=\"Apostrophe logo\" href=\"https://docs.apostrophecms.org\"\u003e\n      \u003cimg src=\"https://img.shields.io/badge/MADE%20FOR%20ApostropheCMS-000000.svg?style=for-the-badge\u0026logo=Apostrophe\u0026labelColor=6516dd\"\u003e\n    \u003c/a\u003e\n    \u003ca aria-label=\"Join the community on Discord\" href=\"http://chat.apostrophecms.org\"\u003e\n      \u003cimg alt=\"\" src=\"https://img.shields.io/discord/517772094482677790?color=5865f2\u0026label=Join%20the%20Discord\u0026logo=discord\u0026logoColor=fff\u0026labelColor=000\u0026style=for-the-badge\u0026logoWidth=20\"\u003e\n    \u003c/a\u003e\n    \u003ca aria-label=\"License\" href=\"https://github.com/apostrophecms/blog/blob/main/LICENSE.md\"\u003e\n      \u003cimg alt=\"\" src=\"https://img.shields.io/static/v1?style=for-the-badge\u0026labelColor=000000\u0026label=License\u0026message=MIT\u0026color=3DA639\"\u003e\n    \u003c/a\u003e\n  \u003c/p\u003e\n\u003c/div\u003e\n\nAdd useful meta fields to all pages and pieces.\n\n## Roadmap\n|Feature |Status  |\n--- | ---\n|SEO Meta fields for pages and pieces| ✅ Implemented\n|SEO Page Scanner| 🚧 Under development\n\n## Installation\n\n```bash\nnpm install @apostrophecms/seo\n```\n\n## Use\n\n### 1. Initialization\nConfigure `@apostrophecms/seo` in `app.js`.\n\n```js\nrequire('apostrophe')({\n  shortName: 'MYPROJECT',\n  modules: {\n    '@apostrophecms/seo': {}\n  }\n});\n```\n\n#### Setting the `baseUrl`\n\nIt is important to set the `baseUrl` option on your ApostropheCMS application for various reasons. In the SEO module, it contributes to building the correct `canonical` link tag URL, so in production search engines and web crawlers will register the correct link. The `baseUrl` can be set multiple ways:\n\n**With the `APOS_BASE_URL` environment variable**\n\nHow you set the variable will depend on your hosting setup.\n\n**As part of an environment configuration in `data/local.js`**\n\nThis method is if you are using stagecoach or a similar system for deployment.\n```js\n  module.exports = {\n    baseUrl: 'https://mysite.com',\n    modules: {\n      // other module env configuration\n    }\n  };\n```\n\n**Via the multisite module if using [Apostrophe Assembly](https://apostrophecms.com/extensions/multisite-apostrophe-assembly)**\n\nSee the multisite documentation for details.\n\n### 2. Module configuration\n\n#### SEO fields for pages\n\nSEO fields are enabled automatically for any page-type module. The following modules disable SEO field enhancements by default by setting the `seoFields` option to `false`:\n - `@apostrophecms/global`\n - `@apostrophecms/user`\n - `@apostrophecms/image`\n - `@apostrophecms/image-tag`\n - `@apostrophecms/file`\n - `@apostrophecms/file-tag`\n\n```js\nmodule.exports = {\n  extend: '@apostrophecms/page-type'\n  options: {\n    label: 'Personnel',\n    seoFields: false\n  }\n};\n```\n\nThe `@apostrophecms/seo` module adds a new tab labeled `SEO` to the document editor. This tab contains fields for setting the title, description, robots tag, and canonical link meta data to the head section of the page.\n\n#### SEO fields for pieces\n\nSEO fields for pieces are automatically enabled unless you **disable** them by setting the `seoFields: false` optino for that piece type.\n\nUnless disabled, a new SEO tab will be added with fields for title, description, and robots tag meta fields.\n\n```js\nmodule.exports = {\n  extend: '@apostrophecms/piece-type'\n  options: {\n    label: 'Article',\n    // Turn SEO fields *off* (the default is to turn them on)\n    seoFields: false\n  }\n};\n```\n\n#### Canonical links\n\n\"Canonical links\" are useful when a piece or page should **not** be considered the official version of a document,\nand you would prefer that search engines look elsewhere. This feature is always available for pages.\n\nIf you wish to have this feature for a piece type, you will need to specify the\n`seoCanonicalTypes` option to that piece type module, as an array of types that the editor\ncan choose from. For example:\n\n```js\nmodule.exports = {\n  extend: '@apostrophecms/piece-type'\n  options: {\n    label: 'Article',\n    // allow the editor to select a published page or a `topic` piece as the\n    // \"canonical\" version of this article\n    seoCanonicalTypes: [ '@apostrophecms/page', 'topic' ]\n  }\n};\n```\n\nThis adds additional fields in the SEO tab for choosing a canonical document for search\nengines to consider instead.\n\n\u003e Note that you cannot link to specific page-types, only all pages through `@apostrophecms/page`, but you can link to specific piece-types.\n\n#### Add Google Analytics (GA)\n\nSetting `seoGoogleAnalytics: true` in `@apostrophecms/global` will add a Google Analytics tracking ID field to your Global configuration:\n```js\nrequire('apostrophe')({\n  shortName: 'MYPROJECT',\n  modules: {\n    '@apostrophecms/seo': {},\n    '@apostrophecms/global': {\n      options: {\n        seoGoogleAnalytics: true\n      }\n    }\n  }\n});\n```\n#### Add Google Tag Manager (GTM)\n\nSetting `seoGoogleTagManager: true` in `@apostrophecms/global` will add a Google Tag Manager ID field to your Global configuration:\n\n```js\nrequire('apostrophe')({\n  shortName: 'MYPROJECT',\n  modules: {\n    '@apostrophecms/seo': {},\n    '@apostrophecms/global': {\n      options: {\n        seoGoogleTagManager: true\n      }\n    }\n  }\n});\n```\n#### Add Google Site Verification\n\nSetting `seoGoogleVerification: true` in `@apostrophecms/global` will add a Google Site Verification ID field to your Global configuration:\n\n```js\nrequire('apostrophe')({\n  shortName: 'MYPROJECT',\n  modules: {\n    '@apostrophecms/seo': {},\n    '@apostrophecms/global': {\n      options: {\n        seoGoogleVerification: true\n      }\n    }\n  }\n});\n```\n\n#### Add `robots.txt` to Your Site\nBy default, the SEO extension will add a route to your site for `/robots.txt` that will return a string that allows for all search engines to index your site.\n\n```\nUser-agent: *\\nDisallow:\n```\nWithin the global configuration you can choose to change this to disallow search engine indexing:\n\n```\nUser-agent: *\\nDisallow: /\n```\n\nYou can also select to add a custom string for your `robots.txt`. This can allow you finer control over what sections of your site can be indexed and by which bots.\n\nNote that if you allow search engines to index your site, you can still set `noindex` and/or `nofollow` on a per-page basis from the SEO tab of the individual page editing modals. If you disallow indexing of your site, settings for individual pages will be ignored.\n\nA physical `robots.txt` file in `public/robots.txt`, or `sites/public/robots.txt` in an Assembly project, will override any settings made in this module. If you don't want a one-size-fits all policy for all sites, don't use a physical file.\n\n### Notes\n\n#### Canonical URls\n\n**The canonical link field** on a page or piece allows an editor to select another page that search engines should understand to be the primary version of that page or piece. [As described on Moz.com](https://moz.com/learn/seo/canonicalization):\n\n\u003e A canonical tag (aka \"rel canonical\") is a way of telling search engines that a specific URL represents the primary copy of a page. Using the canonical tag prevents problems caused by identical or \"duplicate\" content appearing on multiple URLs. Practically speaking, the canonical tag tells search engines which version of a URL you want to appear in search results.\n\nFor pages, this link can be to any published page. For pieces, this can be either a published page or another piece-page-type show page.\n\n#### Custom Google Analytics Event on 404\nOptionally add the following include to your `notFound.html` view. If the app has a Google Tracking ID value entered, this will send an event to Google Analytics tracking the 404 response, the URL on which it happened, and, if applicable, the page on which the bad URL was triggered (helping you identify where bad links are located).\n\n```nunjucks\n{% block extraBody %}\n  {{ super() }}\n  {% include \"@apostrophecms/seo:404.html\" %}\n{% endblock %}\n```\n\nIf you already have an `extraBody` block in the `notFound.html` view file, you'll only need to add the `{% include \"@apostrophecms/seo:404.html\" %}` statement somewhere in that.\n```nunjucks\n{% block extraBody %}\n  {# ...Other templating... #}\n  {% include \"@apostrophecms/seo:404.html\" %}\n{% endblock %}\n```\n#### Field Reference\nThe following are the fields that can be added to pieces, pages, and the global doc, as well as what module option enables them.\n\n|Name |Description  | Module Effected | Module Option |\n--- | --- | --- | ---\n|`seoTitle`|Title attribute, populates `\u003cmeta name=\"title\" /\u003e` tag|`@apostrophecms/doc-type`|_Enabled by default_|\n|`seoDescription`|Description attribute, populates `\u003cmeta name=\"description\" /\u003e` tag|`@apostrophecms/doc-type`|_Enabled by default_|\n|`seoRobots`|Robots attribute, populates `\u003cmeta name=\"robots\" /\u003e` tag|`@apostrophecms/doc-type`|_Enabled by default_|\n|`_seoCanonical`|[Canonical URL](https://moz.com/learn/seo/canonicalization), populates `\u003clink rel=\"canonical\" /\u003e` tag|`@apostrophecms/page-type`|_Enabled by default_|\n|`seoGoogleTagManager`|Google Tag Manager Container ID|`@apostrophecms/global`|`seoGoogleTagManager: true`|\n|`seoGoogleTrackingId`|Google Analytics ID|`@apostrophecms/global`|`seoGoogleAnalytics: true`|\n|`seoGoogleVerificationId`|Google Verification ID, populates `\u003cmeta name=\"google-site-verification\" /\u003e`|`@apostrophecms/global`|`seoGoogleVerification: true`|\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fapostrophecms%2Fseo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fapostrophecms%2Fseo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fapostrophecms%2Fseo/lists"}