[{"data":1,"prerenderedAt":849},["ShallowReactive",2],{"content-query-QZjoMU2Izl":3},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"layout":10,"crumb":11,"term":11,"summary":12,"group":13,"order":14,"updated":15,"cta":16,"related":24,"faq":40,"body":53,"_type":842,"_id":843,"_source":844,"_file":845,"_stem":846,"_extension":847,"sitemap":848},"/glossary/hmac","glossary",false,"","What is HMAC? Hash-based message authentication explained","HMAC (hash-based message authentication code) combines a secret key and a hash function to prove a message is authentic and unmodified. How it works, code examples and why webhooks use it.","reference","HMAC","A keyed hash that proves a message is authentic and unmodified.","Security",12,"2026-10-28",{"eyebrow":17,"title":18,"text":19,"to":20,"label":21,"secondaryTo":22,"secondaryLabel":23},"Free tool","Calculate and check an HMAC in your browser","The HMAC calculator generates an HMAC (SHA256, SHA512, SHA1 or MD5) from any payload and secret, or checks a computed value against a signature you paste. It runs in your browser, with no signup.","/hmac-verification/","Open the HMAC calculator","/verify-webhook-signature/","Verify a webhook signature",[25,28,31,34,37],{"title":26,"to":27},"Webhook signature","/glossary/webhook-signature/",{"title":29,"to":30},"Webhook secret","/glossary/webhook-secret/",{"title":32,"to":33},"Standard Webhooks","/glossary/standard-webhooks/",{"title":35,"to":36},"HMAC authentication for webhooks","/docs/webhooks/auth/hmac/",{"title":38,"to":39},"Webhook authentication","/blog/webhook-authentication/",[41,44,47,50],{"q":42,"a":43},"What is HMAC?","HMAC, short for hash-based message authentication code, is a way to compute a short tag from a message and a secret key using a hash function such as SHA-256. Anyone with the same key can recompute the tag and compare. A match proves the message was produced by someone holding the key and was not modified.",{"q":45,"a":46},"What is the difference between HMAC and a hash?","A plain hash like SHA-256 needs no key, so anyone can compute it, and an attacker who changes the message can simply compute a new hash. HMAC mixes in a secret key, so only someone with the key can produce a valid tag.",{"q":48,"a":49},"Is HMAC encryption?","No. HMAC does not hide the message. The message is sent in the clear, or inside HTTPS, and the HMAC tag only proves authenticity and integrity. Use TLS for confidentiality and HMAC for authenticity.",{"q":51,"a":52},"Is HMAC-SHA1 still safe?","HMAC-SHA1 is not broken in the way plain SHA-1 collisions are, but it is deprecated and new systems should use HMAC-SHA256. GitHub, for example, recommends its X-Hub-Signature-256 header over the older SHA-1 X-Hub-Signature header.",{"type":54,"children":55,"toc":835},"root",[56,78,159,166,195,203,230,305,330,451,464,470,594,612,618,677,689,695,798,804,829],{"type":57,"tag":58,"props":59,"children":60},"element","p",{},[61,68,70,76],{"type":57,"tag":62,"props":63,"children":64},"strong",{},[65],{"type":66,"value":67},"text","HMAC (hash-based message authentication code) is a short tag computed from a message and a secret key with a hash function such as SHA-256.",{"type":66,"value":69}," Anyone who holds the same key can recompute the tag and compare it. A match proves two things: the message was created by someone who knows the key (authenticity), and it hasn't been changed since (integrity). It's the mechanism behind almost every ",{"type":57,"tag":71,"props":72,"children":73},"a",{"href":27},[74],{"type":66,"value":75},"webhook signature",{"type":66,"value":77},".",{"type":57,"tag":79,"props":80,"children":84},"pre",{"code":81,"language":82,"meta":7,"className":83,"style":7},"$ printf '{\"event\":\"ping\"}' | openssl dgst -sha256 -hmac 'my-secret'\nSHA2-256(stdin)= a4aa04fd005086c11e0953dd0aaa3306e2d8a9fbd9b7d333cc52ce210bdc67f6\n","bash","language-bash shiki shiki-themes github-dark",[85],{"type":57,"tag":86,"props":87,"children":88},"code",{"__ignoreMap":7},[89,144],{"type":57,"tag":90,"props":91,"children":94},"span",{"class":92,"line":93},"line",1,[95,101,107,112,118,123,128,134,139],{"type":57,"tag":90,"props":96,"children":98},{"style":97},"--shiki-default:#B392F0",[99],{"type":66,"value":100},"$",{"type":57,"tag":90,"props":102,"children":104},{"style":103},"--shiki-default:#9ECBFF",[105],{"type":66,"value":106}," printf",{"type":57,"tag":90,"props":108,"children":109},{"style":103},[110],{"type":66,"value":111}," '{\"event\":\"ping\"}'",{"type":57,"tag":90,"props":113,"children":115},{"style":114},"--shiki-default:#F97583",[116],{"type":66,"value":117}," |",{"type":57,"tag":90,"props":119,"children":120},{"style":97},[121],{"type":66,"value":122}," openssl",{"type":57,"tag":90,"props":124,"children":125},{"style":103},[126],{"type":66,"value":127}," dgst",{"type":57,"tag":90,"props":129,"children":131},{"style":130},"--shiki-default:#79B8FF",[132],{"type":66,"value":133}," -sha256",{"type":57,"tag":90,"props":135,"children":136},{"style":130},[137],{"type":66,"value":138}," -hmac",{"type":57,"tag":90,"props":140,"children":141},{"style":103},[142],{"type":66,"value":143}," 'my-secret'\n",{"type":57,"tag":90,"props":145,"children":147},{"class":92,"line":146},2,[148,153],{"type":57,"tag":90,"props":149,"children":150},{"style":97},[151],{"type":66,"value":152},"SHA2-256(stdin",{"type":57,"tag":90,"props":154,"children":156},{"style":155},"--shiki-default:#E1E4E8",[157],{"type":66,"value":158},")= a4aa04fd005086c11e0953dd0aaa3306e2d8a9fbd9b7d333cc52ce210bdc67f6\n",{"type":57,"tag":160,"props":161,"children":163},"h2",{"id":162},"how-hmac-works",[164],{"type":66,"value":165},"How HMAC works",{"type":57,"tag":58,"props":167,"children":168},{},[169,171,177,179,185,187,193],{"type":66,"value":170},"HMAC is defined in RFC 2104. Given a key ",{"type":57,"tag":86,"props":172,"children":174},{"className":173},[],[175],{"type":66,"value":176},"K",{"type":66,"value":178},", a message ",{"type":57,"tag":86,"props":180,"children":182},{"className":181},[],[183],{"type":66,"value":184},"m",{"type":66,"value":186}," and a hash function ",{"type":57,"tag":86,"props":188,"children":190},{"className":189},[],[191],{"type":66,"value":192},"H",{"type":66,"value":194},":",{"type":57,"tag":79,"props":196,"children":198},{"code":197},"HMAC(K, m) = H( (K' ⊕ opad) || H( (K' ⊕ ipad) || m ) )\n",[199],{"type":57,"tag":86,"props":200,"children":201},{"__ignoreMap":7},[202],{"type":66,"value":197},{"type":57,"tag":58,"props":204,"children":205},{},[206,212,214,220,222,228],{"type":57,"tag":86,"props":207,"children":209},{"className":208},[],[210],{"type":66,"value":211},"K'",{"type":66,"value":213}," is the key padded to the hash's block size, and ",{"type":57,"tag":86,"props":215,"children":217},{"className":216},[],[218],{"type":66,"value":219},"ipad",{"type":66,"value":221}," and ",{"type":57,"tag":86,"props":223,"children":225},{"className":224},[],[226],{"type":66,"value":227},"opad",{"type":66,"value":229}," are fixed constants. You never implement this by hand. Every language ships it:",{"type":57,"tag":79,"props":231,"children":235},{"code":232,"language":233,"meta":7,"className":234,"style":7},"// Node.js\ncrypto.createHmac('sha256', secret).update(rawBody).digest('hex')\n","js","language-js shiki shiki-themes github-dark",[236],{"type":57,"tag":86,"props":237,"children":238},{"__ignoreMap":7},[239,248],{"type":57,"tag":90,"props":240,"children":241},{"class":92,"line":93},[242],{"type":57,"tag":90,"props":243,"children":245},{"style":244},"--shiki-default:#6A737D",[246],{"type":66,"value":247},"// Node.js\n",{"type":57,"tag":90,"props":249,"children":250},{"class":92,"line":146},[251,256,261,266,271,276,281,286,291,295,300],{"type":57,"tag":90,"props":252,"children":253},{"style":155},[254],{"type":66,"value":255},"crypto.",{"type":57,"tag":90,"props":257,"children":258},{"style":97},[259],{"type":66,"value":260},"createHmac",{"type":57,"tag":90,"props":262,"children":263},{"style":155},[264],{"type":66,"value":265},"(",{"type":57,"tag":90,"props":267,"children":268},{"style":103},[269],{"type":66,"value":270},"'sha256'",{"type":57,"tag":90,"props":272,"children":273},{"style":155},[274],{"type":66,"value":275},", secret).",{"type":57,"tag":90,"props":277,"children":278},{"style":97},[279],{"type":66,"value":280},"update",{"type":57,"tag":90,"props":282,"children":283},{"style":155},[284],{"type":66,"value":285},"(rawBody).",{"type":57,"tag":90,"props":287,"children":288},{"style":97},[289],{"type":66,"value":290},"digest",{"type":57,"tag":90,"props":292,"children":293},{"style":155},[294],{"type":66,"value":265},{"type":57,"tag":90,"props":296,"children":297},{"style":103},[298],{"type":66,"value":299},"'hex'",{"type":57,"tag":90,"props":301,"children":302},{"style":155},[303],{"type":66,"value":304},")\n",{"type":57,"tag":79,"props":306,"children":310},{"code":307,"language":308,"meta":7,"className":309,"style":7},"# Python\nhmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()\n","python","language-python shiki shiki-themes github-dark",[311],{"type":57,"tag":86,"props":312,"children":313},{"__ignoreMap":7},[314,322],{"type":57,"tag":90,"props":315,"children":316},{"class":92,"line":93},[317],{"type":57,"tag":90,"props":318,"children":319},{},[320],{"type":66,"value":321},"# Python\n",{"type":57,"tag":90,"props":323,"children":324},{"class":92,"line":146},[325],{"type":57,"tag":90,"props":326,"children":327},{},[328],{"type":66,"value":329},"hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()\n",{"type":57,"tag":79,"props":331,"children":335},{"code":332,"language":333,"meta":7,"className":334,"style":7},"// Go\nmac := hmac.New(sha256.New, []byte(secret))\nmac.Write(body)\nsig := hex.EncodeToString(mac.Sum(nil))\n","go","language-go shiki shiki-themes github-dark",[336],{"type":57,"tag":86,"props":337,"children":338},{"__ignoreMap":7},[339,347,385,404],{"type":57,"tag":90,"props":340,"children":341},{"class":92,"line":93},[342],{"type":57,"tag":90,"props":343,"children":344},{"style":244},[345],{"type":66,"value":346},"// Go\n",{"type":57,"tag":90,"props":348,"children":349},{"class":92,"line":146},[350,355,360,365,370,375,380],{"type":57,"tag":90,"props":351,"children":352},{"style":155},[353],{"type":66,"value":354},"mac ",{"type":57,"tag":90,"props":356,"children":357},{"style":114},[358],{"type":66,"value":359},":=",{"type":57,"tag":90,"props":361,"children":362},{"style":155},[363],{"type":66,"value":364}," hmac.",{"type":57,"tag":90,"props":366,"children":367},{"style":97},[368],{"type":66,"value":369},"New",{"type":57,"tag":90,"props":371,"children":372},{"style":155},[373],{"type":66,"value":374},"(sha256.New, []",{"type":57,"tag":90,"props":376,"children":377},{"style":114},[378],{"type":66,"value":379},"byte",{"type":57,"tag":90,"props":381,"children":382},{"style":155},[383],{"type":66,"value":384},"(secret))\n",{"type":57,"tag":90,"props":386,"children":388},{"class":92,"line":387},3,[389,394,399],{"type":57,"tag":90,"props":390,"children":391},{"style":155},[392],{"type":66,"value":393},"mac.",{"type":57,"tag":90,"props":395,"children":396},{"style":97},[397],{"type":66,"value":398},"Write",{"type":57,"tag":90,"props":400,"children":401},{"style":155},[402],{"type":66,"value":403},"(body)\n",{"type":57,"tag":90,"props":405,"children":407},{"class":92,"line":406},4,[408,413,417,422,427,432,437,441,446],{"type":57,"tag":90,"props":409,"children":410},{"style":155},[411],{"type":66,"value":412},"sig ",{"type":57,"tag":90,"props":414,"children":415},{"style":114},[416],{"type":66,"value":359},{"type":57,"tag":90,"props":418,"children":419},{"style":155},[420],{"type":66,"value":421}," hex.",{"type":57,"tag":90,"props":423,"children":424},{"style":97},[425],{"type":66,"value":426},"EncodeToString",{"type":57,"tag":90,"props":428,"children":429},{"style":155},[430],{"type":66,"value":431},"(mac.",{"type":57,"tag":90,"props":433,"children":434},{"style":97},[435],{"type":66,"value":436},"Sum",{"type":57,"tag":90,"props":438,"children":439},{"style":155},[440],{"type":66,"value":265},{"type":57,"tag":90,"props":442,"children":443},{"style":130},[444],{"type":66,"value":445},"nil",{"type":57,"tag":90,"props":447,"children":448},{"style":155},[449],{"type":66,"value":450},"))\n",{"type":57,"tag":58,"props":452,"children":453},{},[454,456,462],{"type":66,"value":455},"The nested construction is why HMAC is safe even with hashes like SHA-256 that are vulnerable to length-extension attacks, where a naive ",{"type":57,"tag":86,"props":457,"children":459},{"className":458},[],[460],{"type":66,"value":461},"SHA256(key + message)",{"type":66,"value":463}," is not.",{"type":57,"tag":160,"props":465,"children":467},{"id":466},"hmac-vs-a-plain-hash-vs-a-digital-signature",[468],{"type":66,"value":469},"HMAC vs a plain hash vs a digital signature",{"type":57,"tag":471,"props":472,"children":473},"table",{},[474,500],{"type":57,"tag":475,"props":476,"children":477},"thead",{},[478],{"type":57,"tag":479,"props":480,"children":481},"tr",{},[482,486,491,495],{"type":57,"tag":483,"props":484,"children":485},"th",{},[],{"type":57,"tag":483,"props":487,"children":488},{},[489],{"type":66,"value":490},"Plain hash (SHA-256)",{"type":57,"tag":483,"props":492,"children":493},{},[494],{"type":66,"value":11},{"type":57,"tag":483,"props":496,"children":497},{},[498],{"type":66,"value":499},"Digital signature (RSA, Ed25519)",{"type":57,"tag":501,"props":502,"children":503},"tbody",{},[504,528,550,572],{"type":57,"tag":479,"props":505,"children":506},{},[507,513,518,523],{"type":57,"tag":508,"props":509,"children":510},"td",{},[511],{"type":66,"value":512},"Needs a key",{"type":57,"tag":508,"props":514,"children":515},{},[516],{"type":66,"value":517},"No",{"type":57,"tag":508,"props":519,"children":520},{},[521],{"type":66,"value":522},"Shared secret",{"type":57,"tag":508,"props":524,"children":525},{},[526],{"type":66,"value":527},"Private key to sign, public key to verify",{"type":57,"tag":479,"props":529,"children":530},{},[531,536,541,546],{"type":57,"tag":508,"props":532,"children":533},{},[534],{"type":66,"value":535},"Proves integrity",{"type":57,"tag":508,"props":537,"children":538},{},[539],{"type":66,"value":540},"Only against accidents",{"type":57,"tag":508,"props":542,"children":543},{},[544],{"type":66,"value":545},"Yes",{"type":57,"tag":508,"props":547,"children":548},{},[549],{"type":66,"value":545},{"type":57,"tag":479,"props":551,"children":552},{},[553,558,562,567],{"type":57,"tag":508,"props":554,"children":555},{},[556],{"type":66,"value":557},"Proves who sent it",{"type":57,"tag":508,"props":559,"children":560},{},[561],{"type":66,"value":517},{"type":57,"tag":508,"props":563,"children":564},{},[565],{"type":66,"value":566},"Anyone with the shared key",{"type":57,"tag":508,"props":568,"children":569},{},[570],{"type":66,"value":571},"Only the private-key holder",{"type":57,"tag":479,"props":573,"children":574},{},[575,580,585,589],{"type":57,"tag":508,"props":576,"children":577},{},[578],{"type":66,"value":579},"Speed",{"type":57,"tag":508,"props":581,"children":582},{},[583],{"type":66,"value":584},"Fast",{"type":57,"tag":508,"props":586,"children":587},{},[588],{"type":66,"value":584},{"type":57,"tag":508,"props":590,"children":591},{},[592],{"type":66,"value":593},"Slower",{"type":57,"tag":58,"props":595,"children":596},{},[597,599,604,606,610],{"type":66,"value":598},"Webhooks mostly use HMAC because it's fast, simple and every language supports it. The cost is that the sender and receiver share the same ",{"type":57,"tag":71,"props":600,"children":601},{"href":30},[602],{"type":66,"value":603},"secret",{"type":66,"value":605},". Some specs, including ",{"type":57,"tag":71,"props":607,"children":608},{"href":33},[609],{"type":66,"value":32},{"type":66,"value":611},", also define an asymmetric option for cases where receivers shouldn't hold a signing key.",{"type":57,"tag":160,"props":613,"children":615},{"id":614},"how-webhooks-use-hmac",[616],{"type":66,"value":617},"How webhooks use HMAC",{"type":57,"tag":619,"props":620,"children":621},"ol",{},[622,635,672],{"type":57,"tag":623,"props":624,"children":625},"li",{},[626,628,634],{"type":66,"value":627},"The provider builds a signed string, such as the raw body, or ",{"type":57,"tag":86,"props":629,"children":631},{"className":630},[],[632],{"type":66,"value":633},"{timestamp}.{body}",{"type":66,"value":77},{"type":57,"tag":623,"props":636,"children":637},{},[638,640,646,648,654,656,662,664,670],{"type":66,"value":639},"It computes ",{"type":57,"tag":86,"props":641,"children":643},{"className":642},[],[644],{"type":66,"value":645},"HMAC-SHA256(secret, signed_string)",{"type":66,"value":647},", encodes it as hex or base64, and sends it in a header such as ",{"type":57,"tag":86,"props":649,"children":651},{"className":650},[],[652],{"type":66,"value":653},"X-Hub-Signature-256",{"type":66,"value":655}," (GitHub), ",{"type":57,"tag":86,"props":657,"children":659},{"className":658},[],[660],{"type":66,"value":661},"Stripe-Signature",{"type":66,"value":663}," (Stripe) or ",{"type":57,"tag":86,"props":665,"children":667},{"className":666},[],[668],{"type":66,"value":669},"webhook-signature",{"type":66,"value":671}," (Standard Webhooks).",{"type":57,"tag":623,"props":673,"children":674},{},[675],{"type":66,"value":676},"The receiver recomputes the HMAC over the bytes it received and compares in constant time.",{"type":57,"tag":58,"props":678,"children":679},{},[680,682,688],{"type":66,"value":681},"Including a timestamp in the signed string, and rejecting old timestamps, prevents an attacker from resending a captured request. See ",{"type":57,"tag":71,"props":683,"children":685},{"href":684},"/glossary/webhook-replay/",[686],{"type":66,"value":687},"webhook replay",{"type":66,"value":77},{"type":57,"tag":160,"props":690,"children":692},{"id":691},"common-mistakes",[693],{"type":66,"value":694},"Common mistakes",{"type":57,"tag":696,"props":697,"children":698},"ul",{},[699,740,750,760,778,788],{"type":57,"tag":623,"props":700,"children":701},{},[702,714,716,722,724,730,732,738],{"type":57,"tag":62,"props":703,"children":704},{},[705,707,713],{"type":66,"value":706},"Comparing with ",{"type":57,"tag":86,"props":708,"children":710},{"className":709},[],[711],{"type":66,"value":712},"==",{"type":66,"value":77},{"type":66,"value":715}," Use a constant-time comparison: ",{"type":57,"tag":86,"props":717,"children":719},{"className":718},[],[720],{"type":66,"value":721},"crypto.timingSafeEqual",{"type":66,"value":723}," (Node), ",{"type":57,"tag":86,"props":725,"children":727},{"className":726},[],[728],{"type":66,"value":729},"hmac.compare_digest",{"type":66,"value":731}," (Python), ",{"type":57,"tag":86,"props":733,"children":735},{"className":734},[],[736],{"type":66,"value":737},"hmac.Equal",{"type":66,"value":739}," (Go).",{"type":57,"tag":623,"props":741,"children":742},{},[743,748],{"type":57,"tag":62,"props":744,"children":745},{},[746],{"type":66,"value":747},"HMACing a re-serialized body.",{"type":66,"value":749}," The tag covers exact bytes. Parsing and re-stringifying JSON changes them.",{"type":57,"tag":623,"props":751,"children":752},{},[753,758],{"type":57,"tag":62,"props":754,"children":755},{},[756],{"type":66,"value":757},"Hex vs base64.",{"type":66,"value":759}," The same HMAC encodes to different strings. Match the provider's encoding.",{"type":57,"tag":623,"props":761,"children":762},{},[763,768,770,776],{"type":57,"tag":62,"props":764,"children":765},{},[766],{"type":66,"value":767},"Key encoding.",{"type":66,"value":769}," Some providers give the key as base64 (Standard Webhooks ",{"type":57,"tag":86,"props":771,"children":773},{"className":772},[],[774],{"type":66,"value":775},"whsec_",{"type":66,"value":777}," secrets) and expect you to decode it first. Others use the literal string.",{"type":57,"tag":623,"props":779,"children":780},{},[781,786],{"type":57,"tag":62,"props":782,"children":783},{},[784],{"type":66,"value":785},"Weak algorithms.",{"type":66,"value":787}," Avoid HMAC-MD5 and prefer HMAC-SHA256 over HMAC-SHA1 for anything new.",{"type":57,"tag":623,"props":789,"children":790},{},[791,796],{"type":57,"tag":62,"props":792,"children":793},{},[794],{"type":66,"value":795},"Short or guessable keys.",{"type":66,"value":797}," Use at least 32 random bytes.",{"type":57,"tag":160,"props":799,"children":801},{"id":800},"try-it",[802],{"type":66,"value":803},"Try it",{"type":57,"tag":58,"props":805,"children":806},{},[807,809,814,816,821,823,828],{"type":66,"value":808},"The ",{"type":57,"tag":71,"props":810,"children":811},{"href":20},[812],{"type":66,"value":813},"HMAC calculator",{"type":66,"value":815}," computes and compares HMACs in your browser. To check a full webhook against a provider's format, use the ",{"type":57,"tag":71,"props":817,"children":818},{"href":22},[819],{"type":66,"value":820},"signature verifier",{"type":66,"value":822},". To verify HMAC on incoming webhooks before they're forwarded, see the ",{"type":57,"tag":71,"props":824,"children":825},{"href":36},[826],{"type":66,"value":827},"HMAC authentication docs",{"type":66,"value":77},{"type":57,"tag":830,"props":831,"children":832},"style",{},[833],{"type":66,"value":834},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":7,"searchDepth":387,"depth":387,"links":836},[837,838,839,840,841],{"id":162,"depth":146,"text":165},{"id":466,"depth":146,"text":469},{"id":614,"depth":146,"text":617},{"id":691,"depth":146,"text":694},{"id":800,"depth":146,"text":803},"markdown","content:glossary:hmac.md","content","glossary/hmac.md","glossary/hmac","md",{"loc":4},1793199805290]