遇到 git clone 提示 “Permission denied (publickey)” 时,很多人的第一反应是检查密钥对是否生成、公钥是否上传。但如果你已经确认这些步骤无误,问题很可能出在一个更隐蔽的环节:SSH 客户端根本没有按你的预期去使用指定的密钥。

为什么 git clone 总提示 Permission denied (publickey)
根本原因在于,SSH 默认的行为路径与你想象的不同。当你执行 git clone git@github.com:user/repo.git 时,Git 本身并不处理密钥,它只是调用了底层的 ssh 命令。而 ssh 命令在连接 git@github.com 这个地址时,默认只会尝试使用 ~/.ssh/id_rsa 这把密钥(或者按默认顺序尝试 id_dsa, id_ecdsa, id_ed25519)。
真正决定“为哪个地址使用哪把密钥”的,是 ~/.ssh/config 文件里的路由规则。但这里有个关键前提:Git 使用的远程仓库地址(remote URL)必须精确匹配 config 文件中你定义的 Host 别名。如果地址不匹配,所有配置都是白费功夫。
常见的错误现象包括:
- 明明为工作账号生成了
id_ed25519_work,但执行ssh -T git@github.com测试时,依然走的是默认的id_rsa。 - 在
config里配置了Host github-work,但仓库的 remote 地址还是原始的git@github.com:user/repo.git,导致配置完全没生效。 - 使用
ssh-add -l能看到多把密钥已加载到袋里中,但执行 Git 推送操作时依然失败。
怎么写对 ~/.ssh/config 才能生效
这个文件不是可有可无的“高级配置”,而是实现多密钥精准路由的“唯一开关”。要让配置生效,必须同时满足以下几个硬性条件,缺一不可:
Host必须是自定义别名:比如github-personal,不能直接写成真实域名github.com。真实域名应该填在下一行的HostName里。IdentityFile必须使用绝对路径:例如/home/you/.ssh/id_ed25519_personal。在config文件里,波浪线~不会被自动展开为用户目录,所以写相对路径会失效。- 格式必须严格:每组配置之间建议空一行,且不要使用中文标点或多余的缩进。
一个正确的配置示例是这样的:
Host github-personal HostName github.com User git IdentityFile /home/you/.ssh/id_ed25519_personal Host github-work HostName github.com User git IdentityFile /home/you/.ssh/id_ed25519_work
这里有几个高频错误点:漏掉了 User git(GitHub、GitLab 等服务端都强制要求使用 git 用户连接);在 IdentityFile 中使用了 ~/.ssh/... 这样的相对路径;或者把 Host 写成了 github.com-personal,但后续却没有相应地修改 Git 仓库的远程地址。
remote URL 必须手动改成 Host 别名
这是整个流程中最容易被忽略、却至关重要的一步。即便你的 ~/.ssh/config 写得完美无缺,如果 Git 仓库的远程地址(remote URL)还是原来的样子,SSH 依然会直接连接真实域名,完全绕过你精心设置的路由规则。
因此,配置完成后,必须对所有现有仓库的 remote 地址进行更新:
- 查看当前地址:
git remote get-url origin - 修改为对应的 Host 别名:
git remote set-url origin git@github-personal:me/repo.git - 未来克隆新仓库时,也必须使用别名地址:
git clone git@github-work:company/project.git
这里有个重要的测试技巧:验证配置是否生效,应该使用 ssh -T git@github-personal(使用你定义的别名)。而直接使用 ssh -T git@github.com 永远只会测试默认密钥,无法用于验证多账号配置。
不同平台(GitHub/GitLab/Gitee)要分开 Host 块
一个 Host 配置块只能绑定一个 HostName。如果你需要同时管理多个代码托管平台(例如 GitHub 个人账号、GitLab 公司账号、Gitee 团队账号),就必须为每个平台分别编写独立的配置块。
- GitHub 个人账号:
Host github-personal→HostName github.com - GitLab 公司账号:
Host gitlab-company→HostName gitlab.com - Gitee 团队账号:
Host gitee-team→HostName gitee.com
不要试图在一个 Host 块里写多个 HostName,SSH 协议不支持这种写法。另外,从安全和管理角度考虑,也尽量不要让不同平台共用同一把私钥。有些平台会校验关联邮箱或限制密钥复用,可能导致某一方的认证失败。
最后,别忘了检查文件和目录权限,这是一个比想象中更常见的“坑”。~/.ssh/config 文件的权限必须是 600,同样,IdentityFile 指向的私钥文件权限也必须是 600。如果权限不对,SSH 出于安全考虑会直接忽略这条配置。可以使用 chmod 600 ~/.ssh/config ~/.ssh/id_* 命令快速修正。
