├── .github └── workflows │ └── build.yml ├── .gitignore ├── LICENSE ├── README.md ├── babel.config.js ├── docs ├── cli.md ├── custom-domains.md ├── faq.md ├── forever-free.md ├── http-tunnels.md ├── roadmap.md ├── security.md ├── the-basics.md ├── tls-passthru-tunnels.md └── tunnel-my.md ├── docusaurus.config.js ├── package-lock.json ├── package.json ├── sidebars.js ├── src ├── css │ └── custom.css └── pages │ ├── index.js │ └── styles.module.css └── static ├── .nojekyll ├── CNAME └── img ├── cloud-download.svg ├── favicon.ico ├── gift.svg ├── globe2.svg ├── lhr.png ├── logo.svg ├── phone.svg ├── puzzle.svg ├── share.svg └── shield-lock.svg /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | pull_request: 5 | branches: [main] 6 | push: 7 | branches: [main] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | - uses: actions/checkout@v2 15 | if: github.event_name == 'push' 16 | with: 17 | ref: build 18 | path: build_final 19 | - uses: actions/setup-node@v2 20 | with: 21 | node-version: '14.x' 22 | - uses: actions/cache@v2 23 | with: 24 | path: ~/.npm 25 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} 26 | restore-keys: | 27 | ${{ runner.os }}-node- 28 | - name: build 29 | run: | 30 | npm ci 31 | npm run build 32 | - name: push 33 | if: github.event_name == 'push' 34 | run: | 35 | git config --global user.email "robot@localhost.run" 36 | git config --global user.name "lhr robot" 37 | cd build_final 38 | git rm -r . 39 | tar -C ../build/ -cf - . | tar -xf - 40 | git add -A 41 | test -z "$(git diff-index --name-only HEAD --)" || git commit -m "build for $GITHUB_SHA" 42 | git push 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) localhost.run 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus 2](https://v2.docusaurus.io/). 4 | 5 | ## Installation 6 | 7 | ```console 8 | npm i 9 | ``` 10 | 11 | ## Local Development 12 | 13 | ```console 14 | npm run start 15 | ``` 16 | 17 | This command starts a local development server and open up a browser window. Most changes are reflected live without having to restart the server. 18 | 19 | ## PRs 20 | 21 | PR into the `main` branch and on merge the github pages build will be taken care of. 22 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /docs/cli.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: cli 3 | title: Command Line Interface 4 | sidebar_label: Command Line Interface 5 | slug: /cli 6 | --- 7 | 8 | 9 | localhost.run uses SSH, which is already installed on all major operating systems. 10 | 11 | Mac OS, Windows and most Linux will use openssh. The man page for openssh is [here](https://man.openbsd.org/ssh), the commands you will most often need for tunnels are documented below. 12 | 13 | ## ssh ... 14 | 15 | ssh [-R _tunnel_](#-r-customdomainbindporthosthostport) localhost.run [-- [[--output _output_](#--output-output)] [[--[no-]inject-http-proxy-headers](#--inject-http-proxy-headers)] [[--proxy-protocol-header-version _version_](#--proxy-protocol-header-version-version)] 16 | 17 | ### -R [[_customdomain_:]](#customdomain)[_bindport_](#bindport):[_host_:_hostport_](#hosthostport) 18 | 19 | Forward connections from the localhost.run internet accessable domain to _host_:_port_. 20 | 21 | #### _customdomain_ 22 | 23 | Optionally connect a tunnel to a [custom domain](custom-domains.md). 24 | 25 | This defaults to `localhost.run` which generates a free domain under `localhost.run`. 26 | 27 | #### _bindport_ 28 | 29 | Set your domain to listen on either `80` or `443`. 30 | 31 | `80` will deploy an unencrypted forwarder on port 80 and a TLS decryption forwarder on port 443 for your domain and send your application unencrypted traffic from your clients. 32 | 33 | `443` will deploy a redirect to HTTPS on port 80 and a TLS passthru forwarder on port 443 for your domain and send your application the unaltered TLS traffic from your clients. 34 | 35 | :::warning 36 | `443` is an advanced mode of operation used to develop TLS applications, like letsencrypt handlers for example. Unless you know with certainly you want to use it, you probably don't want to use it. 37 | ::: 38 | 39 | #### _host_:_hostport_ 40 | 41 | The local address to connect the tunnel to. For example if you access your local application on `localhost:8080` then this is also `localhost:8080` 42 | 43 | :::note 44 | Some operating systems set `localhost` to the ipv6 address `[::1]` while some frameworks listen on `127.0.0.1`, try `127.0.0.1` instead of `localhost` if there are connection issues. 45 | ::: 46 | 47 | ### --output _output_ 48 | 49 | Set the output format for event messages: 50 | 51 | * text (default) 52 | * json 53 | 54 | ### --inject-http-proxy-headers 55 | 56 | Enable http proxy headers. 57 | 58 | This functionality is *on* by default and can be disabled with `--no-inject-http-proxy-headers`. 59 | 60 | See [HTTP Tunnels Proxy headers section](http-tunnels#proxy_headers) for more information. 61 | 62 | ### --proxy-protocol-header-version _version_ 63 | 64 | Enable the [Proxy Protocol TCP header](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) and set the Proxy Protocol version: 65 | 66 | * v1 67 | * v2 68 | 69 | This defaults to `v1` when the header is enabled. 70 | 71 | 72 | ## Examples 73 | 74 | 1. `ssh -R 80:localhost:8080 localhost.run` will connect a free domain tunnel to localhost on port 8080. 75 | 76 | 1. `ssh -R 80:localhost:3000 localhost.run` will connect a free domain tunnel to localhost on port 3000. 77 | 78 | 1. `ssh -R example.com:80:localhost:3000 localhost.run` will connect a custom domain tunnel from example.com to localhost on port 3000. 79 | 80 | 1. `ssh -R www.example.com:80:localhost:3000 -R example.com:80:localhost:3000 localhost.run` will connect a custom domain tunnel from example.com and www.example.com to localhost on port 3000. See [Custom Domains](custom-domains.md) for more details about using subdomains included with all custom domains. 81 | 82 | 1. `ssh -R admin.example.com:80:localhost:8000 -R example.com:80:localhost:3000 localhost.run` will connect a custom domain tunnel from example.com to localhost on port 3000 and a custom domain tunnel from admin.example.com to localhost on port 8000. See [Custom Domains](custom-domains.md) for more details about using subdomains included with all custom domains. 83 | 84 | 1. `ssh -R example.com:80:localhost:3000 localhost.run -- --no-inject-http-proxy-headers` will connect a custom domain tunnel from example.com to localhost on port 3000 and not add HTTP Proxy headers. 85 | 86 | 1. `ssh -R example.com:80:localhost:3000 localhost.run -- --inject-proxy-protocol-header` will connect a custom domain tunnel from example.com to localhost on port 3000 and send a Proxy Protocol V1 header at the beginning of each TCP connection. 87 | -------------------------------------------------------------------------------- /docs/custom-domains.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: custom-domains 3 | title: Custom Domains 4 | sidebar_label: Custom Domains 5 | slug: /custom-domains 6 | --- 7 | 8 | For $9 per month (billed annually) a Custom Domain subscription enables a stable domain for your tunnel with a priority share of the bandwidth on the localhost.run system. 9 | 10 | You can use your own domain, or you can use a subdomain of lhr.rocks. 11 | 12 | To create a Custom Domain subscription visit https://admin.localhost.run, add your SSH key to your account for authentication and add your chosen Custom Domain to your account. 13 | 14 | If you want to use your own domain name, you will need to set up some DNS records on your domain to both verify ownership and route traffic to the localhost.run service as described below. 15 | 16 | If you want to use a subdomain of lhr.rocks this will already be set up for you and you can start using your tunnel immediately. 17 | 18 | Once setup is complete you can connect a tunnel to your domain by adding it to the `-R` cli argument like this: 19 | ``` 20 | ssh -R yourdomain.com:80:localhost:8080 plan@localhost.run 21 | ``` 22 | 23 | The `plan@` username is optional but if you have multiple SSH keys it's recommended. 24 | 25 | ## lhr.rocks subdomains 26 | 27 | lhr.rocks subdomains require no DNS setup and are the quickest way to get a tunnel with a stable domain name and faster speeds. 28 | 29 | Domains are issued on a first come basis so if your chosen domain is a duplicate please try another one. 30 | 31 | ## Setup DNS for your own domain 32 | 33 | If you have chosen to use your own domain you will need to set up some DNS records. 34 | 35 | Once you've added your custom domain you will be shown these DNS records to set up with your DNS provider. Your DNS provider is usually the same provider that you bought your domain name from. 36 | 37 | Check how to add a TXT record to your DNS provider and add the record you see in the admin console for your domain name, it will have the format of `_lhr.{your domain name}` and a target of your domain ID. 38 | 39 | Some providers add quotes to TXT records for you, please don't be surprised if you see this. You can check your TXT record at https://toolbox.googleapps.com/apps/dig/#TXT/, you should see an answer that looks similar to `_lhr.yourdomain.com. 299 IN TXT "630aa6d4-0294-4cf2-a0cf-843e30dd5b6b"`. 40 | 41 | ### CNAME vs A records 42 | 43 | The next step is tricky, CNAMEs are the easiest to set up but can't live on apex domains. 44 | 45 | An apex domain is the top of your DNS, but it isn't the same as an internet TLD domain, I have four examples to explain this: 46 | * Your DNS allows setting host names under example.com and you want to set your tunnels up on example.com. 47 | 48 | This **is** an apex, create A records on example.com. 49 | 50 | * Your DNS allows setting host names under example.com and you want to set your tunnels up on tunnel.example.com. 51 | 52 | This **is not** an apex create a CNAME record on example.com. 53 | 54 | * Your DNS allows setting host names under jane.example.com and you want to set your tunnels up on jane.example.com. 55 | 56 | This **is** an apex, create A records on jane.example.com. 57 | 58 | * Your DNS allows setting host names under jane.example.com and you want to set your tunnels up on tunnel.jane.example.com. 59 | 60 | This **is not** an apex create a CNAME record on jane.example.com. 61 | 62 | If you are still not sure use A records. 63 | 64 | Set either a CNAME record on your custom domain targeting `cd.localhost.run` *or* 3 A records on your custom domain targeting `54.161.197.247`, `54.82.85.249` and `35.171.254.69`. 65 | 66 | ## Subdomains are included in your custom domain plan 67 | 68 | If you set up a custom domain on `yourdomain.com` you get the option to use subdomains as part of the plan. 69 | 70 | This means that you can not only connect tunnels to `yourdomain.com`, but *also* to any subdomain of your custom domain, like `app1.yourdomain.com` and `anything-else.yourdomain.com` for example. 71 | 72 | To enable this functionality follow the regular setup instructions for your custom domain, and once complete, add a CNAME record for `*.yourdomain.com` pointing at `cd.localhost.run`. 73 | 74 | You can then connect a tunnel to subdomains: 75 | ``` 76 | ssh -R app1.yourdomain.com:80:localhost:8081 localhost.run 77 | ``` 78 | 79 | :::note 80 | You can connect up to 5 tunnels at the same time. 81 | ::: 82 | 83 | :::note 84 | the number of subdomains you can use in a month is limited by letsencrypt limits on certificate generation. 85 | ::: 86 | 87 | ## Changing your domain name 88 | 89 | To change to a new custom domain name follow these steps: 90 | 91 | 1. Delete your existing custom domain name subscription. This will credit your account for the amount of time remaining on this subscription. 92 | 1. Create a new custom domain subscription. This will use the credit from the cancelled subscription instead of asking for a new payment. 93 | 94 | ## Cancelling a subscription 95 | 96 | To cancel a subscription delete your custom domain. 97 | 98 | For any other billing related help please email help@localhost.run . 99 | -------------------------------------------------------------------------------- /docs/faq.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: faq 3 | title: FAQ 4 | sidebar_label: FAQ 5 | slug: /faq 6 | --- 7 | 8 | ## Generating an SSH key 9 | 10 | If you don't already have an SSH key you can generate one with this command: 11 | 12 | ```bash 13 | ssh-keygen -t rsa 14 | ``` 15 | 16 | [GitLab](https://docs.gitlab.com/ee/ssh/) and [GitHub](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent) have detailed documentation on creating SSH keys, once created you need to upload the generated file to https://admin.localhost.run/ . 17 | 18 | ## Common connectivity issues 19 | 20 | ### I've set up my SSH key in my account but my custom domain tunnel is denied 21 | 22 | try adding `plan` as your SSH user, like so: 23 | 24 | ```bash 25 | ssh -R example.com:80:localhost:8080 plan@localhost.run 26 | ``` 27 | 28 | If this doesn't fix it then please send an email for help. 29 | 30 | ### "localhost.run: Permission denied (publickey)" error in your terminal on a free tunnel 31 | 32 | :::warning 33 | If you are using a custom domain subscription you need to use an SSH key, this hint isn't for you. 34 | ::: 35 | 36 | If you are using a free tunnel you can skip the SSH key check by adding `nokey` as your SSH user, like so: 37 | 38 | ```bash 39 | ssh -R 80:localhost:8080 nokey@localhost.run 40 | ``` 41 | 42 | ### My tunnel name changes every time I connect 43 | 44 | Free tunnels change domain names after a few hours. To keep your domain name between connections make sure you have an SSH key and that you are not using the `nokey` username. 45 | 46 | ### "connect_to localhost port 8080: failed" error in your terminal 47 | 48 | Double check that your local application is accessible on localhost and your specificed port by accessing http://localhost:{your local port} in your browser. 49 | 50 | ### "Something went wrong opening the port forward, check your SSH command output for clues!" in your browser 51 | 52 | Check your running SSH command for hints, and double check that your local application is accessible on localhost and your specificed port by accessing http://localhost:{your local port} in your browser. 53 | 54 | ### I can see requests to localhost:8080 when browsing my site thru localhost.run 55 | 56 | Many web frameworks generate full URLs using the domain they _think_ they're running under, which can sometimes be localhost and the port they listen on. 57 | 58 | Google the name of your framework + reverse proxy for hints on how to fix issues like this. 59 | 60 | ### My connection is unstable, tunnels go down often 61 | 62 | Connections can go down for many reasons, but the most common is the TCP connection being broken by timeouts or routing changes on the open internet. 63 | 64 | This is the nature of the internet, it's a little crazy out there, but there is something you can do to avoid router timeouts, and when something breaks the connection anyway to detect and recover them quickly. 65 | 66 | The first, and simplest change, is to enable keepalives on your SSH connection by adding the `-o ServerAliveInterval=60` to your SSH command, like in this example: 67 | 68 | ``` 69 | ssh -o ServerAliveInterval=60 -R 80:localhost:8080 localhost.run 70 | ``` 71 | 72 | SSH will send an empty packet after 60 seconds of inactivity on your tunnel. This serves two purposes: 73 | 74 | 1. It wards off idle timeouts on internet routers by making the connection appear active. 75 | 2. It quickly detects broken routes on the internet, closes the SSH command, and allows you to recover it quickly. 76 | 77 | For most simple use cases this extra option alone is enough to keep tunnels healthy, and on the occasions where routes break to nudge you to restart them. 78 | 79 | For use cases that require auto-healing, the ssh command could be wrapped in a loop (with a sleep to avoid spinning the command) or a more robust ssh client, like for example [autossh](https://www.harding.motd.ca/autossh/). Either can be used to self-heal tunnels that the keepalives detect as down for you. 80 | 81 | If you do use autossh the command options are the same as SSH, don't forget to include the keepalive option, but also be sure to include a `-M 0` to disable autossh's own tunnel call as this won't work with localhost.run. 82 | 83 | ## I want to move my domain(s) to a new email login 84 | 85 | Sometimes this is because you've lost access to the email you used to sign up, or maybe you simply prefer another email. 86 | 87 | To move a domain: 88 | 89 | 1. Cancel your existing custom domain subscription if you still have access to your old account. 90 | 1. Set up your domain in your new account. It is fine that it is the same domain name. 91 | 1. Verify it by changing the TXT record on your`_lhr` subdomain to your new domain's id as instructed in the web console. 92 | 1. Make payment for the new domain subscription. You will be credited for time remaining on the old domain in the next step, don't worry. 93 | 1. Send a message to help@localhost.run explaining that you have to moved your domain to the new account. Your old subscription will be canceled and your new subscription credited with any remaining time. 94 | -------------------------------------------------------------------------------- /docs/forever-free.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: forever-free 3 | title: The Forever Free Tier 4 | sidebar_label: The Forever Free Tier 5 | slug: /forever-free 6 | --- 7 | 8 | localhost.run offers a forever free plan that not only doesn't require a client download, it doesn't even require signup for short-lived tunnels. 9 | 10 | Follow along with [The Basics](the-basics.md) and you'll be up and running in no time. 11 | 12 | There are some limitations on the free domains tho: 13 | 14 | 1. Domain names change regularly. 15 | 1. There is a speed limit. 16 | 17 | These limits are in place to prevent phishing sites from establishing themselves on localhost.run domains. 18 | 19 | These limits are not in place on the Custom Domain plans, so if you need a stable domain name with faster transfer rates please consider signing up for one at https://admin.localhost.run. 20 | 21 | ## Give your free domain a longer life 22 | 23 | If you use a free domain but want the domain name to last longer sign-up at https://admin.localhost.run/ and [add your SSH key](faq.md#generating-an-ssh-key). 24 | -------------------------------------------------------------------------------- /docs/http-tunnels.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: http-tunnels 3 | title: HTTP Tunnels 4 | sidebar_label: HTTP Tunnels 5 | slug: /http-tunnels 6 | --- 7 | 8 | To set up a HTTP tunnel to port 8080, request a port forward from port 80 by running the following command: 9 | ``` 10 | ssh -R 80:localhost:8080 localhost.run 11 | ``` 12 | 13 | localhost.run forwards HTTP traffic down your tunnel to your app, and automatically adds encrypted HTTPS endpoints for your clients to optionally connect to. 14 | 15 | 16 | Prefix your domain name with `https://` to access your tunnel over HTTPS, and localhost will unwrap the encryption for you and send the plain HTTP requests to your app. 17 | 18 | ## Proxy headers 19 | 20 | Reverse proxy headers are automatically added to HTTP requests, specifically [`X_Forwarded_For`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For), [`X_Forwarded_Host`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Host), [`X_Forwarded_Proto`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto) and the newer [`Forwarded`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Forwarded) header. 21 | 22 | These can be turned off with a [command line option](cli) if required. 23 | 24 | ## Carrying non-HTTP protocols over TLS 25 | 26 | Clients connecting to port 443 over TLS do not have to talk the HTTP protocol, any protocol from TLS capable client will be sent down your tunnel. 27 | 28 | For example, a PostgreSQL server listening on port 5432 could be connected to a tunnel with this command: 29 | 30 | ``` 31 | ssh -R 80:localhost:5432 localhost.run 32 | ``` 33 | 34 | and TLS clients could then connect to the generated localhost.run domain on port 443. 35 | 36 | Your app connected to your tunnel should be listening for plain TCP because localhost.run will take care of certificates and decryption for you. 37 | 38 | :::info 39 | Currently only [Custom Domains](custom-domains.md) can carry non-HTTP protocols. 40 | ::: 41 | -------------------------------------------------------------------------------- /docs/roadmap.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: roadmap 3 | title: Roadmap 4 | sidebar_label: Roadmap 5 | slug: /roadmap 6 | --- 7 | 8 | ## redirect http to https 9 | ## encrypted sni 10 | ## arbitrary tcp ports 11 | -------------------------------------------------------------------------------- /docs/security.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: security 3 | title: Security 4 | sidebar_label: Security 5 | slug: /security 6 | --- 7 | 8 | ## SSH 9 | 10 | [SSH isn't only used by localhost.run](https://en.wikipedia.org/wiki/SSH_(Secure_Shell)), it is widely used to provide secure login access to servers. 11 | 12 | It is end to end encrypted, meaning regardless of client encryption or mode of operation the tunnels from localhost.run back to your local app are fully encrypted. 13 | 14 | ## HTTP tunnels 15 | 16 | HTTP is unencrypted, and as such it is recommended that apps demand HTTPS in all cases. 17 | 18 | localhost.run automatically generates HTTPS endpoints for all HTTP tunnels that your clients can connect to and takes care of certificates and decryption for you. 19 | 20 | Proxy headers that most web frameworks will automatically detect are added, and so in most cases this is a simple setting to tell your web app to require HTTPS. 21 | 22 | Check your application frameworks documentation for more details on how to do this and [HTTP tunnels](http-tunnels) for more details on how tunnels work. 23 | 24 | 25 | ## TLS passthru 26 | 27 | localhost.run offers a TLS passthru mode of operation, allowing you to use your own TLS certificates. In this mode of operation localhost.run does not decrypt your traffic, rather it passes it thru as is to your encrypted tunnel, leaving your app to handle decryption. 28 | 29 | Check [TLS passthru tunnels](tls-passthru-tunnels) for more details on how these tunnels work. 30 | -------------------------------------------------------------------------------- /docs/the-basics.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: the-basics 3 | title: The Basics 4 | sidebar_label: The Basics 5 | slug: / 6 | --- 7 | 8 | ## Put a locally running HTTP, HTTPS or TLS app on the internet 9 | 10 | localhost.run is a client-less tool to instantly make a locally running application available on an internet accessible URL. 11 | 12 | All major operating systems already have SSH installed, and localhost.run uses SSH as a client, so no download is necessary to use the service and no account setup is needed for free domains. 13 | 14 | To connect an internet domain to an application running locally on port 8080 open a command terminal and run: 15 | 16 | ```bash 17 | ssh -R 80:localhost:8080 localhost.run 18 | ``` 19 | 20 | import { useState } from 'react' 21 | 22 | export const PortChooser = () => { 23 | const [port, setPort] = useState(3000); 24 | return ( 25 | <> 26 | running on  27 | 28 |   29 | setPort(event.target.value)} /> 30 | ? 31 | use this command: 32 |
{`ssh -R 80:localhost:${port} localhost.run
35 | `}
36 | 37 | ) 38 | }; 39 | 40 | 41 | -------------------------------------------------------------------------------- /docs/tls-passthru-tunnels.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: tls-passthru-tunnels 3 | title: TLS Passthru Tunnels 4 | sidebar_label: TLS Passthru Tunnels 5 | slug: /tls-passthru-tunnels 6 | --- 7 | 8 | To set up a TLS passthru tunnel to port 8443, request a port forward from port 443 by running the following command: 9 | ``` 10 | ssh -R 443:localhost:8443 localhost.run 11 | ``` 12 | 13 | localhost.run will forward all connections to your domain on port 443 thru to your app as is, and your app will need to provide it's own certificate and decrypt the connection. 14 | 15 | :::tip 16 | If you don't want to handle certificates and decryption yourself but still want your clients to connect to HTTPS see [HTTP Tunnels](http-tunnels), they provide HTTPS endpoints and take care of decryption for you. 17 | ::: 18 | 19 | :::info 20 | This mode of operation is currently only available to [Custom Domains](custom-domains.md). 21 | ::: 22 | 23 | ### Non-HTTP protocols 24 | 25 | 26 | -------------------------------------------------------------------------------- /docs/tunnel-my.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: tunnel-my 3 | title: Tunnel My ... 4 | sidebar_label: Tunnel My ... 5 | slug: /tunnel-my 6 | --- 7 | 8 | ## Wordpress 9 | 10 | Wordpress is configured to run under a specific URL, usually `http://localhost`. This is excellent for local development but not for being on the internet. 11 | 12 | Follow the instructions at https://wordpress.org/support/article/changing-the-site-url/ to set the URL of your wordpress installation. 13 | 14 | :::tip 15 | 16 | Free domains change after a period of time and are not recommended for Wordpress due to Wordpress's lengthy URL update procedure. 17 | 18 | If you will primarily be developing Wordpress sites please consider a Custom Domain plan from https://admin.localhost.run. This will give your tunnel a stable domain name which you only need to configure once. 19 | 20 | ::: 21 | 22 | :::caution 23 | 24 | If you want to use Wordpress with a free domain you can explore https://wordpress.org/plugins/relative-url/ and setting `WP_HOME` and `WP_SITEURL` to `'https://' . $_SERVER['HTTP_HOST']`, but please pay attention to the warnings on the relative-url plugin. 25 | 26 | ::: 27 | 28 | ## Favourite Web framework 29 | 30 | If you see requests going to `localhost:{your locally running apps port}` in your browsers dev tools when browsing your site through localhost.run check your framework for reverse proxy settings. 31 | 32 | More information can be found in [the faq](faq#i-can-see-requests-to-localhost8080-when-browsing-my-site-thru-localhostrun). 33 | 34 | ## SSH servers 35 | 36 | With a custom domain plan it is possible to tunnel to a SSH server using stunnel on the SSH client. 37 | 38 | 1. On the SSH server make sure your localhost.run tunnel is connected: 39 | 40 | ```bash 41 | ssh -R tunnel.example.com:80:localhost:22 plan@cd.localhost.run 42 | ``` 43 | 44 | This must be running to connect to SSH from your client computer. 45 | 46 | The rest of this guide is for the client computer. 47 | 48 | 1. Install [stunnel](https://www.stunnel.org/) on your client SSH computer. 49 | 50 | stunnel is an application that wraps TCP, like SSH, in TLS. 51 | 52 | 1. Configure it to wrap connections to localhost:2222 in TLS and send them to your custom domain by creating a file named `stunnel.conf` 53 | 54 | ```none title="stunnel.conf" 55 | foreground = yes 56 | pid = ./stunnel.pid 57 | [lhr] 58 | client = yes 59 | accept = localhost:2222 60 | connect = tunnel.example.com:443 61 | ``` 62 | 63 | 1. Run `stunnel stunnel.conf` in a terminal. This command must be running to connect to SSH. 64 | 65 | 1. Run `ssh localhost -p 2222` to connect over the tunnels to your SSH server. 66 | -------------------------------------------------------------------------------- /docusaurus.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | title: 'localhost.run', 3 | tagline: '<3 local dev', 4 | url: 'https://localhost.run', 5 | baseUrl: '/', 6 | onBrokenLinks: 'throw', 7 | onBrokenMarkdownLinks: 'warn', 8 | favicon: 'img/favicon.ico', 9 | organizationName: 'localhost-run', 10 | projectName: 'site', 11 | themeConfig: { 12 | colorMode: { 13 | defaultMode: 'dark', 14 | disableSwitch: true, 15 | }, 16 | navbar: { 17 | title: 'localhost.run', 18 | logo: { 19 | alt: 'localhost.run logo', 20 | src: 'img/logo.svg', 21 | }, 22 | items: [ 23 | { 24 | to: 'docs/', 25 | activeBasePath: 'docs', 26 | label: 'Docs', 27 | position: 'left', 28 | }, 29 | { 30 | href: 'https://medium.com/localhost-run', 31 | label: 'Blog', 32 | position: 'left' 33 | }, 34 | { 35 | href: 'https://admin.localhost.run/', 36 | label: 'Custom Domains Admin Console', 37 | position: 'right', 38 | }, 39 | ], 40 | }, 41 | footer: { 42 | style: 'dark', 43 | links: [ 44 | { 45 | title: 'Docs', 46 | items: [ 47 | { 48 | label: 'The Basics', 49 | to: 'docs/', 50 | }, 51 | { 52 | label: 'Custom Domains', 53 | to: 'docs/custom-domains', 54 | }, 55 | { 56 | label: 'Command Line Interface', 57 | to: 'docs/cli', 58 | }, 59 | ], 60 | }, 61 | { 62 | title: 'Community', 63 | items: [ 64 | { 65 | label: 'Twitter', 66 | href: 'https://twitter.com/localhost_run', 67 | }, 68 | ], 69 | }, 70 | { 71 | title: 'Help', 72 | items: [ 73 | { 74 | label: '@localhost.run on twitter', 75 | href: 'https://twitter.com/messages/compose?recipient_id=833775057159725057', 76 | }, 77 | { 78 | label: 'email help@localhost.run', 79 | href: 'mailto:help@localhost.run', 80 | }, 81 | ], 82 | }, 83 | { 84 | title: 'More', 85 | items: [ 86 | { 87 | label: 'Blog', 88 | href: 'https://medium.com/localhost-run', 89 | }, 90 | { 91 | label: 'GitHub', 92 | href: 'https://github.com/localhost-run', 93 | }, 94 | ], 95 | }, 96 | ], 97 | copyright: 'Disseminate Consulting Limited is a company registered in England and Wales with company number 10676516.', 98 | }, 99 | }, 100 | presets: [ 101 | [ 102 | '@docusaurus/preset-classic', 103 | { 104 | docs: { 105 | sidebarPath: require.resolve('./sidebars.js'), 106 | routeBasePath: '/docs/', 107 | // Please change this to your repo. 108 | editUrl: 109 | 'https://github.com/localhost-run/site/edit/main/', 110 | }, 111 | blog: { 112 | showReadingTime: true, 113 | // Please change this to your repo. 114 | editUrl: 115 | 'https://github.com/localhost-run/iwishihadablog/edit/master/website/blog/', 116 | }, 117 | theme: { 118 | customCss: require.resolve('./src/css/custom.css'), 119 | }, 120 | }, 121 | ], 122 | ], 123 | }; 124 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docs-localhost-run", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "build": "docusaurus build", 9 | "swizzle": "docusaurus swizzle", 10 | "deploy": "docusaurus deploy", 11 | "serve": "docusaurus serve", 12 | "clear": "docusaurus clear" 13 | }, 14 | "dependencies": { 15 | "@docusaurus/core": "2.0.0-beta.9", 16 | "@docusaurus/preset-classic": "2.0.0-beta.9", 17 | "@mdx-js/react": "^1.6.21", 18 | "clsx": "^1.1.1", 19 | "react": "^16.8.4", 20 | "react-dom": "^16.8.4" 21 | }, 22 | "browserslist": { 23 | "production": [ 24 | ">0.5%", 25 | "not dead", 26 | "not op_mini all" 27 | ], 28 | "development": [ 29 | "last 1 chrome version", 30 | "last 1 firefox version", 31 | "last 1 safari version" 32 | ] 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /sidebars.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | docs: [ 3 | 'the-basics', 4 | 'cli', 5 | 'custom-domains', 6 | 'http-tunnels', 7 | 'tls-passthru-tunnels', 8 | 'tunnel-my', 9 | 'forever-free', 10 | 'security', 11 | 'faq', 12 | ] 13 | }; 14 | -------------------------------------------------------------------------------- /src/css/custom.css: -------------------------------------------------------------------------------- 1 | /* stylelint-disable docusaurus/copyright-header */ 2 | /** 3 | * Any CSS included here will be global. The classic template 4 | * bundles Infima by default. Infima is a CSS framework designed to 5 | * work well for content-centric websites. 6 | */ 7 | 8 | /* You can override the default Infima variables here. */ 9 | :root { 10 | --ifm-color-primary: #25c2a0; 11 | --ifm-color-primary-dark: rgb(33, 175, 144); 12 | --ifm-color-primary-darker: rgb(31, 165, 136); 13 | --ifm-color-primary-darkest: rgb(26, 136, 112); 14 | --ifm-color-primary-light: rgb(70, 203, 174); 15 | --ifm-color-primary-lighter: rgb(102, 212, 189); 16 | --ifm-color-primary-lightest: rgb(146, 224, 208); 17 | --ifm-code-font-size: 95%; 18 | } 19 | 20 | .docusaurus-highlight-code-line { 21 | background-color: rgb(72, 77, 91); 22 | display: block; 23 | margin: 0 calc(-1 * var(--ifm-pre-padding)); 24 | padding: 0 var(--ifm-pre-padding); 25 | } 26 | 27 | .terminal { 28 | padding: 2px 2px; 29 | } 30 | 31 | .terminalBody { 32 | height: 20em; 33 | padding-left: 5; 34 | text-align: left; 35 | color: white; 36 | } 37 | 38 | .terminalBody p { 39 | margin: 0 0; 40 | } 41 | 42 | .terminalHeader { 43 | color: gray; 44 | text-align: center; 45 | padding: 5px; 46 | border-bottom: 1px solid; 47 | } 48 | 49 | .textSelect { 50 | user-select: all; 51 | } 52 | 53 | .feature { 54 | margin: 30px; 55 | } 56 | 57 | .hero__title { 58 | white-space: nowrap; 59 | } 60 | -------------------------------------------------------------------------------- /src/pages/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState, } from 'react'; 2 | import clsx from 'clsx'; 3 | import Layout from '@theme/Layout'; 4 | import Link from '@docusaurus/Link'; 5 | import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; 6 | import useBaseUrl from '@docusaurus/useBaseUrl'; 7 | import styles from './styles.module.css'; 8 | 9 | function Terminal() { 10 | return ( 11 |
 12 |       
