jsDelivr GitHub CDN
WordPress plugin — host images on GitHub, serve free forever via jsDelivr CDN. Zero server storage.
Install
No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:
wp plugin install https://github.com/idreeswork/jsdelivr-github-cdn/archive/refs/heads/main.zipjsDelivr GitHub CDN — WordPress Plugin
Host images on GitHub. Serve them free forever via jsDelivr CDN. Zero server storage.
What it does
Push images from WordPress to a public GitHub repository. jsDelivr serves them globally via CDN using permanent commit-based URLs — cached in jsDelivr's S3 forever, even if you delete the repo.
Your WordPress server never stores the images. No bandwidth. No disk usage. No CDN bills.
Your site → Plugin uploads → GitHub repo → jsDelivr CDN → Visitor
↓
assets.yourdomain.com (optional)
Features
- Upload tab — drag and drop, paste from clipboard (Ctrl+V), editable filename slugs
- Bulk Migrate — push all existing WordPress media to CDN with live progress log
- Media Library — grid view with CDN status, push/revert per image
- Apply to Post modal — set featured image + alt text on any post without leaving the plugin
- Alt text auto-fill — pulls from Rank Math focus keyword → SEO title → post title
- FIFU integration — uses
fifu_dev_set_image()official API for featured images - WebP conversion — auto-converts JPEG/PNG to WebP before uploading (quality 90)
- Duplicate detection — MD5 hash check before every upload, reuses existing URL
- Media Base URL — serve images from your own domain (
assets.yourdomain.com) via Cloudflare Worker, decoupled from GitHub identity - URL migration tool — bulk rewrite stored URLs when switching repos or providers
- Stable slug filenames —
best-llm-course.webpnot2026-03-17T18-30-59-abc.webp - SPA tab switching — no page reload between tabs
- Token health monitoring — expiry warnings, rate limit alerts, repo size display
Requirements
| Requirement | Minimum |
|---|---|
| WordPress | 6.0 |
| PHP | 8.1 |
| GitHub account | Free |
| Cloudflare account | Free (optional, for custom domain) |
| FIFU plugin | Optional (for featured image support) |
| Rank Math SEO | Optional (for alt text auto-fill) |
Installation
1. Create a GitHub repository
Create a public repository (e.g. assets) in your GitHub account or organization. jsDelivr requires the repo to be public.
2. Create a GitHub token
Go to GitHub → Settings → Developer Settings → Fine-grained tokens
- Repository access: Only select repositories → choose your CDN repo
- Permissions: Contents → Read and Write
- Expiration: No expiration (recommended)
3. Install the plugin
Download the latest zip from Releases and install via Plugins → Add New Plugin → Upload Plugin.
4. Configure
Go to jsDelivr CDN → Settings:
| Field | Value |
|---|---|
| Personal Access Token | Your fine-grained token |
| GitHub Username | Your GitHub username or org name |
| Repository Name | Your CDN repo name (e.g. assets) |
| Branch | main |
| Upload Folder | uploads |
Click Test Connection — you should see Connected: username/reponame.
Optional: Custom domain via Cloudflare Worker
Serves images from assets.yourdomain.com instead of raw jsDelivr URLs. This decouples your WordPress content from GitHub identity — if you rename the repo or change providers, update one line in the Worker. Zero WordPress database changes needed.
Step 1: Create the Worker
In Cloudflare Dashboard → Workers & Pages → Create → Hello World
Replace the default code with:
export default {
async fetch(request) {
const url = new URL(request.url);
const path = url.pathname;
// Only serve image files — block directory browsing
if (!path.match(/\.(webp|jpg|jpeg|png|gif|svg|avif)$/i)) {
return new Response('Not found.', { status: 404 });
}
// Update ONLY this line when repo/account changes
const BACKEND = 'https://cdn.jsdelivr.net/gh/YOUR_USERNAME/YOUR_REPO@main';
const response = await fetch(BACKEND + path, {
cf: { cacheEverything: true, cacheTtl: 604800 }
});
const headers = new Headers(response.headers);
headers.set('Access-Control-Allow-Origin', '*');
headers.set('Cache-Control', 'public, max-age=604800, immutable');
return new Response(response.body, {
status: response.status,
headers: headers,
});
}
}
Replace YOUR_USERNAME/YOUR_REPO with your values. Click Deploy.
Step 2: Add custom domain route
Workers & Pages → your Worker → Settings → Domains & Routes → Add Custom Domain → type assets.yourdomain.com
If the DNS record is not auto-created, add it manually in DNS → Records:
| Type | Name | IPv6 address | Proxy |
|---|---|---|---|
| AAAA | assets | 100:: |
Proxied (orange) |
Step 3: Set Media Base URL
In the plugin Settings tab → Media Base URL → enter https://assets.yourdomain.com → Save.
All new uploads will generate https://assets.yourdomain.com/uploads/filename.webp as the stored URL.
Step 4: Migrate existing URLs
A migration panel appears in Settings after setting the Media Base URL. Click Migrate 100 URLs — it runs in batches and rewrites stored jsDelivr URLs to use your domain. Safe to run multiple times. Existing jsDelivr URLs keep working during migration.
How URLs work
| Mode | Stored URL format |
|---|---|
| No base URL | https://cdn.jsdelivr.net/gh/user/repo@sha/uploads/file.webp |
| With base URL | https://assets.yourdomain.com/uploads/file.webp |
Commit-based jsDelivr URLs are permanent forever — cached in jsDelivr's S3 on first request, served from there even if the GitHub repo is deleted.
Upload workflow
- Go to jsDelivr CDN → Upload
- Drag images, browse, or paste from clipboard (Ctrl+V)
- Edit the filename slug per image (e.g.
best-llm-course) — the.webpextension is added automatically - Click Upload All
- After upload completes, the Apply to Post modal opens automatically
- Search for a course or post, select it
- Alt text is auto-populated from Rank Math focus keyword (or SEO title, or post title)
- Edit alt text if needed
- Click Set as Featured Image
Bulk migration
- Go to jsDelivr CDN → Bulk Migrate
- Optionally enable Convert to WebP during migration
- Click Push Remaining to CDN
- Migration runs ~1 image/second with live log — pause and resume any time
- After Step 1 completes, use Free Server Space to delete local copies
Post meta stored per attachment
| Meta key | Value |
|---|---|
_jcdn_jsdelivr_url |
Public CDN URL (or base URL) |
_jcdn_remote_path |
Stable path in repo e.g. uploads/best-llm-course.webp |
_jcdn_commit_sha |
GitHub commit SHA |
_jcdn_migrated_at |
Datetime pushed to CDN |
Frequently asked questions
What happens to old images if I rename the GitHub repo?
Commit-based jsDelivr URLs are permanently cached — they serve forever regardless of what happens to the repo. If you set up a custom domain via Worker, update the BACKEND const in the Worker. That's it.
Does this work with organizations? Yes. Set GitHub Username to your organization name. The fine-grained token must be created in your personal account settings and granted access to the org repo.
Does it work without FIFU? Yes. FIFU is only needed for featured images sourced from external URLs. The upload, CDN hosting, and URL management work independently.
What is the storage limit? jsDelivr recommends keeping repos under 1 GB. At ~50 KB average WebP that is ~20,000 images. When full, create a new repo and update Settings — old URLs keep working forever.
Is jsDelivr free? Yes for open source use. See jsDelivr's terms.
What is the Cloudflare Worker request limit? Free plan: 100,000 requests/day. After the first browser load, images are cached locally — subsequent page views do not hit the Worker. For most sites this limit is never reached.
Architecture overview
WordPress Admin
│
▼
jsDelivr CDN Plugin
│
├─ Upload Tab ──────────► GitHub API (PUT /repos/{owner}/{repo}/contents/{path})
│ │
│ ▼
│ GitHub Repository (public)
│ │
│ ▼
├─ CDN Filter ──────────► jsDelivr CDN (cdn.jsdelivr.net/gh/{owner}/{repo}@{sha}/...)
│ │
│ [optional Cloudflare Worker]
│ │
└─ Frontend ────────────► assets.yourdomain.com/uploads/filename.webp
WordPress stores only a URL reference. No image bytes on your server.
Development
/
├── jsdelivr-github-cdn.php Bootstrap, constants
├── includes/
│ ├── Plugin.php REST API routes (upload, migrate, revert, pending, set-featured, post-meta, migrate-urls)
│ ├── GitHub/
│ │ └── Uploader.php GitHub Contents API, slug generation, conflict resolution
│ ├── Media/
│ │ └── CDNFilter.php Frontend URL rewriting (admin/REST/CLI/cron safe)
│ └── Admin/
│ ├── Dashboard.php 4-tab SPA dashboard
│ └── MediaLibraryIntegration.php Media Library column + attachment meta box
└── admin/
├── css/dashboard.css
└── js/dashboard.js Upload queue, modal, history, migrate, SPA tabs
Changelog
2.3.0
- Editable filename slugs in upload queue —
best-llm-course.webpinstead of timestamp filenames - Slug conflict resolution — auto-appends
-2,-3when slug already exists - Ctrl+V clipboard paste support in upload tab
2.2.0
- Media Base URL setting — serve from your own domain via Cloudflare Worker
- URL migration tool — bulk rewrite stored URLs to new domain
- Batch upload fix — modal opens once after full batch, not per image
- 2-concurrent upload semaphore for faster batch processing
2.1.0
- Apply to Post modal — replaces inline panel, works from any tab
- Alt text auto-fill from Rank Math focus keyword / SEO title / post title
- Current featured image preview before replacing
- FIFU official API (
fifu_dev_set_image) + correct alt key (fifu_image_alt) - SPA tab switching — no page reload between tabs
- Revert endpoint — remove CDN URL from attachment
- Pending endpoint — efficient bulk migrate (only unmigrated images)
2.0.0
- Initial public release
License
GPL-2.0-or-later — see LICENSE