url.fileURLToPath()

Converts a file: URL into a correctly decoded, platform-specific absolute filesystem path.

Signature
url.fileURLToPath(url[, options])
  • Added in Node.js v10.12.0

Parameters

ParameterTypeDescription
$urlURL | stringThe file URL string or URL object to convert to a path.
$optionsoptionalObjectOptions controlling how the path is produced.
$options.windowsoptionalboolean | undefined= undefinedtrue if the path should be return as a windows filepath, false for posix, and undefined for the system default.

Return value

stringThe fully-resolved platform-specific Node.js file path.

Errors

Throws TypeError [ERR_INVALID_URL_SCHEME] when the URL's protocol is not file:, and ERR_INVALID_FILE_URL_PATH or ERR_INVALID_FILE_URL_HOST for a file: URL that cannot map to a path.

Changelog

  • PHP v22.1.0, v20.13.0The options argument can now be used to determine how to parse the path argument, including forcing Windows or POSIX rules.

fileURLToPath() is the correct translation from a file: URL to a filesystem path. It percent-decodes the path, converts slashes for the host platform, handles Windows drive letters and UNC hosts, and rejects anything that is not a file: URL.

When to reach for it

Almost always in ESM, where __dirname and __filename do not exist. import.meta.url is a file: URL string, and fileURLToPath(import.meta.url) is the supported way to turn it into a path you can hand to fs or path.dirname(). It is also what you need for the url passed to loader hooks and for any API that hands you a module URL.

What trips people up

The hand-rolled alternatives are all wrong. url.replace('file://', '') leaves %20 in place, so any path containing a space breaks. On Windows it also leaves a leading slash before the drive letter, producing /C:/…. And new URL(import.meta.url).pathname has both problems.

A file: URL with a query or fragment silently loses it: fileURLToPath('file:///a?b') returns /a. That matters if you built the URL by string concatenation from a filename containing ? or # — use pathToFileURL() to build it instead.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Get the directory of the current ESM module in place of __dirname
  • Resolve a data file that sits next to a module using import.meta.url
  • Convert the url argument in a module loader hook to a path for fs
  • Normalize file: URLs coming from a tool's config into real paths

Frequently asked questions

How do I get __dirname in an ES module?

Use path.dirname(fileURLToPath(import.meta.url)). Node also exposes import.meta.dirname directly in newer releases, but the fileURLToPath form works across every version that supports ESM.

Why can't I just strip 'file://' from import.meta.url?

Because the URL is percent-encoded: any path with a space, #, or non-ASCII character comes out wrong, and on Windows you are left with a leading slash before the drive letter. fileURLToPath handles decoding and platform rules for you.

What happens if I pass an http URL to fileURLToPath?

It throws a TypeError with code ERR_INVALID_URL_SCHEME. Check url.protocol === 'file:' first if the input might come from somewhere other than a module URL.

More examples

__dirname and __filename in ESM

Simulates the standard ESM shim with a fixed module URL so the output is stable. In a real module you pass import.meta.url; the resulting __dirname behaves exactly like the CommonJS one.

Example · javascript
// In a .mjs file or a "type": "module" package:
//   import { fileURLToPath } from 'node:url';
//   import path from 'node:path';
//   const __filename = fileURLToPath(import.meta.url);
//   const __dirname = path.dirname(__filename);

const { fileURLToPath } = require('node:url');
const path = require('node:path');

const moduleUrl = 'file:///srv/app/src/lib/loader.mjs';
const filename = fileURLToPath(moduleUrl);
const dirname = path.dirname(filename);

console.log(filename);
console.log(dirname);
console.log(path.join(dirname, '..', 'config.json'));

Why manual string slicing fails

Both shortcuts return paths still containing %20 and %23, which fs will look up literally and fail to find. The Windows pair shows the extra defect: the naive form keeps a leading slash before C:, which is not a valid path.

Example · javascript
const { fileURLToPath } = require('node:url');

const u = 'file:///home/carol/my%20docs/report%23final.txt';

console.log('correct: ', fileURLToPath(u));
console.log('replace: ', u.replace('file://', ''));
console.log('pathname:', new URL(u).pathname);

const win = 'file:///C:/Users/carol/a%20b.txt';
console.log('windows correct:', fileURLToPath(win, { windows: true }));
console.log('windows replace:', win.replace('file://', ''));

Resolving a sibling file from a module URL

new URL(relative, moduleUrl) does the resolution, then fileURLToPath converts once at the boundary where fs is called. In ESM you would write new URL('settings.json', import.meta.url) — the same pattern, and it survives the module being moved.

Example · javascript
const { fileURLToPath } = require('node:url');
const fs = require('node:fs');

fs.writeFileSync('settings.json', JSON.stringify({ mode: 'fast' }));

// Given a module URL, resolve a file next to it without touching strings.
const moduleUrl = new URL('file://' + process.cwd() + '/index.js');
const dataUrl = new URL('settings.json', moduleUrl);

console.log(fileURLToPath(dataUrl).endsWith('/settings.json'));
console.log(fs.readFileSync(fileURLToPath(dataUrl), 'utf8'));

fs.unlinkSync('settings.json');

Discussion

  • Be the first to comment on this lesson.