terminal
13 |
14 | 15 |

$ ssh -R 80:localhost:8080 nokey@localhost.run

16 |
17 |

yourapp.localhost.run tunneled with tls termination

18 |
19 |
20 |
21 | ) 22 | } 23 | 24 | const features = [ 25 | { 26 | title: 'No download or signup', 27 | imageUrl: 'img/cloud-download.svg', 28 | description: ( 29 | <> 30 | Connect a tunnel now using the ssh client already installed on your computer. 31 | 32 | ), 33 | }, 34 | { 35 | title: 'Bring your own domain name', 36 | imageUrl: 'img/globe2.svg', 37 | description: ( 38 | <> 39 | Attach your own domain name or a stable lhr.rocks one to a tunnel with a custom domain subscription for $9 per month when billed annually. 40 | 41 | ), 42 | }, 43 | { 44 | title: 'Secure', 45 | imageUrl: 'img/shield-lock.svg', 46 | description: ( 47 | <> 48 | TLS certificates are automatically generated to enable HTTPS and secure client connections. Tunnels are encrypted to your app. 49 | 50 | ), 51 | }, 52 | { 53 | title: 'Instantly share urls', 54 | imageUrl: 'img/share.svg', 55 | description: ( 56 | <> 57 | Show clients work running on your computer, access a web application running on your computer from your phone or tablet, or give your Raspberry Pi a home on the internet. 58 | 59 | ), 60 | }, 61 | { 62 | title: 'Forever free tier', 63 | imageUrl: 'img/gift.svg', 64 | description: ( 65 | <> 66 | Free domains will always be free, giving you a quick way to instantly share a web application running on any computer. 67 | 68 | ), 69 | }, 70 | { 71 | title: 'Advanced use cases', 72 | imageUrl: 'img/puzzle.svg', 73 | description: ( 74 | <> 75 | Tunnels cater to more than just web application development, they can also pass thru encrypted TLS connections and connect non-HTTP protocols. 76 | 77 | ), 78 | }, 79 | ]; 80 | 81 | function Feature({imageUrl, title, description}) { 82 | const imgUrl = useBaseUrl(imageUrl); 83 | return ( 84 |
85 | {imgUrl && ( 86 |
87 | {title} 88 |
89 | )} 90 |

{title}

91 |

{description}

92 |
93 | ); 94 | } 95 | 96 | function Home() { 97 | const context = useDocusaurusContext(); 98 | const {siteConfig = {}} = context; 99 | return ( 100 | 103 |
104 |
105 |

{siteConfig.title}

106 |

{siteConfig.tagline}

107 |

connect a tunnel to your web application running on port 8080 now with

108 | 109 |
110 | 116 | Get Started 117 | 118 |
119 |
120 |
121 |
122 | {features && features.length > 0 && ( 123 |
124 |
125 |
126 | {features.map((props, idx) => ( 127 | 128 | ))} 129 |
130 |
131 |
132 | )} 133 |
134 |
135 | ); 136 | } 137 | 138 | export default Home; 139 | -------------------------------------------------------------------------------- /src/pages/styles.module.css: -------------------------------------------------------------------------------- 1 | /* stylelint-disable docusaurus/copyright-header */ 2 | 3 | /** 4 | * CSS files with the .module.css suffix will be treated as CSS modules 5 | * and scoped locally. 6 | */ 7 | 8 | .heroBanner { 9 | padding: 4rem 0; 10 | text-align: center; 11 | position: relative; 12 | overflow: hidden; 13 | } 14 | 15 | @media screen and (max-width: 966px) { 16 | .heroBanner { 17 | padding: 2rem; 18 | } 19 | } 20 | 21 | .buttons { 22 | display: flex; 23 | align-items: center; 24 | justify-content: center; 25 | } 26 | 27 | .features { 28 | display: flex; 29 | align-items: center; 30 | padding: 2rem 0; 31 | width: 100%; 32 | } 33 | 34 | .featureImage { 35 | height: 200px; 36 | width: 200px; 37 | } 38 | -------------------------------------------------------------------------------- /static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/localhost-run/site/2bf69f81acd0cd902ca4ce7e51392c632d70cd57/static/.nojekyll -------------------------------------------------------------------------------- /static/CNAME: -------------------------------------------------------------------------------- 1 | localhost.run 2 | -------------------------------------------------------------------------------- /static/img/cloud-download.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/localhost-run/site/2bf69f81acd0cd902ca4ce7e51392c632d70cd57/static/img/favicon.ico -------------------------------------------------------------------------------- /static/img/gift.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /static/img/globe2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /static/img/lhr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/localhost-run/site/2bf69f81acd0cd902ca4ce7e51392c632d70cd57/static/img/lhr.png -------------------------------------------------------------------------------- /static/img/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 40 | 43 | 44 | 46 | 47 | 49 | image/svg+xml 50 | 52 | 53 | 54 | 55 | 56 | 62 | 73 | 76 | 81 | 86 | 91 | 96 | 101 | 102 | 106 | 111 | 116 | 121 | 126 | 131 | 136 | 141 | 146 | 151 | 156 | 161 | 166 | 171 | 176 | 181 | 186 | 187 | 193 | 194 | 195 | -------------------------------------------------------------------------------- /static/img/phone.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /static/img/puzzle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /static/img/share.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /static/img/shield-lock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | --------------------------------------------------------------------------------