{"id":19556355,"url":"https://github.com/fiverr/perimeterx-axios-interceptor","last_synced_at":"2025-07-22T10:06:34.595Z","repository":{"id":39885422,"uuid":"246058070","full_name":"fiverr/perimeterx-axios-interceptor","owner":"fiverr","description":"🧱 Intercept requests which are blocked by PerimeterX - pop up the challenge and retry the request","archived":false,"fork":false,"pushed_at":"2024-12-05T10:32:21.000Z","size":3001,"stargazers_count":13,"open_issues_count":0,"forks_count":2,"subscribers_count":36,"default_branch":"master","last_synced_at":"2025-07-01T02:04:36.586Z","etag":null,"topics":["axios","captcha","challenge","intercept","perimeterx"],"latest_commit_sha":null,"homepage":"https://opensource.fiverr.com/perimeterx-axios-interceptor/?appId=","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fiverr.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-03-09T14:29:42.000Z","updated_at":"2025-06-27T21:19:14.000Z","dependencies_parsed_at":"2024-10-25T17:03:29.966Z","dependency_job_id":"787c315e-efdf-4d76-841b-96b43f9ef2f6","html_url":"https://github.com/fiverr/perimeterx-axios-interceptor","commit_stats":{"total_commits":41,"total_committers":4,"mean_commits":10.25,"dds":0.4390243902439024,"last_synced_commit":"71f6f100140346fe941f4352bedb3f284e381d73"},"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/fiverr/perimeterx-axios-interceptor","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fiverr%2Fperimeterx-axios-interceptor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fiverr%2Fperimeterx-axios-interceptor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fiverr%2Fperimeterx-axios-interceptor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fiverr%2Fperimeterx-axios-interceptor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fiverr","download_url":"https://codeload.github.com/fiverr/perimeterx-axios-interceptor/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fiverr%2Fperimeterx-axios-interceptor/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266472836,"owners_count":23934470,"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","status":"online","status_checked_at":"2025-07-22T02:00:09.085Z","response_time":66,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["axios","captcha","challenge","intercept","perimeterx"],"created_at":"2024-11-11T04:37:39.227Z","updated_at":"2025-07-22T10:06:34.566Z","avatar_url":"https://github.com/fiverr.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# perimeterx-axios-interceptor [![](https://img.shields.io/circleci/build/github/fiverr/perimeterx-axios-interceptor?style=flat-square)](https://circleci.com/gh/fiverr/perimeterx-axios-interceptor) [![](https://img.shields.io/npm/v/perimeterx-axios-interceptor.svg?style=flat-square)](https://www.npmjs.com/package/perimeterx-axios-interceptor) [![](https://img.shields.io/badge/-playground-informational?style=flat-square)](https://fiverr.github.io/perimeterx-axios-interceptor/?appId=)\n\n## 🧱 Intercept requests which are blocked by PerimeterX - pop up the challenge and retry the request\n\nUsing [Advanced Blocking Response](https://github.com/PerimeterX/perimeterx-nginx-plugin#-advanced-blocking-response) blocked JSON requests receive a JSON response with status 403. This response includes a payload which allow us to display PerimeterX's challenge. After visitor is exonerated, the original request will be sent and resolved using the original promise.\n\n### Playground\n[Visit the playground](https://fiverr.github.io/perimeterx-axios-interceptor/?appId=) to experience the behaviour\n\n### Quick implementation\n```js\nimport axios from 'axios';\nimport { attach } from 'perimeterx-axios-interceptor';\n\nattach(axios);\n```\n\n### Implement all the things!\n```js\nimport axios from 'axios';\nimport { attach, detach } from 'perimeterx-axios-interceptor';\n\nattach(axios, {\n    filter: ({ path }) =\u003e !/\\/logger/.test(path),\n    onintercept: request =\u003e logger.info(`Intercepted a block response from request ${request.url}`),\n    onignore: request =\u003e logger.info(`Ignored a block response from request ${request.url}`),\n    onsuccess: request =\u003e logger.info(`Exonerated a request to ${request.url}`),\n    onfailure: (request, error) =\u003e logger.info(`Failed to exonerate request to ${request.url}: ${error.message}`),\n    onerror: error =\u003e logger.error(error),\n    simulate: true, // Will **not** load the challenge\n    modalConfig: {\n        className: 'my-challenge-popup',\n        title: 'Are you human?',\n        subtitle: 'Please complete the challenge',\n        quickfixes: [\n            '1. Disable adblocker',\n            '2. Enable Javascript'\n        ],\n        suffix: 'Still having issues? Contact support at support@example.com',\n        timeout: 3000,\n        allowClose: false,\n        compact: false,\n    }\n});\n\n// Remove the interceptor for some reason. Perhaps in order to re attach with different settings\ndetach(axios);\n\n// Disable Auto-ABR\nwindow._pxMonitorAbr = false;\n```\n\n### Flow description\nUsing the feature [Advanced Blocking Response](https://github.com/PerimeterX/perimeterx-nginx-plugin#-advanced-blocking-response) featured in PerimeterX's NginX Lua plugin\n\n1. Request is blocked by PerimeterX (403)\n1. Challenge modal is added to UI\n1. Challenge is resolved by user (user is exonerated)\n1. Replay original request and resolve original promise\n\n![](https://user-images.githubusercontent.com/516342/78470552-1fdf8080-7733-11ea-882a-3a261492c85c.png)\n\n### HTML output\n\n\u003cdetails\u003e\n    \u003csummary\u003e\u003cstrong\u003eView full HTML output\u003c/strong\u003e\u003c/summary\u003e\n\n```html\n\u003cdialog class=\"perimeterx-async-challenge\" open=\"open\"\u003e\n    \u003cdiv\u003e\n        \u003cp class=\"title\"\u003eOne Small Step\u003c/p\u003e\n        \u003cp class=\"subtitle\"\u003ePlease check the box below to continue your normal visit\u003c/p\u003e\n        \u003cdiv id=\"px-captcha\" class=\"challenge-box\"\u003e\n            \u003c!-- Challange markup (div.g-recaptcha) injected by PerimeterX Javascript --\u003e\n        \u003c/div\u003e\n        \u003cp class=\"quickfix\"\u003ePlease exclude this website from ad blocking or ad filtering software.\u003c/p\u003e\n        \u003cp class=\"quickfix\"\u003eMake sure you don't have any browser extensions tampering with request headers or user agent string.\u003c/p\u003e\n        \u003cp\u003eIf you're still having trouble accessing the site, please contact customer support.\u003c/p\u003e\n        \u003cstyle\u003e\n.perimeterx-async-challenge {\n    z-index: 10000;\n    position: fixed;\n    left: 0;\n    top: 0;\n    width: 100%;\n    height: 100%;\n    border: 0;\n    margin: 0;\n    padding: 0;\n    background: rgba(0, 0, 0, .3);\n    color: black;\n}\n.perimeterx-async-challenge \u003e div {\n    margin: 20vh 20vw 0;\n    background: white;\n    box-shadow: 0 0 2em rgba(0, 0, 0, .4);\n    padding: 1em 1.5em;\n}\n.perimeterx-async-challenge p,\n.perimeterx-async-challenge .challenge-box {\n    margin: 0 0 .5em;\n}\n.perimeterx-async-challenge .title {\n    font-size: 2em;\n    font-weight: bold;\n}\n.perimeterx-async-challenge .subtitle {\n    font-size: 1.4em;\n}\n.perimeterx-async-challenge .quickfix {\n    font-size: .8em;\n    margin: 0;\n}\n.perimeterx-async-challenge .quickfix:before {\n    content: \"•\";\n    margin: 0 .5em\n}\n@media screen and (max-width:1040px) {\n    .perimeterx-async-challenge \u003e div {\n        margin: 10vh 10vw 0;\n    }\n}\n@media screen and (max-width:800px) {\n    .perimeterx-async-challenge \u003e div {\n        margin: 5vw 5vw 0;\n    }\n}\n        \u003c/style\u003e\n    \u003c/div\u003e\n\u003c/dialog\u003e\n\u003cscript src=\"https://captcha.px-cdn.net/\u003cPERIMETERX_APP_IP\u003e/captcha.js\"\u003e\u003c/script\u003e\n```\n\n\u003e If you add a custom class, `dialog` element will include **both class names**: `\u003cdialog class=\"perimeterx-async-challenge my-challenge-popup\" open\u003e`\n\n\u003c/details\u003e\n\n### API\n#### 1st argument: `axios`\nThis is the Axios instance this plugin will intercept. If you use a global axios instance, just pass that one in\n#### 2nd argument: options\nThis is an optional object. All of its properties are also optional:\n##### `filter` {function}\nFilter function is fired **before** the intercepting function. If filter is passed as an argument, it **must return a truthy value** for the interceptor to fulfil it's role. **Falsy values will result in the interceptor passing the response as is**.\nIt's signature includes the following named arguments:\n\n| Name | Type | Meaning | Usage\n| - | - | - | -\n| `path` | string | Request original path | `filter: ({ path }) =\u003e !/^\\/(tracking\\|beacon)(\\/\\|$)/.test(path)`\n| `appId` | string | PerimeterX Application ID | `filter: ({ appId }) =\u003e appId === window._pxAppId`\n\nIt is considered a good practice **not** to disrupt the user experience for background communication suck as liveness beacons, logs and metrics. The axios error will be thrown to the listener and will be added a new field, `ignored`, so that consumers can elect to ignore these skipped blocks.\n\n\u003e Requests that will fail the filter method will fire the \"onignore\" callback from your configuration.\n\n```js\naxios('/beacon')\n    .catch(\n        error =\u003e error.ignored\n            ? logger.debug('Ignored blocked request')\n            : logger.error(error)\n    );\n```\n\n##### `onerror` {function}\nThis function is called when an internal error happened with this interceptor\nThe signature includes the error:\n```js\nonerror: (error) =\u003e logger.error(error)\n```\n##### `onintercept` {function}\nThis function is called on every time a request is recognised as a PerimeterX block.\nThe signature includes the original request object (axios.config):\n```js\nonintercept: (request) =\u003e logger.info({ message: 'Axios intercepted a PerimeterX block response', url: request.url })\n```\n##### `onignore` {function}\nSimilar to `onintercept`, only this will fire for **ignored** request that did not pass the `filter` method.\nThe signature includes the original request object (axios.config):\n```js\nonignore: (request) =\u003e logger.info({ message: 'Axios ignored a PerimeterX block response', url: request.url })\n```\n##### `onsuccess` {function}\nThis function is called when a challenge was successfully completed.\nThe signature includes the original request object (axios.config):\n```js\nonsuccess: (request) =\u003e logger.info({ message: 'Axios interceptor exonerated request', url: request.url })\n```\n##### `onfailure` {function}\nThis function is called when a challenge was successfully completed.\nThe signature includes the original request object (axios.config) and the rejection error:\n```js\nonfailure: (request, error) =\u003e logger.info({ message: error.message, url: request.url, stack: error.stack })\n```\n##### `simulate` {boolean}\nSet \"simulate\" to a truthy value to allow monitoring without prompting users with exoneration.\nThe callback `onintercept` will fire, the rest will not. The promise will be rejected with the PerimeterX 403 response.\n##### `modalConfig` {object}\nThis object allows configuration of the modal GUI:\n\n- className (`{string}`): Add custom className to modal\n    - **Default**: None\n- title (`{string}`): Replace or disable default title\n    - **Default**: \"One Small Step\"\n- subtitle (`{string}`): Replace or disable default subtitle\n    - **Default**: \"Please check the box below to continue your normal visit\"]\n- quickfixes (`{string[]}`): Replace or disable default quick fixes (list)\n    - **Default**:\n        - \"• Please exclude this website from ad blocking or ad filtering software.\"\n        - \"• Make sure you don't have any browser extensions tampering with request headers or user agent string.\",\n- suffix (`{string}`): Replace or disable default suffix\n    - **Default**: \"If you're still having trouble accessing the site, please contact customer support.\"\n- timeout (`{number}`): Time, in milliseconds, to allow PerimeterX script to load before aborting\n    - **Default**: 3000 (3 seconds)\n- allowClose (`{boolean}`): Allow users to close the modal\n    - **Default**: true\n= compact (`{boolean}`): Compact mode - modify some of the default CSS to reduce the size of the modal - helps to fit in tight spaces like iframes\n    - **Default**: false\n\n\u003e Setting \"title\", \"subtitle\", \"quickfixes\", or \"suffix\" to a falsy value (null, empty string...) will prevent them from being rendered to GUI.\n\n```js\nmodalConfig: {\n    className: 'my-challenge-modal',\n    title: 'Just a little check',\n    quickfixes: [\n        '=\u003e Turn off ad blockers',\n        '=\u003e Contact support if you need further assistance'\n    ],\n    suffix: null,\n    timeout: 5000\n}\n```\n## About axios instances\nIf you plan to [create axios instances](https://github.com/axios/axios#creating-an-instance), I suggest you consider using [axios-inherit](https://www.npmjs.com/package/axios-inherit) to add interceptor inheritance capability.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffiverr%2Fperimeterx-axios-interceptor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffiverr%2Fperimeterx-axios-interceptor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffiverr%2Fperimeterx-axios-interceptor/lists"}