Apr 7, 2026
Configuring local HTTPS for web app development
Recently, I was working on a Phoenix LiveView app called BESTguestbookEVER for one of our clients, which is an app where wedding guests record a video message instead of writing in a traditional guestbook. Thus, one of the key features of this web app is the video recording functionality. While implementing the recording functionality itself using web APIs was a relatively time-consuming task, what turned out to unexpectedly be a bigger headache was dealing with browser permissions, especially across different browsers. I thought it’s as simple as just asking the users for permission and then the user granting it through the browser prompts, but uh… no. As they say, the devil is in the details.
Finding out I needed HTTPS locally
The details for everything I needed to do to get video and audio permissions working is a story for another time, but one of the things I had to do was to setup HTTPS on the local development environment. After doing a quick search and reading through forum posts, I found out that web browsers generally prohibit giving video and audio permissions to websites accessed through insecure connections. This makes sense for security reasons, but it’s very annoying when you’re developing locally, since you typically don’t have a TLS certificate and thus no HTTPS connection (but HTTP instead).
What’s even more annoying was that I only discovered this later when I tried to test the functionality on my phone, since browsers typically permit giving permissions over HTTP if the address is localhost. Therefore, the permissions just stopped showing up when I used my phone to access the local server hosted on my laptop through its local IP address (which would not be localhost).
Trying to use self-signed certificates
“Okay,” I said to myself. “If we’re going to need HTTPS to test this functionality on the phone, then let’s just get HTTPS”. One of the solutions that came up was to just temporarily use a self-signed certificate, and conveniently Phoenix does have a generator for that called phx.gen.cert, and even a guide on how to get HTTPS in development. “Okay great,” I said to myself. “Crisis averted!”
Well… nope! Despite these self-signed certificates allowing for a HTTPS connection, modern browsers know these certificates are not trustable since they are self-signed. Thus, they would still treat the connection as an insecure one just like a HTTP connection, and so it will still not allow you to give camera and microphone permissions to the website.
I tried to dig into possible workarounds for this, since I thought it would be possible to just tell the browser that “hey, it’s okay; it’s only a local server and it’s trustable”. But well… also nope! I only found unofficial and hacky ways to do this which were quite convoluted through e.g. this StackOverflow thread, and that was only for Chrome. I tried some of those methods and they didn’t even work anymore. I realised I would have to do this for Firefox and Safari, and given how strict Apple is with permissions on Safari, I didn’t have much hope. At that point I realised that this approach was not going to work. I needed another way.
That StackOverflow thread I linked has 2.1 million views. 2.1 million! And it was asked all the way back in 2011, over a decade ago at the time of writing. That’s a lot of people facing the same problem as me. And typically, when there is a problem that common without a clear solution, someone would eventually make a solution for it, especially since this has been a problem for a long time.
Setting up a local certificate authority
Fortunately in this case, I did find a solution someone else made called mkcert. Basically, this tool does it the “proper” way by setting up a new local certificate authority (CA) and adding it to the list of trusted CA’s in a device (called a “trust store”). All I had to do was to get the root CA key .pem file it generated and send that over to my phone. I tested it on Chrome, Firefox, Brave, and Safari on my laptop, and also Safari and Chrome on my phone, and it seemed to all work.
To setup a local CA, I followed the README.md of the mkcert project, which more or less involved the following steps:
- I installed
mkcerton my laptop using the installation steps for my OS. - I ran
mkcert -installto setup a local CA on my machine. - Although the previous step already added the local CA to my machine’s list of trusted CA’s, I still had to add it to my phone to access the website from it. To do this, I took the
rootCA.pemfile thatmkcertgenerated (which is located in the path specified in the output of the commandmkcert -CAROOT), and then “installed” it on my phone (i.e., adding it to its trust store).
Creating and using the certificates
After setting up a local CA, the last thing I had to do was to create a certificate from this CA and ask the app to use it. Fortunately, I didn’t have to create the certificate manually since mkcert generates a certificate and key for a list of hostnames and IP addresses passed into it. For example, running mkcert localhost 127.0.0.1 creates a certificate and key for those two addresses. And so using this command, I generated a certificate and key for the local IP address of my laptop, which would be used to access the website from my phone.
Once these are generated, I added them to the Phoenix LiveView app by adapting the guide on how to get HTTPS in development I mentioned previously. This involved moving the certificate and key files into /priv/cert/ inside of the project and adding the following lines to the config/dev.exs file, where I replaced the certificate and key file names appropriately. Importantly, I added this in the dev.exs file and not the production nor the general configuration files because the local HTTPS setup only applies to the development environment, not the production environment.
config :my_app, MyAppWeb.Endpoint,
# ...
https: [
ip: {0, 0, 0, 0},
port: 4001,
cipher_suite: :strong,
keyfile: "priv/cert/example.com.pem",
certfile: "priv/cert/example.com-key.pem"
]
As seen in the config, I could then can access the app using HTTPS through the port 4001 instead of the typical 4000 for HTTP. Note also that I’ve bound the IP to the IPv4 loopback address by setting ip: {0, 0, 0, 0} so that I could access the website from other machines like my phone.
And that’s it! That’s how I got HTTPS working locally on a Phoenix LiveView web app.
Concluding remarks
In summary, my journey with getting HTTPS working locally on a Phoenix LiveView app started with discovering that I needed it in the first place to test the video and audio permissions on my phone. Then I went searching for a solution, starting with using self-signed certificates which didn’t end up working, and finally using mkcert to set up a local CA and issuing my own certificates and keys from such CA using mkcert.
There are plenty of steps to get this working, which is one of the reasons why I was asked by Sav, one of the lead devs of Sanico Software, to write this blog post as documentation for our team and others who might be trying to achieve the same thing. Even better, Sav takes this idea of documentation one step further by writing a command-line interface (CLI) tool to run those commands for you and to tell you the rest of the things you have to do to set this up (e.g. getting the root CA certificate file on your phone). Isn’t that cool?
And finally, as the mkcert readme notes, managing our own CA can be a huge pain with a lot of manual effort. Often with more low-level tasks, it’s best to just use whatever someone with more knowledge in it has already made instead of trying to re-create a worse and potentially dangerous (insecure) wheel. This is especially true when it comes to matters in web security and cybersecurity more generally, as the potential damage that can be done is great. For example, the readme notes to not share the rootCA-key.pem file with anyone, since it “gives complete power to intercept secure requests from your machine”. Phew; thank you for letting me know!
