Rails on Localhost: Secure Context and Local HTTPS with Caddy

Rails on Localhost: Secure Context and Local HTTPS with Caddy

Localhost is treated as a trustworthy origin even without TLS, so all apps on localhost run in a secure context. This allows secure features to work in development. You can also run multiple apps on localhost with subdomains + ports to separate them. When you do need local HTTPS, use Caddy server.

4 min read

TL;DR: You can and should use localhost in development. Browsers give a special treatment to the http://localhost domain. Even though it's on HTTP, most of the time it will be treated as HTTPS. So any browser APIs that need HTTPS, will continue working even if you're using localhost. When you do need HTTPS locally, use Caddy.

Also, localhost can have subdomains. So you can just prefix the URL with your app name, and suffix it with the port to separate multiple apps, like http://{subdomain}.localhost:{port}. I had no idea about this until I saw this tweet from DHH.

and again in the Rails World keynote...

Rails World Keynote: Localhost
Rails World Keynote

Secure Contexts

đź’ˇ
A potentially trustworthy origin is one that the browser can generally trust to deliver data security, even though strictly speaking it does not meet the criteria of a secure context. - MDN

Browsers normally only treat HTTPS origins as secure contexts. But there’s a special exception for local development: certain origins are treated as potentially trustworthy even if they don’t use TLS.

That list includes:

  • http://127.0.0.1
  • http://localhost
  • Any subdomain ending in .localhost (e.g. http://app.localhost)

The reasoning is simple: when you access these addresses, the request never leaves your machine. There’s no network hop where data could be intercepted. So, although it’s technically plain HTTP, the browser considers it safe enough to behave as if it were secure.

Using subdomains with localhost (like blog.localhost:3000) has several practical benefits for local development, especially when working on complex Rails apps. They make your development environment behave more like production, helping you test cookies, sessions, routing, and multi-app setups, without extra DNS or host configuration.

Here're a few use-cases for localhost domains with a custom subdomain.

1. Simulating production-like domains

  • In production, you might serve different parts of your app on different subdomains (e.g., blog.example.com, api.example.com).
  • Using blog.localhost mimics this locally, so you can test routing, cookies, CORS, and redirects in an environment closer to production.

2. Handling cookies & sessions correctly

  • Browsers scope cookies to a domain.
  • With subdomains (e.g., blog.localhost, admin.localhost), you can:
    • Test shared cookies across subdomains.
    • Verify session handling when different subdomains need to share authentication (like SSO).

3. Testing multi-tenant or subdomain-based routing

  • Rails apps often use subdomain-based routing for multi-tenancy (tenant1.example.com, tenant2.example.com).
  • Using tenant.localhost allows you to test these routes without production domains.

4. Easier separation for multiple apps

  • If you’re running multiple apps (e.g., writesoftwarewell.localhost:3000, typeangle.localhost:3001), subdomains help keep them organized and accessible.

5. No extra setup needed

  • localhost is a special-use domain that browsers resolve without DNS.
  • Any *.localhost automatically resolves to 127.0.0.1. No /etc/hosts changes are required.

What if I do need local HTTPS?

There're some edge cases when you may want your local development app to behave very similar to the one in production, which may include using HTTPS during development. This is the case when you want to debug an issue that occurs on HTTPS but not on HTTP, or you want to locally test third-party APIs that need HTTPS.

You can run the Rails app locally on HTTPS with the Caddy server. Caddy is an awesome web server to serve your sites, services, and applications.

First, install Caddy.

$ brew install caddy

We have the Rails app running on the port 3000. We'll use Caddy to as a reverse proxy server that will accept requests over HTTPS and forward them to the Rails app. In Caddy, setting up a reverse proxy is as simple as this:

blog.localhost {
	reverse_proxy :3000
}

Add the following code in a new file called Caddyfile, stored in your Rails app.

Now, you can run Caddy from your Rails app.

$ caddy run

The run command starts Caddy in the foreground, i.e. you see the output. If you want to run it in the background, run the caddy start command. However, I suggest running it in the foreground.

In a separate terminal window / tab, launch the Rails server as usual.

$ rails server

Now visit the url endpoint https://blog.localhost. The very first time, it will take a second to load as Caddy needs to generate a self-signed certificate for the blog.localhost domain. But you should quickly see your app running on HTTPS.

We're almost done, but there is one small quality-of-life enhancement we can make. Right now, it's quite cumbersome having to run a separate commands to launch the Caddy server. Let's move the caddy run command in the Procfile to launch Caddy along with the Rails server and TailwindCSS compiler.

Here's the updated Procfile.dev:

web: bin/rails server
css: bin/rails tailwindcss:watch
caddy: caddy run

That's it. Now you can run bin/dev to launch your application along with Caddy.

Visit https://blog.localhost to ensure the app is up and running and serving requests.


That's a wrap. I hope you found this article helpful and you learned something new. In short: localhost traffic isn’t encrypted, but because it’s confined to your own box, browsers treat it as “trustworthy enough” to enable features that normally require HTTPS.

As always, if you have any questions or feedback, didn't understand something, or found a mistake, please leave a comment below or send me an email. I reply to all emails I get from developers, and I look forward to hearing from you.

If you'd like to receive future articles directly in your email, please subscribe to my blog. Your email is respected, never shared, rented, sold or spammed. If you're already a subscriber, thank you.