HOTP vs TOTP: counter-based versus time-based codes
HOTP and TOTP are one HMAC machine with two moving parts. How counter-based and time-based codes differ per RFC 4226 and RFC 6238, and which one your accounts use.

Every six-digit code you have ever typed into a login box came out of the same machine. The machine takes a secret that you and the service both know, feeds it through HMAC-SHA-1 together with one other input, chops the 160-bit result down to a handful of digits, and shows you the remainder. Both of the standard one-time password algorithms — HOTP and TOTP — are this exact machine. The only difference is the second input. What moves?
In HOTP, a counter moves: every time you ask for a code, the number ticks up. In TOTP, time itself moves, and the code changes with the calendar whether anyone asks or not. Everything else people tell you about the difference — validity windows, desync risks, why a code dies at second 29 — follows from that single design choice.
The machinery both share
RFC 4226, which defines HOTP, gives the formula in one line: HOTP(K, C) = Truncate(HMAC-SHA-1(K, C)). K is the shared secret, a random key generated at enrollment and usually shown to you as a QR code. C is the moving factor. HMAC-SHA-1 mixes the two into 160 bits, and the Truncate step — dynamic truncation, in the RFC's vocabulary — picks 31 of those bits and reduces them to a six-digit number you can actually type.
Neither side ever transmits the secret. Your device computes a code from K and its moving value. The server computes its own code from the same K and the value it believes you are on. If the two match, you are in — and a six-digit guess has a one-in-a-million chance by luck, before rate limiting shuts guessing down after a handful of tries.
TOTP, defined by RFC 6238, is an extension of that algorithm rather than a new one. The RFC defines it in one line of its own: TOTP = HOTP(K, T), where T is an integer counting time steps. The machinery, the truncation, the six digits — all identical. What TOTP is covers the operational side. Which leaves the only real question: what should the moving factor be?
HOTP: a counter that moves when you do
In the HMAC-based one-time password algorithm, the moving factor is an eight-byte counter that starts at zero and increments once per code generated. Press the button on a keyfob, and the counter ticks from 40 to 41 and a new code appears. The previous code does not expire on any schedule — it simply stops being what the token would produce. As far as the algorithm is concerned, a generated code remains the token's current answer until the next press.
That has a subtle consequence on the server side, and RFC 4226 spells it out honestly in its resynchronization section: the server's counter advances only after a successful authentication, while the token's counter advances every time a code is requested. Generate five codes idly and use the fifth, and the server is four behind you. This is the desync problem, and it is the price of everything HOTP buys you.
TOTP: a clock that moves whether you do or not
The time-based one-time password algorithm replaces the event counter with a time counter: T equals the number of time steps since a starting point — the Unix epoch by default — divided by the step size. The RFC recommends a step of 30 seconds, chosen, in its words, as a balance between security and usability. Both sides perform the same division, round down, and run the identical HMAC-and-truncate machinery on the result.
The consequence is that codes now expire on a schedule. The code for step 47,893,291 is the token's answer for exactly 30 seconds, whether or not anything was pressed. No presses means no desync — your phone and the server can go years without exchanging a word, and as long as their clocks agree, their counters agree. The dependency moved from event bookkeeping to timekeeping, which is why a phone whose clock has drifted stops producing working codes. That failure mode dominates every authenticator support forum on earth, and it has its own field guide.
The trade-off, in one table
| HOTP (RFC 4226) | TOTP (RFC 6238) | |
|---|---|---|
| What moves | An event counter, one tick per code requested | The clock, one tick per time step — 30 seconds by default |
| Code lifetime | Until the next code is generated — minutes or hours in practice | One time step, roughly 30 seconds plus validation slack |
| Replay window | Longer — any unused code is still current | Short — an expired code is dead |
| Desync risk | Yes, from idle code generation | No counters, but clock drift can break it |
| Infrastructure demand | Nothing but a shared secret and a button | Synchronized clocks on both sides |
| Natural habitat | Event-based hardware keyfobs, some enterprise setups | Nearly every consumer authenticator app |
Read the table as a single exchange. HOTP spends its security window to buy clock independence — the token needs no oscillator, no battery drain keeping time, nothing but a counter and a button. TOTP spends clock dependence to buy a short exposure window — a code you disclose is worthless within a minute. Neither dominates the other, which is why both standards are alive more than eighteen years after publication.
Where each lives in the wild
TOTP won consumer 2FA outright. Google accounts, GitHub, banks, games, and password managers enroll you by QR code, and the code that appears is almost always time-based. Two reasons, both mundane. Phones have clocks — networked, continuously corrected clocks — so the one infrastructure demand of TOTP is free. And users understand type the number before it goes away as an instruction, in a way they never understood ask which code is current.
HOTP persists where buttons are cheaper than crystals. The event-based hardware token — a keyfob or card with one button and a screen — runs for years with no oscillator to power, which is why it still appears in enterprise deployments, industrial settings, and anywhere a token must survive conditions that would kill a smartphone. Some VPN and remote-access setups keep HOTP for the same reason: a counter is the only state either side maintains, and there is no clock to argue about.
Look-ahead windows, resync, and the one-step rule
RFC 4226's answer to desync is the look-ahead parameter, written s. On receiving a code that does not match its own counter, the server recalculates the next s codes and checks the received value against all of them. Your token is four presses ahead — a window of s = 5 absorbs it, and the server fast-forwards its counter to the observed position. The RFC recommends keeping the window as low as usability allows, because every accepted future code widens the replay surface. Its own security analysis puts an attacker's success probability at roughly s times v over ten to the power of the digit count — double the window, and you have doubled the odds per attempt budget.
For deeper desync, the RFC permits asking for a sequence of two or three consecutive codes — forging one is improbable, forging a run of them approaches absurd — after which the server resets to the observed position. That is resync in the counter world.
TOTP's tolerance is measured in time steps instead of counter positions. RFC 6238 recommends allowing at most one time step of network delay, which is why a validator that also checks the previous step forgives about a minute of clock skew — a phone a few seconds off works fine, a phone five minutes off never does. Validators can also record the drift they observe and adjust for it entry by entry, which is how dedicated hardware tokens with slowly drifting oscillators stay in service for years.
And the rejected-at-second-one mystery is not a mystery at all. A code generated at the tail end of a time step, typed as the next step begins, arrives in a different window than it was born in. The one-step tolerance exists precisely for that case. Developers wiring this up themselves face the same boundary problems, and implementing TOTP covers the decisions — the counter-based variant carries the same lessons with different numbers attached.
Running both from one place
Most people meet both algorithms without ever learning which is which, because the app hides the difference. It is worth knowing anyway, because the failure modes differ: a TOTP entry that stops working points at the clock, an HOTP entry that stops working points at the counter.
2FA by DependsiT speaks both, along with Steam Guard — TOTP, HOTP, and Steam Guard codes with per-key algorithm, digits, period, and counter, so a 30-second app entry and an event-based token entry sit in the same vault list, each with its own parameters intact. The guest generator needs no account at all: paste a Base32 secret or an otpauth:// URI and codes appear instantly, computed entirely in the page with zero network requests and nothing stored. That also makes it a quick diagnostic — if a code fails on your phone, the same secret in the generator tells you whether the secret or the phone is at fault. For the broader picture, the case for two-factor authentication is the starting point.
Frequently asked questions
Which is safer, HOTP or TOTP?
Per attempt, they are identical: the same HMAC core, the same truncation, the same one-in-a-million odds for a lucky six-digit guess. The difference is exposure time. A TOTP code is the answer for one 30-second step, while an unused HOTP code remains current until the token generates the next one — minutes or hours in real deployments, widened further by the look-ahead window. That shorter validity window is why TOTP dominates consumer 2FA. HOTP's counter independence is why it survives in battery-only keyfobs that keep no time.
Why do TOTP codes last 30 seconds?
Because RFC 6238 recommends a default time step of 30 seconds, described as a balance between security and usability. A shorter step shrinks the window an attacker has to replay an intercepted code, but leaves you typing under pressure and waiting longer for a fresh code — the wait for the next distinct code averages half the step. A longer step is calmer and correspondingly more exposed, since a code disclosed at the start of its step stays valid the whole time. Thirty seconds is long enough to read, type, and submit, and short enough that a leaked code dies within a minute.
Can HOTP fall out of sync?
Yes, and it is the algorithm's known cost. The token's counter advances every time a code is generated, while the server's advances only after a successful login, so idle button presses drift the two apart. RFC 4226's answer is the look-ahead window: the server recomputes the next s codes and accepts a match anywhere in that range, then fast-forwards to it. For deeper drift, it can require a sequence of two or three consecutive codes before resetting. The window stays deliberately small, since a larger one widens the replay surface by the same factor.
Why was my code rejected one second after it appeared?
Boundary effect. A code generated at the tail end of a time step often arrives at the server after the step has rolled over, so the server compares it against the next step's expected value and correctly rejects it. RFC 6238 recommends validators allow at most one time step of delay, which forgives exactly this — the previous step gets checked too. If a fresh code still fails, the cause is usually clock drift, and the settings paths that fix it are in our guide to 2FA codes not working.
Can I switch an account from HOTP to TOTP?
Not on your side alone. The algorithm is fixed at enrollment by the service, which decides whether your shared secret is consumed against a counter or a clock — both ends must agree, always. On your end you control how a stored entry is rendered: 2FA by DependsiT gives each key its own algorithm, digit count, period, and counter, which covers the cases where a service or hardware token deviates from the defaults. But if the service enrolled you in HOTP, the real fix is re-enrollment after the service offers TOTP. Most consumer services only offer TOTP in the first place.
If you want to see the difference with your own eyes, the guest generator is the fastest way: paste a secret and watch which kind of motion your codes follow.