# reachable-url

> Resolve a URL as fast as possible and report whether the destination is reachable over HTTP.

Latest version **2.1.4** (published 2026-09-17) · MIT license · 0 weekly downloads

## Install

```sh
npm install reachable-url
pnpm add reachable-url
yarn add reachable-url
bun add reachable-url
```

## Health

**Score 55/100 (C)** — status: active.

Positive: no vulnerabilities; recently updated; high maintenance score.

Warnings: low downloads; no types; no esm support.

## Facts

| | |
|---|---|
| Version | 2.1.4 |
| Published | 2026-09-17 |
| First published | 2018-05-01 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | none |
| Module format | CommonJS |
| Node | >=14 |
| Dependencies | 1 |
| Unpacked size | 14.3 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| GitHub stars | 42 |
| Author | Kiko Beats |
| Maintainers | kikobeats |
| Keywords | check, health, http, ping, probe, reachability, reachable, redirect, resolve, url |

## Links

- npm: https://www.npmjs.com/package/reachable-url
- Repository: https://github.com/Kikobeats/reachable-url
- Issues: https://github.com/Kikobeats/reachable-url/issues
- npm.io page: https://npm.io/package/reachable-url

## Dependencies (1)

- [@kikobeats/got](https://npm.io/package/@kikobeats/got.md) ~11.8.9

## Alternatives

- [base64url](https://npm.io/package/base64url.md) — 6.1M weekly downloads
- [get-installed-path](https://npm.io/package/get-installed-path.md) — 502.9K weekly downloads
- [@uppy/url](https://npm.io/package/@uppy/url.md) — 185.8K weekly downloads
- [@d3fc/d3fc-shape](https://npm.io/package/@d3fc/d3fc-shape.md) — 16.2K weekly downloads
- [localizer](https://npm.io/package/localizer.md) — 226 weekly downloads

## Recent versions

- 2.1.4 (latest) — 2026-09-17
- 2.1.3 — 2026-08-15
- 2.1.2 — 2026-08-05
- 2.1.1 — 2026-08-03
- 2.1.0 — 2026-08-02
- 2.0.0 — 2026-08-02
- 1.8.4 — 2026-08-02
- 1.8.3 — 2025-09-05
- 1.8.2 — 2025-06-28
- 1.8.1 — 2024-05-11
- 1.8.0 — 2024-02-10
- 1.7.2 — 2023-10-24
- 1.7.1 — 2022-09-26
- 1.7.0 — 2022-09-24
- 1.6.11 — 2022-05-17
- … 55 more at https://npm.io/package/reachable-url/versions

## README

# reachable-url

![Last version](https://img.shields.io/github/tag/Kikobeats/reachable-url.svg?style=flat-square)
[![Coverage Status](https://img.shields.io/coveralls/Kikobeats/reachable-url.svg?style=flat-square)](https://coveralls.io/github/Kikobeats/reachable-url)
[![NPM Status](https://img.shields.io/npm/dm/reachable-url.svg?style=flat-square)](https://www.npmjs.org/package/reachable-url)

> Given an URL, it resolves as fast as possible, performing a GET without downloading the body.

## Install

```bash
$ npm install reachable-url --save
```

## Usage

```js
const reachableUrl = require('reachable-url')

reachableUrl.isReachable(await reachableUrl('https://google.com')) // => true
```

## API

### reachableUrl(input, [options])

#### url

*Required*<br>
Type: `string`

The target URL to be resolved.

#### options

Same as [got#options](https://github.com/sindresorhus/got#goturl-options), plus:

##### maxBody

Type: `number`<br>
Default: `0`

How many bytes of the body to keep when the download would otherwise be cancelled. A non-negative integer or `Infinity`; anything else throws.

The default answers reachability from the status and headers alone, never reading a body. Ask for more when the bytes themselves decide something:

```js
// one byte is enough to tell an image from an HTML error page served as one
const response = await reachableUrl('https://example.com/favicon.png', { maxBody: 1 })
response.body[0] === 60 // => `<`, so the server answered with markup
```

Asking for more than one byte drops the `Range` header, since a server that honors it would answer with just that byte. `Infinity` is the extreme of that, and keeps the whole entity:

```js
const response = await reachableUrl('https://example.com/favicon.svg', { maxBody: Infinity })
response.body // => the whole entity
```

Passing `cache` keeps the whole body too, since a cache entry is only written once the body has been read in full:

```js
const cache = new Map()
const response = await reachableUrl('https://example.com/video.mp4', { cache })
response.body // => the whole entity, so it can be cached
```

`cache` needs [@kikobeats/cacheable-request](https://github.com/Kikobeats/cacheable-request): upstream [cacheable-request](https://github.com/jaredwray/cacheable/tree/main/packages/cacheable-request) never settles when the origin keeps the connection alive, and no timeout recovers from it. [@kikobeats/got](https://github.com/Kikobeats/got) depends on the fork since 11.8.9, so it works without any configuration. If something in your dependency tree still resolves the upstream package, passing `cache` throws instead of hanging.

#### returns

The [got response](https://github.com/sindresorhus/got#response), plus `requestUrl`, `redirectUrls`, `redirectStatusCodes` and the `followRedirect` in effect.

By default the request asks for a single byte (`Range: bytes=0-0`, which [`maxBody`](#maxbody) drops when it wants more). When a server ignores that and starts sending the whole entity, the download is cancelled: the status and headers already say whether the URL is reachable, so `body` is `undefined` on those responses unless [`maxBody`](#maxbody) asked for some of it.

A `206` that did answer the range is reported as the `200` it stands for, with `content-length` taken from a numeric `content-range` total. An unknown total (`bytes 0-0/*`) is left as a `206`.

### reachableUrl.isReachable(response)

#### response

*Required*<br>
Type: `object`

The response returned by `reachableUrl`, which echoes back `followRedirect` so it can be handed straight over.

A URL is reachable when the response is a final 2xx.

A redirect status is the final answer only when redirects were not being followed:

```js
const response = await reachableUrl('https://example.com', { followRedirect: false })
reachableUrl.isReachable(response) // => true, the 3xx is the destination
```

With redirect following on (the default), a 3xx is the hop the follow stopped at (a `beforeRedirect` hook threw, `maxRedirects` ran out), meaning the URL was never reached:

```js
const response = await reachableUrl('https://example.com', {
  hooks: { beforeRedirect: [() => { throw new Error('refused') }] }
})
reachableUrl.isReachable(response) // => false
```

A partial object missing `followRedirect` is judged as if redirects were being followed, so a bare `{ statusCode: 302 }` is unreachable.

## License

**reachable-url** © [Kiko Beats](https://kikobeats.com), released under the [MIT](https://github.com/Kikobeats/reachable-url/blob/master/LICENSE.md) License.<br>
Authored and maintained by Kiko Beats with help from [contributors](https://github.com/Kikobeats/reachable-url/contributors).

> [kikobeats.com](https://kikobeats.com) · GitHub [Kiko Beats](https://github.com/Kikobeats) · X [@Kikobeats](https://x.com/Kikobeats)

---
_Source: https://npm.io/package/reachable-url · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
