---
title: "Enforce SSH instead of HTTPS for Git operations"
description: "GitHub stopped accepting passwords for Git this month. Keys were the better answer anyway — here is a setup that also survives tools with HTTPS URLs baked in."
author: "Omar Albeik"
date: 2021-08-23
type: note
topics: [software, engineering]
language: en
reading_time_minutes: 3
canonical_url: https://albeik.dev/en/blog/enforce-ssh-instead-of-http-for-git-operations
translation_url: https://albeik.dev/ar/blog/enforce-ssh-instead-of-http-for-git-operations
source_url: https://albeik.dev/en/blog/enforce-ssh-instead-of-http-for-git-operations.md
---

# Enforce SSH instead of HTTPS for Git operations

Ten days ago GitHub stopped accepting account passwords for Git operations over
HTTPS. If you push over HTTPS you now paste a personal access token instead —
a longer secret, kept in the same place the password was, expiring on a
schedule you have to remember.

SSH avoids the question. You generate a key pair once, keep the private half on
your machine, hand the public half to the host, and stop typing credentials for
Git entirely. It also authenticates the server to you, which a token does not.

Setup takes about five minutes. The last two steps are the ones most guides
leave out, and they are the reason this sticks.

## Generate a key

```bash
ssh-keygen -t ed25519 -C "personal github" -f ~/.ssh/personal-github
```

Ed25519 rather than RSA: shorter keys, faster handshakes, and no key-size
argument to get wrong. If you have to talk to something older than OpenSSH 6.5,
`-t rsa -b 4096` is the fallback.

Name the key after what it unlocks — `personal-github`, `work-gitlab` — and
make one per account rather than one per machine. Accounts get revoked
individually, and a key named `id_ed25519_2` tells you nothing on the day you
need to revoke it.

Use a passphrase. The agent means you type it once per session, not once per
push.

## Add the public key to the host

Copy the public half — the file ending in `.pub`, never the other one:

```bash
pbcopy < ~/.ssh/personal-github.pub
```

Then paste it into your account settings:
[GitHub](https://docs.github.com/en/github/authenticating-to-github/adding-a-new-ssh-key-to-your-github-account),
[GitLab](https://docs.gitlab.com/ee/ssh/), or
[Bitbucket](https://support.atlassian.com/bitbucket-cloud/docs/set-up-an-ssh-key/).

## Rewrite HTTPS URLs to SSH

A key does nothing while your remotes still say `https://`. Instead of fixing
them one repository at a time, tell Git to substitute globally, in
`~/.gitconfig`:

```ini
[url "ssh://git@github.com/"]
  insteadOf = https://github.com/

[url "ssh://git@gitlab.com/"]
  insteadOf = https://gitlab.com/

[url "ssh://git@bitbucket.org/"]
  insteadOf = https://bitbucket.org/
```

Every HTTPS URL for those hosts is now rewritten before the request leaves your
machine — including the URLs you did not write. That covers CocoaPods specs,
`go get`, npm dependencies that point at a Git repository, submodules cloned
from someone else's `.gitmodules`, and CI scripts you would rather not touch.

This is the step that turns "I set up a key" into "I never see a credential
prompt again".

## Tell SSH which key to use

Git speaks SSH now, but SSH still has to choose a key. Left alone it tries the
default names, so a key called `personal-github` is never offered. In
`~/.ssh/config`:

```ssh-config
Host github.com
  User git
  IdentityFile ~/.ssh/personal-github
  IdentitiesOnly yes
  AddKeysToAgent yes

Host gitlab.com
  User git
  IdentityFile ~/.ssh/work-gitlab
  IdentitiesOnly yes
  AddKeysToAgent yes
```

`IdentitiesOnly yes` earns its line. Without it SSH offers every key the agent
holds, in whatever order it likes, and a host that sees the wrong key first can
reject you before the right one is ever tried — which reads as a permissions
bug and is not one.

On macOS, add `UseKeychain yes` to each host block and the passphrase survives
a restart.

## Check it

```bash
ssh -T git@github.com
```

A greeting with your username means the key works. To confirm the rewriting,
clone something over HTTPS and watch it go through without a prompt:

```bash
git ls-remote https://github.com/omaralbeik/SwifterSwift.git
```

If that returns refs without asking for anything, both halves are in place: Git
is rewriting the URL, and SSH is picking the right key for it.
