---
name: enable-github-pages-custom-domain
description: Enable GitHub Pages for a repository with a custom domain via API, add .nojekyll, verify.
category: devops
---

# Enable GitHub Pages for a repository with a custom domain

This skill automates enabling GitHub Pages via the REST API, adds a `.nojekyll` file to prevent Jekyll processing, and verifies the site is serving content. It assumes you have a Personal Access Token (PAT) with `repo` scope and that the repository already contains a `CNAME` file with the desired domain.

## When to use
- You want to programmatically enable GitHub Pages for a repo (instead of using the web UI).
- The repo uses a `gh-pages` branch (or any branch) as the source.
- You have a custom domain configured via a `CNAME` file and need to set DNS separately.
- You want to ensure `.nojekyll` is present to serve static HTML as-is.

## Prerequisites
- `curl` or Python with `urllib` available (examples use Python).
- GitHub PAT stored securely (e.g., in memory or environment variable).
- Repository exists and you have push access.
- `CNAME` file already committed to the source branch (e.g., `gh-pages`).

## Steps

### 1. Verify repository and branch
```bash
git ls-remote https://github.com/<owner>/<repo>.git gh-pages
```
If the branch does not exist, create an orphan `gh-pages` branch and push an empty commit.

### 2. Ensure CNAME file exists
Check that the `gh-pages` branch contains a `CNAME` file with your domain (e.g., `sailor.bar`).

### 3. Enable GitHub Pages via API
Use the GitHub REST API to set the Pages source.

**Endpoint:** `PATCH /repos/{owner}/{repo}/pages`

**Headers:**
- `Authorization: token <PAT>`
- `Accept: application/vnd.github+json`

**Body (JSON):**
```json
{
  "source": {
    "branch": "gh-pages",
    "path": "/"
  }
}
```

**Example (Python):**
```python
import json, urllib.request, urllib.error

def enable_pages(owner, repo, pat):
    url = f"https://api.github.com/repos/{owner}/{repo}/pages"
    data = json.dumps({
        "source": {"branch": "gh-pages", "path": "/"}
    }).encode()
    req = urllib.request.Request(url, data=data, method='PATCH')
    req.add_header('Authorization', f'token {pat}')
    req.add_header('Accept', 'application/vnd.github+json')
    req.add_header('Content-Type', 'application/json')
    try:
        with urllib.request.urlopen(req) as resp:
            return resp.status, resp.read()
    except urllib.error.HTTPError as e:
        return e.code, e.read()
```

A `201` or `202` indicates acceptance; a `204` means already enabled. A `409` may occur if a Pages build is in progress—wait and retry.

### 4. Wait for Pages to be ready (optional)
Poll the Pages status endpoint until it returns `200` and the JSON includes `"status": "built"`.

**Endpoint:** `GET /repos/{owner}/{repo}/pages`

Repeat every 5‑10 seconds for up to 2 minutes.

### 5. Add `.nojekyll` file (if missing)
To prevent Jekyll processing, add an empty `.nojekyll` file to the `gh-pages` branch.

**Endpoint:** `PUT /repos/{owner}/{repo}/contents/.nojekyll?ref=gh-pages`

**Body (JSON):**
```json
{
  "message": "Add .nojekyll to disable Jekyll",
  "content": "",
  "branch": "gh-pages"
}
```
The `content` must be base64‑encoded; an empty string encodes to `""`.

If the file already exists, the API will return a `422`; you can ignore or fetch the current SHA and update.

### 6. Verify the site
- **Via raw.githubusercontent.com** (works immediately):
  ```
  https://raw.githubusercontent.com/<owner>/<repo>/gh-pages/index.html
  ```
  Should return `200` and your HTML.
- **Via custom domain** (after DNS is configured):
  ```
  http://<your-domain>/
  https://<your-domain>/
  ```
  Should return `200` and the same content.

### 7. (Optional) Configure DNS
Set an A‑record for your domain to GitHub Pages IPs:
```
185.199.108.153
185.199.109.153
185.199.110.153
185.199.111.153
```
Or a CNAME to `<owner>.github.io` (only for subdomains; apex domains need A‑records or ALIAS).

Wait for DNS propagation (can take minutes to hours).

## Pitfalls & Troubleshooting
- **404 when checking Pages status before enabling**: Pages must be enabled first; the endpoint returns 404 until then.
- **409 Conflict on enable**: Indicates a Pages build is already in progress. Wait a few seconds and retry the PATCH.
- **Site still 404 after enabling**: Verify that the `gh-pages` branch contains an `index.html` at the root. Use `raw.githubusercontent.com` to confirm content is present.
- **Custom domain shows GitHub Pages placeholder 404**: DNS is not pointing to GitHub’s servers. Ensure A‑records are set and propagate.
- **.nojekyll not respected**: Ensure the file is committed to the `gh-pages` branch (not just main/master).
- **PAT lacking scopes**: Needs `repo` scope for private repos; `public_repo` is enough for public.

## Verification
After completing the steps, run:
```bash
curl -I http://<your-domain>/
```
Look for `HTTP/2 200` and a content-length matching your index.html size.

## Example usage (complete flow)
```python
owner = "valldun1"
repo = "Sailor-bar"
pat = "<YOUR_PAT>"

# 1. Enable Pages
enable_pages(owner, repo, pat)

# 2. Wait for built status (poll GET /repos/.../pages)

# 3. Add .nojekyll
add_nojekyll(owner, repo, pat)

# 4. Verify via raw URL
verify_raw(owner, repo)
```
Replace helper functions with the API calls shown above.

## Notes
- This skill does **not** handle DNS configuration; that must be done separately via your domain registrar or DNS provider.
- If you prefer to use the `gh` CLI, you can achieve similar results with `gh api` calls, but the REST API approach works in any environment with HTTP access.
- The skill is idempotent: re‑running it after the site is already enabled will not cause errors.
