pshtt: scanning an estate for HTTPS best practices
Scan domains and return data based on HTTPS best practices
At a glance
- What is it?
- pshtt is a Python tool that probes four endpoints per domain and writes a row of HTTPS findings per host, so an organisation can measure how much of its web presence is encrypted correctly. It comes from CISA with 18F, and its output is a report for people, not a scanner that exploits anything.
- Who is it for?
- pshtt fits a security or infrastructure team that needs a repeatable measurement of HTTPS posture across a large domain list, for a report rather than for a fix. It does not fit a team that wants a continuous TLS configuration scanner or an online TLS grading service.
- Can I use it commercially?
- Yes. CC0-1.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 36 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem it was built for: measuring a federal estate, not testing one host
pshtt, pronounced pushed, is a tool to scan domains for HTTPS best practices, and it saves its results to a CSV or JSON file.
The origin explains the design. pshtt was developed to push organisations, especially large ones like the US Federal Government, to adopt HTTPS across the enterprise. Federal agencies must comply with M-15-13, a 2015 memorandum from the White House Office of Management and Budget, and BOD 18-01, a 2017 directive from the Department of Homeland Security, which require federal agencies to enforce HTTPS on their public web services. The readme notes that much has been done but that there is more yet to do.
That framing sets the expectations correctly. This is an inventory tool. It takes a list of domains, probes them consistently, and produces a table you can hand to someone who owns the domains. It is not a TLS configuration scanner that enumerates cipher suites, and it is not a vulnerability scanner.
The project is a collaboration between the Cyber and Infrastructure Security Agency's National Cybersecurity Assessments and Technical Services team and the General Service Administration's 18F team, with contributions from NASA, Lawrence Livermore National Laboratory and various non-governmental organisations.
The audience is a security team doing a periodic assessment across thousands of hosts, which is why the output is a file rather than a screen.
Installing it, and the two ways to run it
The installation is a single pip command, and the readme gives two ways to run it.
Installed as a module:
pip install pshttand then invoked directly:
pshtt example.com [options]Running directly from the repository, without installing, means installing the requirements first:
pip install -r requirements.txtand then running it as a module via python -m:
python -m pshtt.cli example.com [options]The requirements file is interesting in its own way. It contains a single entry, an editable install of the project itself, with a comment explaining that any additional requirements should be added to the dependencies entry in pyproject.toml instead. That is a deliberate choice to keep one source of truth for dependencies, and it also means the file is not a lockfile of pinned versions.
The packaging is plain setuptools with PEP 621 metadata in pyproject.toml, and the setuptools requirement is conditional per Python version, because 61.0.0 was the first version to offer the full backend, 77.0.0 is the first to support license expressions under PEP 639, and setuptools 68.0.0 was the last to support Python 3.7 with 75.3.2 the last for Python 3.8.
The project classifies itself as Development Status:: 4 - Beta, and the licence is CC0-1.0, which is worth noting because CC0 is a public domain dedication rather than a conventional software licence, and it includes an express disclaimer of warranties.
Four probes per domain, and the CLI options that shape a run
A domain is checked on four endpoints: http://, http://www, https:// and https://www. Four probes per host is a small amount of traffic, and it is the reason the tool can be run across a large estate without being disruptive.
The usage lines show the three input shapes:
pshtt [options] DOMAIN...
pshtt [options] INPUT
pshtt dhs.gov
pshtt --output=homeland.csv --debug dhs.gov us-cert.gov usss.gov
pshtt --sorted current-federal.csvIf INPUT ends with .csv, the domains are read from the first column of the CSV, which is how a previous run's output becomes the next run's input. The CSV output is always written to disk unless --json is specified, defaulting to results.csv.
The options are compact:
-h --help Show this message.
-s --sorted Sort output by domain, A-Z.
-o --output=OUTFILE Name output file. (Defaults to "results".)
-j --json Get results in JSON. (Defaults to CSV.)
-m --markdown Get results in Markdown. (Defaults to CSV.)
-d --debug Print debug output.
-u --user-agent=AGENT Override user agent.
-t --timeout=TIMEOUT Override timeout (in seconds).
-c --cache-third-parties=DIR Cache third party data, and what directory to cache it in.
-f --ca-file=PATH Specify custom CA bundle (PEM format)The --output option takes a base name rather than a path, which is a small thing that tells you the tool is built around one output file per run. The --sorted flag sorts by domain A-Z, which matters when you are diffing two runs of the same estate, and the --markdown option is the one to reach for when the output goes into a ticket rather than a spreadsheet.
Behind a proxy: supplying your own CA bundle
The CA bundle section exists because the tool is often run from inside an agency network, and that is a real constraint rather than an edge case.
By default, pshtt relies on the root CAs trusted in the Mozilla root store. If you work behind a corporate proxy or have your own certificates that are not publicly trusted, you can specify your own CA bundle, in PEM format:
pshtt --ca-file=/etc/ssl/ca.pem server.internal-location.govThat option interacts directly with the results. The output includes a column for whether a domain is custom truststore trusted, which is true if its canonical endpoint has a certificate trusted by the custom truststore, alongside the column for publicly trusted. So a domain behind a proxy with an internal certificate can be reported as custom truststore trusted and not publicly trusted, which is a factual statement about the certificate rather than a failure.
The --cache-third-parties option is related in spirit. It caches third party data and takes the directory to cache it in, which matters for a large scan because repeatedly fetching the same third party endpoints is both slow and noisy.
There is also a --suffix-cache option mentioned in passing, in the description of how the base domain is calculated, since the Public Suffix List is downloaded and factored in to work out the base domain, and caching that list avoids fetching it on every run.
Reading the results: the columns that decide the verdict
The output is a set of columns, and the definitions are where the tool's judgement actually lives.
The domain and redirect columns come first. Domain is the domain being scanned. Base Domain is the base domain of Domain, so for sub.example.com it is example.com, and pshtt downloads and factors in the Public Suffix List to calculate it. Canonical URL is one of the four endpoints, chosen as a judgment call based on the observed redirect logic of the domain. Live means any endpoint is live, HTTPS Live means any HTTPS endpoint is live, and HTTPS Full Connection means an HTTPS endpoint pshtt could make a full TLS connection to.
Then the HTTPS columns. Valid HTTPS means the domain responds on port 443 at the hostname in its Canonical URL with an unexpired valid certificate for the hostname, which can be true even when the Canonical URL uses HTTP. HTTPS Publicly Trusted means the canonical endpoint has a publicly trusted certificate, and HTTPS Custom Truststore Trusted means it is trusted by the custom truststore.
The two that most often get misread are Defaults to HTTPS and Strictly Forces HTTPS. A domain defaults to HTTPS if its canonical endpoint uses HTTPS. A domain strictly forces HTTPS if one of the HTTPS endpoints is live and if both HTTP endpoints are either down or redirect immediately to any HTTPS URI, with an HTTP redirect allowed to go to HTTPS on another domain as long as it is immediate, and a domain with an invalid cert still able to be enforcing HTTPS.
That distinction is the whole point of the tool. A domain that redirects to HTTPS but also happily serves HTTP content is not enforcing HTTPS, and a scan that only recorded the redirect would miss it.
The error columns are equally concrete: HTTPS Bad Chain when either HTTPS endpoint contains a bad chain, HTTPS Bad Hostname when hostname validation fails, and HTTPS Expired Cert when either endpoint has an expired certificate.
What it does not do, and where a different tool fits
It is worth being clear about the boundary, because a tool named after a security scanner invites assumptions it does not meet.
pshtt probes four URLs per domain and reports a small set of derived HTTPS properties. It does not enumerate cipher suites or protocol versions, it does not check for certificate revocation problems in detail, it does not look for mixed content on the page, and it does not attempt to change anything. The readme also does not describe a scan scheduling mode, a database of historical results, or an authentication scheme for the tool itself.
The alternatives split into three different shapes. A general TLS configuration scanner gives you the protocol and cipher inventory that pshtt deliberately leaves out, and it is the right tool if the question is whether a server offers a weak cipher. An online TLS grading service gives a quick verdict on one hostname, with no bulk scanning and no CSV output, which is the opposite of what you want for an estate. A configuration management system for the actual web servers fixes the problem rather than measuring it, and it needs a different kind of access.
pshtt sits where the others do not: a repeatable, low-traffic measurement across thousands of hosts that produces a spreadsheet somebody can act on, at a cadence of quarterly or per-release. Its value is the consistency of the columns and the fact that a run can be repeated and compared.
The one caveat is that the judgements are encoded, not argued. Canonical URL is described as a judgment call, and the forcing rules are stated precisely, but the interpretation of those columns for a specific agency or domain is still a human decision, and the tool's output is the input to that decision rather than the end of it.
Cadence, licence and the state of the repository
The maintenance picture is healthy and the release cadence is regular. The recent tags are v0.7.6 from 2026-07-09, v0.7.5 from 2026-06-24 and v0.7.4 from 2026-06-23, which is three patch releases in a fortnight in the middle of 2026. The repository is not archived, and the last push was on 2026-09-18. The default branch is develop, which is the usual convention for a project where the released version is tagged from a release branch rather than from the head.
The licence is CC0-1.0, a public domain dedication rather than a permissive software licence with a warranty disclaimer. For a tool that outputs measurement data about other people's domains, that is a defensible choice, and it means there is nothing to comply with when you redistribute the results. It is a description of how the licensing is arranged and not legal advice.
The repository also carries the signs of a project with real infrastructure behind it. There is a .pre-commit-config.yaml and a .yamllint configuration, a .coveragerc and Coveralls badge, a CodeQL workflow badge, a .prettierignore for formatting the YAML, and a .mdl_config.yaml for markdown linting. The gce-scripts/ directory points at Google Cloud deployment automation, and there are separate requirements-dev.txt and requirements-test.txt files alongside requirements.txt, plus a bump-version script and a tag.sh, which together describe a project with a defined release ritual.
The one thing to note is the beta classification in the pyproject metadata. The tooling is careful, the release rhythm is steady, and the output format is stable enough to build a process on, but a 0.x version still signals that the interface may change.
Editorial conclusion
pshtt fits a security or infrastructure team that needs a repeatable measurement of HTTPS posture across a large domain list, for a report rather than for a fix. It does not fit a team that wants a continuous TLS configuration scanner or an online TLS grading service. Before adopting it, read the column definitions in the readme, since the difference between defaults to HTTPS and strictly forces HTTPS changes what a result means, and test the tool against a domain you already know the answer for before you trust its output on a thousand hosts.
Frequently asked questions
What does pshtt do?
It is a tool to scan domains for HTTPS best practices, saving its results to a CSV or JSON file. It was developed to push organisations, especially large ones like the US Federal Government, to adopt HTTPS across the enterprise, in support of M-15-13 and BOD 18-01.
How do I install and run pshtt?
Run pip install pshtt and then invoke pshtt example.com [options]. To run it from a checkout instead, install the requirements with pip install -r requirements.txt and run it as a module with python -m pshtt.cli example.com [options].
What does pshtt check on each domain?
It probes four endpoints, http://, http://www, https:// and https://www, and reports columns including Live, HTTPS Live, HTTPS Full Connection, Valid HTTPS, HTTPS Publicly Trusted, Defaults to HTTPS, Strictly Forces HTTPS, and error columns such as HTTPS Bad Chain, HTTPS Bad Hostname and HTTPS Expired Cert.
What is the difference between defaults to HTTPS and strictly forces HTTPS in pshtt output?
A domain defaults to HTTPS if its canonical endpoint uses HTTPS. A domain strictly forces HTTPS if one of the HTTPS endpoints is live and both HTTP endpoints are either down or redirect immediately to any HTTPS URI, where the redirect may go to HTTPS on another domain, and a domain with an invalid cert can still be enforcing HTTPS.
Can pshtt use my organisation's own CA certificates?
Yes. By default it relies on the root CAs trusted in the Mozilla root store, but the --ca-file option takes a custom CA bundle in PEM format for use behind a corporate proxy. The output records a HTTPS Custom Truststore Trusted column for certificates trusted by that custom truststore.
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/cisagov-pshtt)Was this useful?