{"_id":"@apostrophecms/redirect-stable","name":"@apostrophecms/redirect-stable","dist-tags":{"latest":"1.6.0"},"versions":{"1.6.0":{"name":"@apostrophecms/redirect-stable","version":"1.6.0","description":"Manage redirects for apostropheCMS","main":"index.js","scripts":{"eslint":"eslint .","lint":"npm run eslint","mocha":"mocha","test":"npm run lint && npm run mocha"},"repository":{"type":"git","url":"git+https://github.com/apostrophecms/apostrophe.git","directory":"packages/redirect"},"homepage":"https://github.com/apostrophecms/apostrophe/tree/main/packages/redirect#readme","author":{"name":"Apostrophe Technologies"},"license":"MIT","devDependencies":{"apostrophe":"workspace:^","eslint":"^9.39.1","eslint-config-apostrophe":"workspace:^","mocha":"^11.7.5"},"gitHead":"b3e29f004f514041e1e389293ae67017c60d006e","_id":"@apostrophecms/redirect-stable@1.6.0","bugs":{"url":"https://github.com/apostrophecms/apostrophe/issues"},"_nodeVersion":"24.10.0","_npmVersion":"11.6.1","dist":{"integrity":"sha512-dilODfHZiP+nWbk/TyTDG5tX6nUR2es7ch9vHymHL4B50Flx9eBO3SG08LsR3wnfmf9Bsu6z0tf+G5FJjspZOQ==","shasum":"35d81fb68c793d7d6673ffccfa3905391d30669b","tarball":"https://registry.npmjs.org/@apostrophecms/redirect-stable/-/redirect-stable-1.6.0.tgz","fileCount":17,"unpackedSize":51107,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDW2odKx/6N1VOAkZnfzXqUB692BJQ9MuQqbrq5hx/MOgIgUYc5xQf45jBiOjn6GoSX0TclNFPzPOpLkXn+B9bzgo0="}]},"_npmUser":{"name":"boutell","email":"tom@apostrophecms.com"},"directories":{},"maintainers":[{"name":"alexgilbert","email":"alex@apostrophecms.com"},{"name":"boutell","email":"tom@apostrophecms.com"},{"name":"romanek","email":"stuart+npm@apostrophecms.com"},{"name":"bodonkey","email":"robert.means1969+apostrophecms@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/redirect-stable_1.6.0_1781112818187_0.32374908660932755"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-10T17:33:37.951Z","1.6.0":"2026-06-10T17:33:38.327Z","modified":"2026-06-10T17:33:38.667Z"},"maintainers":[{"name":"alexgilbert","email":"alex@apostrophecms.com"},{"name":"boutell","email":"tom@apostrophecms.com"},{"name":"romanek","email":"stuart+npm@apostrophecms.com"},{"name":"bodonkey","email":"robert.means1969+apostrophecms@gmail.com"}],"description":"Manage redirects for apostropheCMS","homepage":"https://github.com/apostrophecms/apostrophe/tree/main/packages/redirect#readme","repository":{"type":"git","url":"git+https://github.com/apostrophecms/apostrophe.git","directory":"packages/redirect"},"author":{"name":"Apostrophe Technologies"},"bugs":{"url":"https://github.com/apostrophecms/apostrophe/issues"},"license":"MIT","readme":"<div align=\"center\">\n  <img src=\"https://raw.githubusercontent.com/apostrophecms/apostrophe/main/logo.svg\" alt=\"ApostropheCMS logo\" width=\"80\" height=\"80\">\n\n  <h1>Manage site redirects for ApostropheCMS</h1>\n  <p>\n    <a aria-label=\"Apostrophe logo\" href=\"https://docs.apostrophecms.org\">\n      <img src=\"https://img.shields.io/badge/MADE%20FOR%20ApostropheCMS-000000.svg?style=for-the-badge&logo=Apostrophe&labelColor=6516dd\">\n    </a>\n    <a aria-label=\"Join the community on Discord\" href=\"http://chat.apostrophecms.org\">\n      <img alt=\"\" src=\"https://img.shields.io/discord/517772094482677790?color=5865f2&label=Join%20the%20Discord&logo=discord&logoColor=fff&labelColor=000&style=for-the-badge&logoWidth=20\">\n    </a>\n    <a aria-label=\"License\" href=\"https://github.com/apostrophecms/blog/blob/main/LICENSE.md\">\n      <img alt=\"\" src=\"https://img.shields.io/static/v1?style=for-the-badge&labelColor=000000&label=License&message=MIT&color=3DA639\">\n    </a>\n  </p>\n</div>\n\n## Installation\n\nFirst make sure you have an [Apostrophe project](https://apostrophecms.com)!\n\nThen:\n\n```javascript\nnpm install @apostrophecms/redirect\n```\n\n## Configuration\n\nIn `app.js`, add the module to your configuration:\n\n  ```js\n  require('apostrophe')({\n    shortName: 'MYPROJECT',\n    modules: {\n      '@apostrophecms/redirect': {}\n    }\n  });\n  ```\n### `statusCode`\n*Defaults to `302`*\nBy passing `statusCode` to your configuration you can change the default status code value.\nAccepted values are `301` and `302`\n\n```javascript\n// Other modules, then...\n'@apostrophecms/redirect': {\n  options: {\n    statusCode: 301\n  }\n}\n```\n> Note that permanent redirects are cached by Google for a long time. It is a good idea to encourage users to test with a temporary redirect first, then switch to permanent which is an SEO best practice — as long as it's correct.\n\n### `withType`\n*Defaults to `@apostrophecms/page`*\nBy passing `withType` to your configuration you can specify the document type your internal redirects can redirect to.\n\n```javascript\n// Other modules, then...\n'@apostrophecms/redirect': {\n  options: {\n    withType: 'article'\n  }\n}\n```\n\n**Note:** Apostrophe 2 supported creating relationships to multiple doc types from a single interface. This feature doesn't yet exist in the newer versions of Apostrophe, as such redirects can only specify a single doc type to redirect to.\n\n### `skip`\n\nFor performance the redirect check can be skipped for URLs matching certain regular expressions.\nThe default configuration of the `skip` option is:\n\n```javascript\n// Other modules, then...\n'@apostrophecms/redirect': {\n  options: {\n    skip: [ /\\/api\\/v1\\/.*/ ]\n  }\n}\n```\n\nIf you wish to skip other patterns, we recommend keeping the default one as it speeds up API calls.\n\n## Usage\n\nWhile logged in as an admin, click the \"Redirects\" button. A list of redirects appears, initially empty. Add as many redirects as you like. The \"from\" URL must begin with a `/`. The \"to\" URL may be anything and need not be on your site. The \"description\" field is for your own convenience.\n\n### Matching the query string\n\nBy default a redirect includes any query string (the `?` and whatever follows it, up to but not including any `#`) on incoming requests when matching for redirection. \n\nYou can toggle the \"ignore query string when matching\" option in a redirect definition to ignore query strings on incoming requests and only match on the base URL path. A redirect that does not use this option will always match first, so you can match various specific query strings and then have a fallback rule for other cases.\n\n### Matching wildcards\n\nNormally, aside from the query string, only an exact match is honored. If you wish to redirect an entire subdirectory, you may use a `*` immediately after a `/`, for example:\n\n```\n/auto/*\n```\n\nThis will redirect **all** URLs beginning with `/auto/` to your destination, like this:\n\n```\n/auto/hyundai -> /fr/auto\n/auto/hyundai/ioniq-5 -> /fr/auto\n/auto/hyundai/ioniq-5 -> /fr/auto\n```\n\n⚠️ Note that this **discards the rest of the URL**. If this is not what you want, see below for ways to avoid it.\n\n### Exact matches always win\n\nIf you have a redirect rule with no `*`, it will always beat a redirect rule with an `*`.\n\nFro instance, if you have rules like this:\n\n```\n/auto/gm/bolt -> /en/auto/gm/bolt\n/auto/* -> /fr/auto\n```\n\nThen actual redirects will play out like this:\n\n```\n/auto/gm/bolt -> /en/auto/gm/bolt\n/auto/toyota/tercel -> /fr/auto\n```\n\n### Capturing wildcards\n\nIf you are creating an \"URL\" redirect, and not an \"Internal Page\" one, you may also \"capture\" and reuse the rest of the URL by including the `*` . This is useful when the rest of the URL is still correct.\n\nFor instance, if your \"Old URL\" is:\n\n```\n/auto/*\n```\n\nAnd you select \"URL\" as the redirect type and type the following in the \"URL\" field:\n\n```\n/fr/auto/*\n```\n\nThen redirects will occur like this:\n\n```\n/auto/hyundai -> /fr/auto/hyundai\n/auto/hyundai/ioniq-5 -> /fr/auto/hyundai/ioniq-5\n/auto/hyundai/ioniq-5 -> /fr/auto/hyundai/ioniq-5\n```\n\n### Creating fallbacks with multiple wildcard rules\n\nNote that if you have two redirect rules involving an `*` and one is longer than the other, **the longer match always wins.*\n\nFor example, if your redirects are set up like this:\n\n```\n/auto/hyundai/* -> /en/car/hyundai/*\n/auto/* -> /fr/auto/*\n```\n\nThen redirects will play out like this:\n\n```\n/auto/mercedes -> /fr/auto/mercedes\n/auto/mercedes/w123 -> /fr/auto/mercedes/w123\n/auto/hyundai -> /en/car/hyundai\n/auto/hyundai/ioniq-5 -> /en/car/hyundai/ioniq-5\n/auto/hyundai/ioniq-5 -> /en/car/hyundai/ioniq-5\n```\n\n### Safety concerns\n\nBe aware that each redirect is live as soon as you save it and that it is possible to make a mess with redirects. A few obvious dangers like redirecting `/*` or `/api/v1/*` are locked out, but it is still possible to cause problems depending on your site's structure. In a pinch, you can remove unwanted redirects via the MongoDB command line client (look for `{ type: \"@apostrophecms/redirect\" }` in the `aposDocs` collection in MongoDB).\n\n### Soft redirects: when you don't need this module at all\n\nFor your convenience, ApostropheCMS automatically creates \"soft redirects\" every time you change the slug of a page or piece, provided the document was accessed at least once at the old URL. So you shouldn't need to manually create a \"hard redirect\" in that situation.\n\n## Extending the module\n\n### Providing a fallback handler\n\nIf you wish to handle redirects in another way when this module does not find a match, you can do so by listening for the `@apostrophecms/redirect:noMatch` event. This event handler receives `req, result`. To issue a redirect, set `result.redirect` in your event handler. To issue a \"raw\" redirect to which any sitewide prefix is not appended automatically, set `result.rawRedirect` in your event handler. You can also set `result.status` to match your need as the status code of the redirection, default is `302`.   \nIf you wish to alter the target url when a redirection is about to come, like for example to change the domain, you can listen to the `@apostrophecms/redirect:beforeRedirect`. This event handler receives `req, result`. `result` will have two properties that you can alter before the redirection: `status` and `url`. **Do not** call `req.res.redirect()` yourself in your event handler in those two cases.\n\nFor example:\n\n```javascript\n// modules/redirect-fallback/index.js\nmodule.exports = {\n  handlers(self) {\n    return {\n      '@apostrophecms/redirect:noMatch': {\n        // will be awaited, you can do queries here if needed\n        async fallback(req, result) {\n          if (req.url.match(/pattern/)) {\n            result.redirect = '/destination';\n          }\n        }\n      }\n    }\n  }\n}\n```\n\n### Preempting the redirect module\n\nIf your goal is to preempt this module by making a decision to redirect differently in some cases before this module looks for a match, register your own middleware and perform the redirect there. Use `before` to specify that your own module's middleware comes first.\n\nFor example:\n\n```javascript\n// modules/early-redirect/index.js\nmodule.exports = {\n  middleware(self) {\n    return {\n      earlyRedirect: {\n        before: '@apostrophecms/redirect',\n        middleware(req, res, next) {\n          if (req.url.match(/pattern/)) {\n            return res.redirect('/destination');\n          } else {\n            return next();\n          }\n        }\n      }\n    };\n  }\n};\n```\n\n### Executing the middleware sooner\n\nBy default the middleware of this module that checks for redirect is executed depending on the place of this module in your modules configuration.   \nYou can choose to execute it before any other module by setting the `before` option, indicating before which module middleware this one should run (for example `@apostrophecms/global`).   \nNote that by doing this, the above preemption will not work anymore.   \n\n\n## Redirecting to other locales\n\nIt's possible to redirect from one locale to another one, with external redirections, \nsince you manually define the url to redirect to.\n\nAs for internal redirects (relationships with pages), this works across locales as well, \nbut keep in mind that you will only see the internal redirects that target the current locale when managing redirects. \nTo find redirects that target an internal page in a different locale, switch locales before viewing \"Manage Redirects.\"\n\nA note for developers: a query builder called `currentLocaleTarget` hides redirects that have relationships to other locales (different from the current one).\nIf you want to get all redirects whatever the locale of their internal redirects you can undo this behavior using the query builder:\n```javascript\nconst redirects = await self.apos.modules['@apostrophecms/redirect']\n    .find(req)\n    .currentLocaleTarget(false)\n    .toArray();\n```\n","readmeFilename":"README.md","_rev":"1-9df8220fb089cda06c69e806aa7c6097"}