Get a public URL for local websites with ngrok

Purpose of ngrok

Sometimes, you don’t know what you don’t know, which is probably the worst type of unknown. For me, until recently ngrok was one of those things. But before going any further, I should explain what ngrok is. ngrok is a simple-to-use utility that allows you to get a public URL that you can use to expose a service running locally. By locally, I typically mean something bound to localhost.

An example of such a use would be starting a Django server locally for testing and then using ngrok to display to it a friend in a far-off location. On the ‘Talk Python to Me’ podcast, it was mentioned as an option to help test Amazon Alexa skills but I haven’t tried that myself. As a more concrete example, we use it for integration testing our blog posts with the Google structured data tester as well as the Twitter card validator.

Getting started

First, sign up for ngrok using your method of choice and then follow their installation and setup instructions. After those few steps, you’re ready to start. It’s really that easy and less work than it took for me to type this paragraph.

Example use

I’ll demonstrate an example by serving a site with Jekyll. For development, I normally serve our site locally using Vagrant so there is no browser to directly display it. Instead, it has to be tunneled to the host OS or accessed by public URL using ngrok.

To start, I’ll serve the site on port 4000 and put it in the background. This is strictly bound to the loopback interface and inaccessible via the host. Otherwise, it would have to be bound to another interface but for ngrok that isn’t required.

[vagrant@localhost influential-code-llc]$ bundle exec jekyll serve -P 4000 &

Once the server is running, it can be served using ngrok. The basic usage follows this pattern: ngrok <protocol> <port>. In this example, the protocol is HTTP on port 4000 and is started as shown below.

[vagrant@localhost vagrant-data]$ ngrok http 4000

After it starts, the terminal will look like the following image.

![ngrok started and showing public URL and requests]({{"/img/ngrok_start_terminal.png” | absolute_url}})

The terminal UI displays the public URL that can be accessed via http or https: Once the site is accessed, you’ll begin to see requests appear at the bottom of the the terminal UI. In my example, I have requests for:

  • /favicon.ico
  • /assets/minimal-social-icons.svg
  • /img/logo.png
  • /assets/main.css
  • / (index.html)

The terminal UI also shows where you can access the ngrok web UI: http://localhost:4040. The web UI is not the application that you’re serving but is something provided by ngrok that allows you to inspect the requests and replay them as needed.


There are many configuraiton options available, and you can read about them in the ngork documentation.

An example of custom configuration is the subdomain. In the example above, the subdomain was da2b850c, not exactly something of note. It’s important to note that the subdomain changes each time ngrok is run so for a more repeatable endpoint, you can use the -subdomain switch (e.g. `ngrok 4000 http -subdomain=influentialcode). This is helpful if you’re trying to present this repeatably in a more formal setting. Continue reading for an important point about this and other options.

And this brings me to the last point here. ngrok is a great tool but as my economics teacher used to say, “There’s not such thing a free lunch.". Some of the features, including the -subdomain and tls protocol option, are part of a paid plan. If you think that this is a tool that would be used extensively, this may be a good option. But don’t let this stop you from trying it out. There is plenty that you can do without paying for the extra features.

Thank you for taking the time to read this post. If you have questions or comments about this post, please leave them below. If you’d like to know when other posts are available, please follow us on Twitter.