Share your project with a Public URL using NPM

By Robbie
October 7, 2021


This tutorial will show you how use npm to get a public URL for your project.

By the end of this tutorial, you’ll be able to run npm run expose which will both start your app and give it a public URL.

In most cases, you won’t need to do any firewall or networking config to make this work, because the traffic to the public URLs will automatically be tunneled through most networks and firewalls.


  • NodeJS. It comes with npm bundled in
  • A Linux, Mac or Windows machine with access to the internet
  • Half an hour or so of your time

You should be able to get a Public URL even if you are on a corporate network, VPN or behind a firewall (caveat: If its a very paranoid firewall, this may not work, for example if you work for a big three letter government agency or another extremely security sensitive employer and you are trying this at their office. Try at home to be sure in this case).

Set up your project

Open up your package.json file. If there is no package.json file (brand new project?), create one with npm init and follow the prompts.

You should have an entry in package.json for a "start" script. It should look like this:

    "name": "Your Project",
    "dependencies": {
        "react": "17.0"
    "scripts": {
        "start: "<command to start your app>, i.e. node src/index.js"

If not, you should create one. It should look like "start": "<command to start your app>" . Replace <command to start your project> with the command you use to start your app, such as node src/index.js if you use express and have it set up in src/index.js, or react-scripts start for react projects created using create-react-app.

You can now run npm start to start your app using npm. Thats useful even without a Public URL because you don’t need to remember the command to start your app.

Find out what port number your app is listening on

You need to find out the port number your app is listening on. There are a few ways you can do this.

You can run npm start. Usually the port number will be shown in the output. You might see output like or localhost:3000. The port number is the number after :, in this example output it would be port 3000.

In express, look for app.listen. It will have the port number passed as a parameter. For React apps, the default port is 3000 if you used create-react-app, unless you’ve changed it.

If the port number is not the common port numbers of 80 or 443, it will be in the last part of the URL you normally use when accessing your app locally, after the last : i.e. for http://myapp.local:8000, the port number is 8000.

If there is no port number in the URL and the URL does not start with https, your app is running on port 80, which is the default port that HTTP clients such as web browsers use so it doesn’t need to be specified in URLs.

If the URL does start with https and there is no port number in the URL, the port number is 443, which is the default HTTPS/SSL port.

Install expose

There is one dependency we need to satisfy before we continue. As npm is just a dependency manager that relies on other software to provide most functionality (such as npm dependencies), it can’t get you a public URL by itself without extra help.

For this tutorial, you’ll need to install expose, an app I created specifically for this purpose. You can then configure npm to use it to generate public URLs.

The process to install expose is a bit different, but just as easy as installing an NPM dependency.

Go to for instructions to install expose. Its an easy one line copy and paste install for Linux and Mac, just copy and paste the code shown into a terminal.

A downloadable executable is available for Windows, which you will then need to copy somewhere in your PATH, such as C:\Windows\System32 using an account with Administrator permissons.

Add expose to NPM

Adding expose to npm is a bit different to adding other dependencies.

Normally when adding dependencies to npm you would add them to the "dependencies" or "devDependencies" section in package.json or run npm install. This would work for dependencies like libraries, but expose is a separate app, kind of like grunt or webpack which are often installed globally.

So in this case, we’ll add an entry to the "scripts" section in package.json because we are going to use npm to launch your app with the "start" script you created earlier and then launch expose, which will give your app a Public URL. This is similar to how other projects often run grunt or webpack before starting the app.

In package.json, look for the "scripts" section. If there isn’t one, create it.

Add this entry to the scripts section: "expose": "npm start & expose <insert your port number here and remove the arrows>";

For example, lets say my app listens on port 3000, like most React apps. My package.json file should look something like this:

    "name": "Your Project",
    "description": "The project you want to get a public URL for"
    "dependencies": {
        "react": "17.0"
    "scripts": {
        "expose": "npm start & expose 3000"

This script will start your app, then run expose to give your app a Public URL.

Expose your app

Now run npm run expose.

You’ll see something similar to this in the output: is forwarding to localhost:3000 is forwarding to localhost:3000

These public HTTP and HTTPS URLs are randomly generated. Hit them in a browser or any other HTTP client and the traffic will go to your locally running project. All traffic is passed from the service to the the expose client app, which is why you didn’t have to do any complex networking or firewall config.

Traffic to the public URLs can easily tunnel through most corporate firewalls and networks.

You can also run expose by itself for non node applications. For example expose 3000 will also create Public URLs, except you will need to run npm start first otherwise the connections won’t reach your app.

Randomly generated URLs are good to get started. They are public, so you can share them around with colleagues and friends. They should be accessible from any unrestricted internet connection in the world. Some things they might be useful for:

  • You’re building a mobile app backend and want to connect to it from the app on your mobile device. Rather than plug a cable into your phone, configure USB debugging or do other complex config, you can expose your backend API with a public URL, then configure your mobile app to use this. No cables needed.

  • Webhook integrations. With a public URL a webhook provider can send a request directly to your locally running project, enabling you quickly test changes and use tools like debuggers to speed up development.

  • Local HTTPs. Normally to get HTTPs locally you need to buy a certificate and configure your web server to use it. Since you now have a https URL, you don’t need to do this unless you really want to. HTTPS is required for certain features like Web Notifications and Progressive Web Apps.

  • Collaborative development. Maybe you’re building a back end API and your colleague is building the React App that will use it, or vice versa. You can both generate public URLs for your projects and use them to integrate your work with each other or even other squad members. Feedback will be faster than deploying code to a remote server because you and your collaborators wont need to wait for deployments.

  • Demos. You can use the URL to demo your project to a colleague or someone else, just send them the URL.

More use cases and cookbooks can be found in the expose documentation:

Getting a custom URL that does not change

Because expose by default generates random subdomains, using a custom subdomain (like would save you from needing to reconfigure a different endpoint every time you run expose. is free to use for randomly generated subdomains. If you want to use your own custom subdomains like, you can do this for as little as $5.99/month depending on how many domains you want to use. This also supports the expose service, which is run by an independent developer and doesn’t have a big company behind it.

Here’s an example package.json using a custom subdomain.

	"name": "Your Project",
	"description": "The project you want to get a public URL for"
	"dependencies": {
		"react": "17.0"
	"scripts": {
		"expose": "expose 3000 as"

With this config, running npm run expose will now expose your project under the URL (assuming no one else has taken that subdomain): is forwarding to localhost:3000 is forwarding to localhost:3000

Of course, you can also run expose by itself without npm. In this example you can run expose 3000 as, but you’ll need to run npm start first otherwise the traffic hitting the Public URLs won’t reach your app because it won’t be running.

You can Sign up here to get custom subdomains.


The Public URLs expire after several hours. They should be used for development but not for any apps in production. Don’t use them to do anything involving extreme amounts of traffic like load testing, but serving an app or API is fine.


npm scripts are useful for testing and building your app, now you can also use them to get a public URL.

As an independent developer, I don’t have a big marketing department or investor funding to help me spread the word about expose. So if you found this article useful, please share it around on your favourite social platforms like Facebook, Twitter and LinkedIn.

Also consider purchasing a subscription to help cover the costs of running the expose service.

Happy coding!

Share this article

Follow the author