devcert: a local certificate authority so your dev server stops warning the browser
Local HTTPS development made easy
At a glance
- What is it?
- devcert is a TypeScript package that creates its own certificate authority, signs certificates for your local domains with it, and installs that authority into the operating system trust store. It suits developers who need a real HTTPS origin on a custom domain without clicking through browser warnings, and it reaches into system keychains and /etc/hosts to do it.
- Who is it for?
- devcert fits a Node developer who needs cookies, service workers or a secure context on a custom local domain, because it produces a certificate that the browser actually trusts rather than a self-signed one you have to click past on every project.
- Can I use it commercially?
- Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
- Is it still maintained?
- Activity is slowing. The repository last received commits 7 months ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 6, 2026, and from our analysis. They are not legal advice.
Star history
- davewasmer/devcert1,309 ★ · +2
| Repository | 2026-10-20 | 2026-11-06 |
|---|---|---|
| davewasmer/devcert | 1.3k | 1.3k |
2 Stars in the last 17 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
The problem is the browser warning, not the encryption
Running a local HTTPS server is described in the documentation as usually sucking, because there is a range of approaches and each has its own tradeoff. The common one, self-signed certificates, means having to ignore scary browser warnings for each project.
That framing is the key to what devcert does. The encryption was never the problem. The problem is that a self-signed certificate is signed by nobody the browser trusts, so every fresh origin produces a full-page interstitial that you have to click through, and a browser that treats that as a habit teaches users to click through warnings everywhere else too.
The fix is to become a certificate authority. The usage is short:
let ssl = await devcert.certificateFor('my-app.test');
https.createServer(ssl, app).listen(3000);The returned object carries a key and a certificate, which is what `https.createServer` expects, and the promise resolves once the certificate is ready. Open the URL and the page loads with no warnings.
One behaviour is worth knowing from the start, because it is the kind of thing that causes confusion later. Certificates are cached by name, so two calls for `certificateFor('foo')` return the same key and certificate. The first call does the work, and later calls are fast.
The project is written in TypeScript, the package publishes a build in `dist/`, and the manifest lists a `build` script running `tsc` and a `prepublishOnly` script that builds before publishing.
A local CA is created and pushed into OS keychains
When it installs or upgrades, devcert creates a self-signed certificate authority and uses it to sign every certificate it creates. That authority is then offered to the operating system keychains on macOS, Linux and Windows, which is what makes the browser stop complaining.
The interesting wrinkle is that some HTTP clients do not consult the operating system keychain at all. Firefox and NodeJS itself are the two named in the documentation, and both keep their own trusted certificate list. So a certificate that is perfectly valid for Chrome on macOS can still be rejected by Node in the same project, which is exactly the sort of thing that eats an afternoon.
Two options exist for that case, and they put the CA material into the returned object so the calling program can decide what to do with it.
`getCaPath` set to `true` adds a `caPath` property holding the file path of the certificate authority file. That path is what you feed to trust stores that accept a path argument, and the documentation names NodeJS's own `NODE_EXTRA_CA_CERTS` environment variable as the example.
`getCaBuffer` set to `true` adds a `ca` property holding the UTF-8 encoded contents of the certificate authority file, for trust stores that do not read OS settings, with the same NodeJS and Firefox examples in mind.
The distinction between the two is small but practical. A path is what a command line tool can be pointed at, a buffer is what you have to write somewhere yourself before handing it over.
Two options decide how much of your machine devcert touches
The option that most changes the footprint of the tool is `skipHostsFile`.
By default, when you request a certificate for a custom domain, anything other than `localhost`, devcert tries to modify your system so requests for that domain go to your local machine instead of the real one. It does this by editing the `/etc/hosts` file.
With `skipHostsFile` set, that step is skipped. The consequence is stated directly: if you ask for a certificate for `my-app.test` and have no other DNS redirect in place, you cannot reach the app at that hostname, because the machine has no way to know it should resolve to itself.
The reasoning behind it is about how certificates work rather than about devcert. SSL certificates are issued for domains, so a certificate for `my-app.test` does not say `localhost`, and hitting `localhost` with that certificate served will not work either. The domain you serve on and the domain in the certificate have to be the same name.
The second option, `skipCertutil`, controls whether devcert installs the `certutil` tooling, a package used to automate installing SSL certificates in specific situations, namely Firefox on every operating system and Chrome on Linux only. Normally devcert installs it if it is needed and not already present. Passing `skipCertutil: true` avoids that.
What changes when you skip it is documented per platform. On Firefox, which is pointed at as the easy case, there is a point-and-click wizard for importing and trusting a certificate, so devcert opens Firefox and kicks off that wizard for you, and the reminder is that you only need to do this once per machine. On Chrome on Linux the situation is the opposite: the documentation says the only way to get Chrome to trust an SSL certificate on Linux is through the `certutil` tooling, with no manual process, so passing `skipCertutil: true` there means devcert certificates will not be trusted by Chrome.
certutil installs differently on every platform, and Windows is manual
The `certutil` tooling is installed in operating system specific ways, and the list is short enough to memorise.
On Mac the command is `brew install nss`. On Linux it is `apt install libnss3-tools`. On Windows it is marked as not applicable, with the reason given plainly: there is no easy, hands-off way to install certutil on Windows, so devcert falls back to the Firefox wizard approach described earlier.
That Windows asymmetry is the one to plan around. The trust store registration still works there, since devcert does register the authority with the Windows keychain, but the part of the process that automates Firefox and Chrome on Linux has to be done by hand.
The dependency list in the package manifest is a good indication of what the tool actually does under the hood. `sudo-prompt` is how it asks for administrator rights when it needs to edit the hosts file or install a system package, `password-prompt` handles the interactive prompts, `application-config-path` and `configstore` are where the CA and its password are kept, `openssl` is reached through the `openssl-configurations/` directory in the repository, `is-valid-domain` validates the names you ask for, `command-exists` checks whether `certutil` is present, `get-port` finds a free port, `tmp` handles temp files, and `lodash`, `glob`, `mkdirp` and `rimraf` do the file management. The `eol` dependency is there to deal with line endings, which matters on a tool that edits `/etc/hosts` across three operating systems.
SAN certificates let one cert cover a multi-tenant app
If you are developing a multi-tenant app or have many apps locally, you can generate a certificate that also uses the Subject Alternative Name extension by passing an array of domains instead of a string:
let ssl = await devcert.certificateFor([
'localhost',
'local.api.example.com',
'local.example.com',
'local.auth.example.com'
]);
https.createServer(ssl, app).listen(3000);This is the mechanism that makes per-tenant subdomains work in local development. A single certificate carrying four names means the browser accepts the same origin set whether you are at the apex or at one of the service subdomains, and you avoid generating and re-trusting a certificate every time you add a service.
The Docker path follows the same idea. The documentation suggests installing devcert into a base folder in your home directory and generating certificates for all of your local Docker projects, pointing at an issue for the comments and caveats. The general script writes the key and certificate out to files:
const fs = require('fs');
const devcert = require('devcert');
devcert.certificateFor([
'localhost',
'local.api.example.com'
])
.then(({key, cert}) => {
fs.writeFileSync('./certs/tls.key', key);
fs.writeFileSync('./certs/tls.cert', cert);
})
.catch(console.error);The certificate files are then copied into the Docker project, and the folder is added to `.gitignore`, since a private key has no business in version control. The same files load into a Node https server inside the container, and the documentation notes the same works with webpack dev server or similar technologies.
A local CA on your machine is a real security decision
This is the part of the tool that deserves a paragraph of its own, because devcert is not just generating a certificate, it is installing a certificate authority into your operating system trust store. Any certificate that authority signs will be trusted by your browser and by most software on the machine, which is the entire point and also the risk.
The exposure is local. The CA is generated on your machine, the private key never leaves it, and the trust store entry is scoped to that machine. But an authority that can sign a certificate for any name is exactly the capability an attacker with access to that key would want, which is why the key material matters. The package manifest shows a `password-prompt` dependency and a `configstore`-based configuration path, which is how the CA and its password are held locally.
The second exposure is the hosts file. Redirecting a real-looking domain to your own machine is fine while you are developing, and it is also the shape of a man in the middle attack. Nothing about devcert makes that dangerous, but it means the tool's state on your machine is worth understanding before you hand the laptop to someone else.
The licensing question is simple on the project side and worth checking on yours. The package manifest declares the MIT licence, and the repository metadata does not record a separate licence file, so the MIT declaration in the manifest is the statement to rely on. That covers the tool. It does not cover anything you generate with it.
On maintenance, the picture is mixed and worth stating. The last push was on 2026-04-02 and the repository is not archived, but the only tagged release is v1.1.1 from 2020-06-10, while the package manifest carries version 1.2.3, which means the published npm version and the released tags are not aligned. The manifest also lists a `test` script whose body is `echo "Ha." && exit 1`, which is a way of saying the repository has no test suite you can run, and the dependency list is pinned to older major versions, with TypeScript 4.3.2 in the dev dependencies and `@types/node` at 8.x. For a tool that edits system files, that is a reasonable thing to weigh before making it part of a team's setup script.
Editorial conclusion
devcert fits a Node developer who needs cookies, service workers or a secure context on a custom local domain, because it produces a certificate that the browser actually trusts rather than a self-signed one you have to click past on every project. Skip it if you only ever serve from `localhost`, where a self-signed certificate is enough, and skip it on a shared or managed machine, because the tool installs a certificate authority into the system trust store and edits the hosts file, which is a level of machine-level trust a corporate laptop may not permit. Before you install it, read the `skipHostsFile` and `skipCertutil` options and decide which system changes you accept, and check the maintenance picture, since the last push was on 2026-04-02 and the only tagged release is v1.1.1 from 2020-06-10 while the package manifest carries version 1.2.3.
Frequently asked questions
What does devcert do differently from a self-signed certificate?
It creates its own certificate authority, signs certificates for your local domains with it, and tries to register that authority with the operating system keychains, so the browser trusts the result without a warning prompt.
How do I make Node trust a devcert certificate?
Request the certificate with `getCaPath: true` or `getCaBuffer: true` and use the returned `caPath` or `ca` value. The documentation names NodeJS's `NODE_EXTRA_CA_CERTS` environment variable as the way to point Node at a CA file path.
What does skipHostsFile do in devcert?
It stops devcert from editing the `/etc/hosts` file to redirect your custom domain to your machine, which means you need some other DNS redirect in place for the app to be reachable at that hostname.
Should I use skipCertutil on Linux with Chrome?
No. The documentation says the only way to get Chrome to trust an SSL certificate on Linux is through the `certutil` tooling, so passing `skipCertutil: true` there means devcert certificates will not be trusted by Chrome. The tooling installs with `apt install libnss3-tools` on Linux and `brew install nss` on Mac.
Can one devcert certificate cover several local domains?
Yes. Passing an array of domains to `certificateFor` produces a certificate using the Subject Alternative Name extension, which is how a multi-tenant app can use one certificate for `localhost` and several service subdomains.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/davewasmer-devcert)Star history chart for your README
[](https://hysenlabs.com/projects/davewasmer-devcert)Was this useful?