By default, when Rails starts a server in development, it listens on
localhost:3000 using regular HTTP. To more closely simulate a production scenario, I wanted to configure my Rails app to use HTTPS instead. Here’s how that works.
Rails uses the puma web server, which is capable of serving HTTPS as long as it is provided an appropriate private key and certificate. This is where the localhost gem comes in.
The localhost gem integrates directly with puma to generate a key and certificate; it then makes those available to puma during the startup process.
Even with the localhost gem installed, Rails will still default to HTTP on port 3000 unless told otherwise. To instruct Rails to use HTTPS, I start Rails like this:
The first time visiting
https://localhost:4000, the browser will complain about an untrusted certificate. This is because the certificate generated by the localhost gem is self-signed.
The specifics are different for each browser, but generally speaking, I needed to dismiss the security warning and add the certificate to the operating system’s registry to avoid future warnings. It takes just a few clicks, following these guides:
In addition, in case I need to use Ruby to make HTTPS connections to localhost, it is a good idea to make OpenSSL was aware of the certificate as well. An explanation can be found on Stack Overflow, but to make a long story short:
With Rails now using HTTPS, I needed to configure Vite to use HTTPS as well, if I wanted hot-reloading to work seamlessly in the browser. Whereas the localhost gem magically passes its key and certificate to puma, with Vite I needed to be more explicit:
The vite_ruby config needed to be updated as well:
With everything working, I was tempted to set
config.force_ssl=true in my Rails development config to further simulate a production environment. However, this caused problems because it enables HSTS by default. Once my browser saw the HSTS header, it now required all apps hosted on my machine to use HTTPS. That’s a bit more than I bargained for!
To prevent security restrictions from one app spilling over onto other apps, I recommend turning off HSTS (in development only):