github-readme-streak-stats: self-hosted streak cards for a GitHub profile README
🔥 Stay motivated and show off your contribution streak! 🌟 Display your total contributions, current streak, and longest streak on your GitHub profile README
At a glance
- What is it?
- A PHP service that renders total contributions, current streak and longest streak as an SVG. The hosted endpoint is the quick path; the GitHub Action and the Docker image are what you use when you want the card to stop depending on someone else's server.
- Who is it for?
- Adopt it if you want a streak card on a profile README and you are willing to either accept the hosted endpoint or run the PHP 8.3 Apache image yourself; the GitHub Action path is the one to start with, because it writes a static SVG into your profile repository and removes the runtime dependency. Do not adopt it if you need contribution history beyond three aggregate numbers, or if you cannot run PHP 8.3 or a container.
- Can I use it commercially?
- Yes. MIT 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 5 days ago.
- What is it written in?
- Mainly PHP, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 27, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What the streak card actually shows
A GitHub profile can show a contribution graph, but it does not give you a number for how many days in a row you have committed. This project fills that gap with one SVG that carries three figures: total contributions, current streak, and longest streak. The README describes it as a way to "Display your total contributions, current streak, and longest streak on your GitHub profile README", and the only required parameter is `user`, the GitHub username to show stats for.
The audience is narrow and obvious: people who maintain a profile README and want a dynamic image in it. If your README is static prose, this adds a moving part for no reason. If you already use a stats card that shows commit counts, languages or repository counts, this is complementary rather than a replacement, because it reports streaks and totals rather than repository metadata.
One design decision is worth flagging. The card is a single SVG, so it embeds in Markdown as an image and needs no JavaScript on the profile page. That is why it works inside GitHub's README renderer, which strips scripts. The cost is that the image is only as fresh as whatever generated it: a hosted request renders on demand, while a generated file is only as current as the last workflow run.
Two delivery models: hosted endpoint versus generated file
The project offers two ways to get the card, and they have different failure modes.
In the web deployment model, the README is pointed at the hosted service at streak-stats.demolab.com with a `user` query parameter. The image is rendered when someone loads your profile page, so it is always current, but it depends on that host being up and on its rate limits. The README itself says self-hosting is "recommended ... more better reliability", and the phrasing in the repository shows the project treats the public endpoint as a convenience rather than a guarantee.
In the GitHub Actions model, a workflow runs on a schedule, generates an SVG file into your profile repository, and commits it. Your README then references the local file. Nothing renders at page load, so there is no third-party dependency at read time, but the card is a snapshot: the README's example workflow uses a cron of `0 3 * * *`, so the numbers are up to a day stale by design.
The server side is a PHP application served by Apache from the `src/` directory, per the Dockerfile, which configures `DocumentRoot /var/www/html/src` and sends `image/svg+xml` for requests ending in `.svg`. Caching is on by default; the `.env.example` lists `DISABLE_CACHE=true` as optional and notes it is "not recommended, may cause rate limit issues", which tells you the cache exists to protect the GitHub API budget rather than to speed up rendering.
Installing it with the GitHub Action and a first card
The Action path needs no server. Create `.github/workflows/streak-stats.yml` in your profile repository, which for a profile README is the repository named after your username (`USERNAME/USERNAME`). The README gives this workflow, which checks out the repo, generates the SVG, and commits it:
name: Update streak stats
on:
schedule:
- cron: "0 3 * * *"
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v7
- name: Generate streak stats
uses: DenverCoder1/github-readme-streak-stats@v1
with:
options: user=${{ github.repository_owner }}&theme=default&disable_animations=true
path: profile/streak.svgThe `options` value is a query string, and `user` is the only required field in it. `path` is where the SVG is written, here `profile/streak.svg`. After the workflow runs, that file appears in the repository. Then reference it from `README.md` as a relative image:
<a href="https://git.io/streak-stats"><img src="./profile/streak.svg" alt="GitHub Streak" /></a>If you only want to see the card once before wiring up a schedule, the quickest check is the hosted form, pasted into any Markdown file and viewed on GitHub:
[](https://git.io/streak-stats)Replace `DenverCoder1` with your own username. If the image loads, the public endpoint can read your public contributions; if it renders empty or fails, that is the same class of problem people search for as "github readme streak stats not working", and the usual cause is the upstream API refusing the request rather than the Markdown being wrong.
Private contributions, tokens and the whitelist variable
Public contribution counts are the default. Private contributions require a token, and the README is explicit about the split: for the Action, a Personal Access Token with no scopes is enough for the basic setup, while private contributions need a token with the `repo` scope. The token is passed as an input rather than placed in the workflow file:
- name: Generate streak stats
uses: DenverCoder1/github-readme-streak-stats@v1
with:
options: user=${{ github.repository_owner }}&theme=default&disable_animations=true
path: profile/streak.svg
token: ${{ secrets.STREAK_STATS_TOKEN }}The README's instructions say to store the value under Settings > Secrets and variables > Actions as a new repository secret named, for example, `STREAK_STATS_TOKEN`, and then reference that name in the workflow. The same secret name is used in the Action-based instructions and the self-hosted instructions, so a fork that renames it has to change both places.
On a self-hosted instance the equivalent is an environment variable. The `.env.example` file ships with `TOKEN=ghp_example`, and the Dockerfile passes `TOKEN` and `WHITELIST` through Apache with `PassEnv`. The `WHITELIST` variable is documented as an optional comma-separated list of GitHub usernames, and the comment states that only those users' streaks will be displayed. That is the closest thing to access control the project documents, and it is a username filter, not authentication: anyone can request a whitelisted user's card.
Self-hosting: Docker, PHP 8.3 and what the image pins
The repository ships a Dockerfile, and it is more opinionated than a typical PHP image. The base is `php:8.3-apache` pinned by digest, with a comment stating that PHP 8.4 is not supported yet. That comment is the single most useful line in the file for anyone planning an upgrade path: if you build on 8.4, you are outside what the project says it supports.
The image installs `git`, `unzip`, `libicu-dev`, `inkscape`, `fonts-dejavu-core` and `curl`, then configures and installs the `intl` extension. Inkscape and the DejaVu fonts are there because the SVG is produced by a rendering pipeline that needs them, not because the request path shells out to a browser. Composer is copied from the official composer image and dependencies are installed with `composer install --no-dev --optimize-autoloader --no-scripts` from the committed `composer.lock`, so builds are reproducible against that lock file rather than against whatever is current on Packagist.
Apache is configured with `a2enmod rewrite headers`, `ServerTokens Prod`, `ServerSignature Off`, and a virtual host whose document root is `/var/www/html/src` with `Options -Indexes`. For `.svg` responses it sets `Content-Type: image/svg+xml` and a restrictive `Content-Security-Policy` of `default-src 'none'; style-src 'unsafe-inline'; img-src data:`. It also sets `Access-Control-Allow-Origin "*"` on every response. That wildcard is deliberate, since the card is meant to be embedded anywhere, but it does mean a self-hosted instance is a public, unauthenticated renderer unless you put something in front of it.
Where it stops being the right tool
The card reports three aggregates. It does not give you a per-day breakdown, a per-repository breakdown, or a history you can query. If your goal is to analyse contribution patterns rather than display a badge, the rendered SVG is the wrong output format entirely: you would be parsing an image to recover numbers the project never exposes as data.
The hosted endpoint is also a shared resource. The README recommends self-hosting for reliability, and the `.env.example` warns that disabling the cache may cause rate limit issues. Both point at the same constraint: the service spends GitHub API quota per uncached request. A profile that gets a lot of views against a single self-hosted instance without a cache is a plausible way to exhaust that quota.
There is a subtler mismatch in the Action setup. The workflow commits a generated file to your profile repository, so your contribution graph gains a commit every day from `github-actions[bot]`. If the point of the card is to display a streak, a daily automated commit is a slightly awkward thing to have inside the same account's history. The README does not discuss this interaction, and it is worth deciding whether you care before you enable the schedule.
Finally, the project is PHP. If your infrastructure is Node or Go and you have no PHP runtime, the hosted endpoint or the Action are your only realistic options; running the Docker image means operating an Apache and PHP stack for one image endpoint.
Alternatives and how they differ in approach
The most common comparison is with github-readme-stats, which appears in the related searches alongside this project. The difference is scope rather than quality. github-readme-stats renders cards built from repository and language metadata: stars, commit counts, top languages, and similar aggregates. github-readme-streak-stats renders contribution timing: total contributions, current streak, longest streak. They answer different questions, and a profile can carry both without redundancy.
A second alternative is to skip the service and use the GitHub contributions graph that the profile page already renders. That requires no token, no workflow and no third-party host, and it is the right answer if you only want a visual of activity. What it does not give you is a number for the current streak, which is the specific thing this project exists to produce.
A third option, for anyone uncomfortable with a hosted renderer, is to generate the SVG yourself from the GitHub API. That is more work, but it removes both the hosted endpoint and the container. The project's own GitHub Action is essentially a packaged version of that idea: it runs the renderer in your CI and commits the result, so the only external dependency at read time is your own repository.
Maintenance, licensing and what to check before upgrading
The repository is not archived, and the last push was on 2026-09-17, the same day as the v1.8.0 release. The releases listed are v1.6.0 on 2026-02-07, v1.7.0 on 2026-06-02 and v1.8.0 on 2026-09-17, which is a cadence of roughly one minor release per quarter. The Action is referenced by the major tag `@v1`, so a workflow that pins `@v1` picks up new minor releases without a change; pinning a full version instead trades automatic updates for predictability.
Licensing is MIT, per the repository metadata, with a `LICENSE` file at the top level. MIT is permissive and does not impose copyleft obligations on your own repository, but the licence covers the code, not the GitHub data the service reads, and it says nothing about the availability of the hosted endpoint. If you fork and run it, you are responsible for the token you supply and for whatever the wildcard CORS header exposes on your host.
The upgrade costs that matter are the pinned base image and the lock file. The Dockerfile pins `php:8.3-apache` by digest and states 8.4 is not supported yet, so a base image bump is a deliberate change rather than a routine one. Dependencies come from the committed `composer.lock`, which means a `composer update` is a separate decision. The `package.json` in the repository pins Node `22.x` and lists only Prettier and its PHP plugin as dev dependencies, so the Node side is formatting, not runtime.
Editorial conclusion
Adopt it if you want a streak card on a profile README and you are willing to either accept the hosted endpoint or run the PHP 8.3 Apache image yourself; the GitHub Action path is the one to start with, because it writes a static SVG into your profile repository and removes the runtime dependency. Do not adopt it if you need contribution history beyond three aggregate numbers, or if you cannot run PHP 8.3 or a container. Before committing, verify the token scopes you actually need: the README says no scopes are required for public data, and the repo scope only for private contributions, and confirm which theme name you want from docs/themes.md rather than guessing at a color.
Frequently asked questions
What is a GitHub streak?
In this project it is the run of consecutive days with contributions, reported as a current streak and a longest streak on the card, alongside total contributions. The README describes the card as displaying total contributions, current streak and longest streak.
How do I show github-readme-streak-stats in my README?
Copy the Markdown image line into your profile README and replace the value after `?user=` with your GitHub username. Alternatively, run the provided GitHub Actions workflow to write an SVG into your repository and reference that file with a relative image path.
Why is my github-readme-streak-stats card not working?
The README does not document a troubleshooting list. The two configurations it does describe are the hosted endpoint and the GitHub Action, and the README recommends self-hosting for better reliability, which suggests the public endpoint is the less dependable of the two.
Which parameters does github-readme-streak-stats require?
Only `user` is required. Everything else is optional, and if a `theme` is specified, any color customizations are applied on top of the theme and override the theme's values.
How do I show private contributions with github-readme-streak-stats?
You need a Personal Access Token. The README says a token with no scopes is enough for the basic Action setup, while private contributions require the `repo` scope, and the token should be stored as a repository secret rather than written into the workflow file.
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/denvercoder1-github-readme-streak-stats)