
1. 2026年还有人在密码推送的坑里这事本身就值得聊聊先别笑就在上个月我还帮两个同事处理过一模一样的报错。一个是刚入职半年的校招生在本地仓库跑git push直接弹窗要求输入用户名密码输完密码之后GitHub干脆利落地回了一句Support for password authentication was removed。另一个是干了五年的老后端开启两步验证2FA之后原本每天都在推的仓库突然开始报fatal: Authentication failed他当场就懵了。这个标题叫2026仍高频不是标题党。GitHub早在2021年8月就强制移除了账号密码的HTTPS推送认证但Personal Access Token后面统一叫PAT作为替代品到今天依然是开发者日常最容易踩坑的一块。原因很简单PAT不是密码但很多人的使用习惯还停留在密码时代。密码是永久有效的PAT会过期密码只有一种权限PAT有几十种权限范围scope可以选密码在GitHub后台就一个PAT你可以一把一把地生成也可能一把一把地忘掉。这篇文章想做的事很朴素把PAT从生成、配置、失效排查到替代方案彻底讲透。如果你曾经被git push的401折磨过或者你刚开完2FA发现原来好使的推送突然全挂了又或者你只是想在2026年把这套机制一次性搞明白、以后再也不用搜教程——那这篇文章就是给你写的。2. PAT的工作原理为什么它一到期你的推送就哑火很多人对PAT的理解就是一串代替密码的字符串这个理解不算错但只停留在第一层。要真正搞定PAT相关的各种报错你得把它当成一个有时效、有权限、有绑定关系的小型身份凭证来看。2.1 一个token到底绑定了哪些东西你在GitHub网页端生成PAT的时候页面上会出现一堆选项。很多人不看直接一路Next这就为后面的坑埋下了伏笔。实际上一个PAT背后至少绑定了四层信息绑定的账号这个token属于哪个GitHub用户推送时GitHub会校验这个归属关系。有效期expirationGitHub允许你设置30天、60天、90天或者自定义天数甚至选永不过期。到期的瞬间token直接失效任何请求都会返回401。权限范围scopes这是token的核心。比如reposcope才允许你读写普通仓库workflowscope允许你推送GitHub Actions的配置文件read:packages允许你拉取私有包。权限不够的时候GitHub返回的报错可能是403而不是401这是很多人排查时被绕晕的原因。组织授权SSO如果你的仓库属于一个启用了SAMLO单点登录的组织你生成token之后还必须单独给这个组织授权否则推送时一样报错。所以当一个token失效它可能是真的到期了可能是权限被改了可能是组织授权掉了也可能是仓库本身转移了归属。只盯着过期两个字排查容易走进死胡同。2.2 为什么GitHub要搞定期过期这么麻烦的机制用一个生活化的类比你家的门锁密码如果几十年不换一旦泄露别人能一直自由进出但如果每三个月换一次就算泄露了影响范围也是有限的。PAT就是这个逻辑GitHub作为全球最大的代码托管平台每年都有海量的自动化扫描工具在GitHub上爬token很多人的token因为被扫到而泄露GitHub甚至会主动发邮件通知你我们发现你的token泄露了已经帮你撤销。这就是为什么GitHub官方一直建议开发者设置合理的过期时间而不是选No expiration。你在本地存的token一旦推到公开仓库、被日志打出来、被第三方工具读取就有泄露风险。定期过期相当于一道兜底防线就算泄露了黑客拿到手的时候可能已经过期了。2.3 过期和失效是两个完全不同的故障排查PAT问题最忌讳的就是把所有的认证失败都归因于过期。我遇到过太多人明明token还差两个月才到期却反复生成新token来续命结果问题根本不在token本身。过期指的是token到达了创建时设定的有效期终点这是GitHub单方面决定的硬时限没有任何办法延长只能重新生成。失效则是一个更宽泛的概念常见的有这么几类你或其他协作者在后台删除了这个token。token的scope不满足当前操作要求。仓库被转移到了另一个组织/账号原token的授权关系不再适用。开启了2FA之后旧的凭证缓存失效。组织或者企业策略变更比如管理员强制要求所有token必须在XX天内轮换。所以在动手重新生成token之前先判断一下到底是哪一种。这个判断不需要多么高深的手段一条命令的事我放在下一节讲。3. HTTPS推送失败的完整排查链路从报错到根因一步步钉死这里我不直接给答案而是把完整的排查链路走一遍。原因很简单你下次遇到的报错不一定和这次一模一样但你只要掌握了排查思路就没什么能难住你的。3.1 先看报错文本判断故障类型Git的报错看着乱其实分三类每一类指向的根因完全不同。第一类Support for password authentication was removed. Please use a personal access token instead.这个报错翻译成人话就是你还在用账号密码登录GitHub不认了。这种情况最常见于新装的电脑、新建的仓库或者git配置里的credential helper指向了空的凭据存储。Git把请求发出去尝试用缓存里的账号密码做认证GitHub一看是密码认证直接拒绝。这里的根因不是token失效而是压根没有使用token。第二类fatal: Authentication failed for https://github.com/...这个报错意味着git已经尝试了当前可用的凭证但GitHub校验之后拒绝了。引起这个报错的可能原因比较多包括但不限于token真的到期了、token输入有误、token权限不足或者远程URL里写死了旧用户名。这类报错是排查的重点区域下面会展开细说。第三类remote: Invalid username or token.GitHub明确告诉你用户名或token无效。先检查用户名是不是写错了很多人GitHub注册的是邮箱但推送时用了邮箱去认证GitHub并不接受邮箱作为用户名。如果用户名没问题再看token本身。3.2 检查本地缓存的旧凭证三平台的清缓存操作许多推送失败问题不在服务器端而在你的电脑里存了一堆过期的旧账。git本身不保存密码它是通过credential helper去系统里查的。也就是说你之前可能输入过一次tokengit把它交给了系统的凭据管理器保存之后每次推送都是调用这个缓存的token。当这个token失效后git不会自动去问你要新的它只会反复拿旧的去试直到失败。这个时候你光在GitHub后台生成新token是不够的因为git根本不知道有新token的存在。你需要把旧的缓存清掉。Windows打开控制面板 - 用户账户 - 凭据管理器 - Windows凭据。在列表里找到git:https://github.com这一项点开看看然后删除。下次推送时git会重新弹窗要求输入用户名和token。macOS打开钥匙串访问应用搜索github.com找到对应的条目后删除。macOS使用osxkeychain作为默认credential helper删除钥匙串条目之后git会重新交互式地要求输入凭证。注意这里有个小坑钥匙串里可能同时存在多个github.com相关条目特别是如果你曾经用SSH方式连接过还会有一个github.com-remote之类的SSH密码条目如果没有把握全删就专门搜https://github.com这种HTTPS相关的。Linux取决于你配置的credential helper。如果用的是libsecret可以用secret-tool search --all servicegit.credential列出当前的凭证如果用的是最朴素的store模式直接编辑~/.git-credentials文件把里面那行带token的URL删掉。清理完缓存之后建议顺手检查一下远程URL是否干净。运行git remote -v如果输出类似https://你的用户名github.com/xxx/xxx.git这种把用户名嵌在URL里的形式需要修正一下git remote set-url origin https://github.com/xxx/xxx.git因为一旦URL里嵌入了旧的用户名无论你缓存的token有多新git都会优先用它导致认证一直失败。3.3 用curl验证token本身是否有效清完缓存之后如果推送还是失败就要判断token本身到底行不行。这一步不需要git参与用curl直接打GitHub的API就行。打开终端执行curl -i -u 你的GitHub用户名:你的token https://api.github.com/user几个关键判断标准返回200 OK说明token有效问题可能出在git配置或者权限上。返回401 Unauthorized说明token无效、过期或者用户名不对。返回403 Forbidden说明token是有效的但权限不够访问这个资源。比如你在一个有SSO的组织里token没有授权该组织或者token缺少reposcope。这个方法最大的好处是绕开了本地git的缓存干扰直接测试最底层凭证是否可用。我把这个curl命令放在所有排查步骤的最前面做效率最高。3.4 开启2FA之后推送失败的深坑标题里专门点到了2FA后HTTPS推送失败这是非常高频的场景值得单独拿出来说。2FA即两步验证。它的本意是增强账号安全登录网页端的时候除了输入账号密码还要额外提供一个验证码。但问题来了HTTPS推送走的是git协议它没有地方让你输验证码。GitHub的处理方式是不允许密码认证的HTTPS请求强制要求使用token。所以一旦你开启了2FA原来用户名密码的推送方式就必须改成用户名token。如果你开完2FA之后没有更新本地缓存的凭证那推送必然失败。这也是很多人困惑我什么都没改怎么突然就挂了的真正原因。注意开了2FA之后你原来的密码并不会被GitHub作废它依然可以用于网页登录只是不能用于HTTPS推送。这是两个独立的认证通道。遇到这个场景的处理方式很直接生成一个新token然后用前面3.2节的方法更新本地凭证缓存。不需要改任何仓库的远程URL因为URL没有变变的只是认证材料。4. 新PAT生成与本地配置一步到位的完整操作排查结束之后就到了实际解决阶段。这一步做得好至少未来半年内你不用再碰这个问题。我按照生成token - 配置本地 - 推送验证的顺序完整走一遍每个环节的注意事项也一起放进来。4.1 生成PAT时你要认真选的三个选项登录GitHub后依次进入Settings-Developer settings-Personal access tokens-Tokens (classic)。如果你之前用的是Fine-grained token路径略有不同但经典PAT的使用面更广先讲它。点击Generate new token (classic)之后你需要认真面对三个选项。第一是过期时间Expiration。GitHub默认给的是30天你可以下拉改成60天、90天甚至自定义。我的建议是日常个人开发用90天自动化脚本/CI环境用30天到60天并对到期时间做记录。不建议选No expiration哪怕它看起来省事。为什么因为过期时间是你token泄露后的最后一道保险一旦选了永不过期保险就没了。90天左右比较平衡一年最多折腾四次每次花五分钟更新完全可以接受。第二是权限范围Select scopes。权限选择的核心原则就四个字最小够用。普通仓库读写勾repo就够了你需要推送GitHub Actions的配置文件.github/workflows/还需要额外勾workflow权限你需要读取私有容器镜像勾read:packages你需要删除仓库勾delete_repo。不要一把梭把所有权限全勾上万一token泄露黑客拿着你的token能做的事就太多了。第三是token生成后的保存。这一步GitHub只会显示一次token明文刷新页面就永远消失了。正确做法是生成后立刻复制粘贴到密码管理器里。我用的是本地加密的密码管理工具KeepassXC也有很多人用1Password、Bitwarden。千万别直接贴在记事本里也别发到公司聊天群明文保存token等于给自己埋雷。4.2 把token交给git三平台凭据管理配置token生成完毕之后接下来的问题是怎么让它被git使用。很多人习惯把token直接拼在远程URL里比如git push https://用户名:tokengithub.com/xxx/xxx.git我强烈不建议这么干。因为这样写的话远程URL会出现在你项目的.git/config里一旦你把这个项目的目录分享出去、截图到群里、或者推到别的代码托管平台token就跟着泄露了。正确的姿势是配置git的credential helper让git按需调用系统凭据管理器。Windows上现代Git for Windows默认自带manager-core即Git Credential Manager。你只需要在第一次推送时在弹窗里输入GitHub用户名和token注意是token不是密码之后git就会自动保存到Windows凭据管理器。可以验证一下git config --global credential.helper输出如果是manager-core或者manager就说明默认配置OK。如果是空的执行git config --global credential.helper manager-coremacOS上默认helper是osxkeychain这个helper会调用系统的钥匙串。所以只要你第一次推送时在弹出的macOS对话框中输入了token之后git自动从钥匙串读取无需任何额外配置。确认一下git config --global credential.helper输出osxkeychain就对了。如果没有安装Git时勾选对应的选项或者执行git config --global credential.helper osxkeychainLinux上稍微麻烦一点因为桌面环境不统一。如果你用的是GNOME桌面可以装libsecret工具链sudo apt install libsecret-1-0 gnome-keyring git config --global credential.helper /usr/share/doc/git/contrib/credential/libsecret/git-credential-libsecret对于服务器环境没有图形界面很多人选择store模式也就是把凭证明文存在~/.git-credentials文件里。这种方案安全级别低但确实是最省事的。我给一个折中建议服务器上用store模式可以但记得给这个文件设置权限chmod 600 ~/.git-credentials至少别让别人随便读。4.3 验证推送从配置到落地配置完成后我在本地仓库执行一次真正的推送验证同时观察全过程git remote -v git push origin main第一次推送时git会触发credential helper弹出输入框。Windows上是图形弹窗macOS上是钥匙串弹窗Linux终端则可能直接提示输入。这里的用户名填你的GitHub用户名密码/口令那一栏填token不是GitHub账号密码。很多人在这一步习惯性地输入了账号密码结果又收获一次Authentication failed。如果push成功你可以再push一次这一次不会再弹窗说明凭证已经被系统缓存了后续一段时间内你的推送都会是静默的、无感的。如果push依然失败回到第3节把整个链路再排查一遍尤其是curl验证token那一步。我的一个额外经验配置完成之后最好主动打开Settings-Developer settings-Personal access tokens看一眼这个token的Last used时间。如果显示的是刚刚说明GitHub确实收到了来自这个token的请求如果显示Never说明你的git请求根本没走到这个token问题一定出在本地配置上。这个细节能帮你省下好几个小时的排查时间。5. 治本方案从PAT到SSH、Fine-grained PAT的迁移与取舍刚才讲的全是怎么把PAT用明白。但这篇文章的标题既然叫仍高频那光教人怎么换token是不够的——如果你一年要换四次token不管是记性多好的人都可能在某一次遗漏。所以我想把更治本的方案也一起放进来做一次有效取舍。5.1 SSH Key一劳永逸的推送方式SSH和HTTPS是git远端通信的两大协议。HTTPS用用户名token认证SSH用公钥/私钥认证。SSH最大的优势在于一对密钥生成之后基本不需要更换只要你的私钥不泄露它可以一直用下去不存在90天过期这种概念。切换到SSH的完整过程第一步生成密钥对如果你没有的话ssh-keygen -t ed25519 -C 你的邮箱或备注一路回车到结束默认会在~/.ssh/下生成id_ed25519私钥和id_ed25519.pub公钥。第二步把公钥添加到GitHub。打开Settings-SSH and GPG keys-New SSH key把id_ed25519.pub的内容整个复制进去起个备注名保存。私钥留在本地永远不要发给任何人。第三步把本地仓库的远程URL从HTTPS切换到SSHgit remote set-url origin gitgithub.com:用户名/仓库名.git第四步测试连接ssh -T gitgithub.com看到Hi 用户名! Youve successfully authenticated, but GitHub does not provide shell access.就说明配置成功了。SSH方案有个额外好处是支持GPG签名、支持在~/.ssh/config里做多账号管理对于同时维护多个GitHub账号、或者需要频繁操作远程仓库的人来说体验比HTTPStoken高一个档次。它也有不适合的场景。最典型的就是CI/CD流水线、GitHub Actions的secrets配置、容器构建环境——这些场景需要的是可以在几十秒内生成、用完即弃、细粒度控制权限的临时凭证SSH密钥不适合在这种场景里大规模分发。5.2 Fine-grained PAT2026年的新选择GitHub在2022年就开始推广Fine-grained PAT细粒度访问令牌到2026年它已经是新建token时的默认推荐选项了。对比经典PATFine-grained PAT有几个关键区别维度Classic PATFine-grained PAT有效期最长不过期最长1年权限范围scope级别比较粗单个仓库/组织级别可以精确到只读问题、只读代码、读写Actions仓库可见性授权后默认访问所有可见仓库可以精确选定授权的仓库组织访问需要额外SSO授权创建时直接指定组织范围和权限过期时间支持永不过期强制设置最长为1年Fine-grained PAT适合需要精细控制权限的场景比如你的token只用来读某个仓库的issues或者只用来推送某个项目的Actions配置。但它也有门槛创建流程更复杂很多老工具/老脚本对Fine-grained PAT的兼容性不如Classic PAT完美某些CLI工具可能识别不了它的权限结构。如果你不确定你的工具链支不支持Fine-grained PAT先用Classic PAT保底等确认兼容之后再把生产环境的token切过来。5.3 我的建议按场景选方案我自己目前的做法是本地日常开发推送优先SSH key。生成一次用上几年不用思考过期问题。临时脚本、CI流程、自动化任务用Fine-grained PAT最短有效期最小权限。第三方工具需要访问GitHub比如IDE的内置Git面板、代码扫描工具分情况能用SSH就SSH不能的话用Classic PAT并把scope限制到最小。这套组合下来我大概有将近一年没因为token问题被卡过推送了。不是说PAT不好而是你得让工具出现在合适的场景里才能发挥它最大的价值。6. 我在这个坑里爬出来的几条实操经验最后这部分不聊技术原理了聊点踩坑踩出来的肌肉记忆。有些东西文档里不会写但实际工作中非常管用。第一条创建token之后立即在终端里跑一次git push。很多人创建完token就关掉页面过了几天才用。等真正推送失败的时候已经记不清这个token当时勾了哪些权限、设了多长有效期。创建完立刻推一次既验证了token可用也把配置链路整体通了一遍之后再用就心里有底。第二条在密码管理器里面记录token的到期时间。我用的KeepassXC支持给每条记录设置过期提醒我会在token创建那天顺手在备注里写到期时间2026-XX-XX到期前一周收到提醒就去换新。如果你用的密码管理器不支持提醒那就新建一个日历事件用手机日历同样能做。看似多了一步实际能帮你避免在某个赶进度的深夜被推送失败突然打断。第三条旧token不要立刻删。当你生成了新token准备替换旧token时不要马上在GitHub后台把旧token删除。先确认新token在所有设备、所有CI环境都配置好了再删旧的。我之前吃过一次亏只更新了本地电脑的token忘了CI环境里还写着旧token一删CI直接全线挂掉。稳妥的做法是旧token保留48小时确认无误后再清理。第四条小心复制token时带入不可见字符。这个坑非常隐蔽。有时候你从网页复制token中间会混入换行符或者空格尤其是当你在终端里粘贴时部分终端会自动补一个回车。这时候你看到的token和实际发送的token不一样认证永远失败但你看不到问题在哪。建议粘贴token之后在终端里跑一下echo 粘贴的内容检查有没有多余的空格或换行再确认无误。第五条定期检查GitHub后台的Active tokens列表。GitHub的Security log会记录所有敏感操作token的创建、删除、授权变更都会留痕。每个月花两分钟扫一眼如果发现某个token在异常时间被访问、或者出现你自己不记得创建过的token立刻去后台撤销掉。这种习惯花的时间极少但可能在某个关键时刻帮你避免一次严重的安全事故。PAT这个东西说到底是GitHub在密码和更高级安全方案之间的一个过渡产物。它比密码安全但比SSH key麻烦它支持细粒度控制但配置复杂度又比SSH高。2026年了还会有大量开发者被困在这个环节本质上就是因为过渡这两个字——它既没有完全退出历史舞台也没有好用到让人一次配置永久无忧。搞清楚它的机制配合合适的场景选择Classic PAT、Fine-grained PAT或者SSH key这件事就能彻底从你的待办清单里划掉。