Skip to content

Added urls:assets config option to serve static theme assets from CDN#27009

Open
muratcorlu wants to merge 1 commit intoTryGhost:mainfrom
muratcorlu:feature/asset-cdn-url
Open

Added urls:assets config option to serve static theme assets from CDN#27009
muratcorlu wants to merge 1 commit intoTryGhost:mainfrom
muratcorlu:feature/asset-cdn-url

Conversation

@muratcorlu
Copy link
Copy Markdown
Contributor

@muratcorlu muratcorlu commented Mar 28, 2026

Ghost already supports CDN base URLs for uploaded content (urls:image, urls:media, urls:files), but static theme assets (/assets/* and /public/*) have always been served from the origin. This adds urls:assets to complete the picture.

When urls:assets is configured, all URLs produced by the {{asset}} helper and Ghost's own public asset tags (cards.min.js, member-attribution.min.js, etc.) are rewritten to use the CDN as the base instead of the origin.

Benefits:

  • Improved page load performance: assets can load in parallel from origin domain
  • Cookie-free domain: serving assets from a separate domain means browsers don't send session cookies with every asset request, reducing request overhead
  • Easier CDN cache management: With the changes on Ghost, no need to drop CDN caches for assets.

Configuration:

  "urls": { "assets": "https://cdn.example.com/my-site" }

Theme assets resolve to:

  https://cdn.example.com/my-site/assets/css/screen.css

Public assets resolve to:

  https://cdn.example.com/my-site/public/cards.min.js

Note: when urls:assets is set, the Ghost subdirectory (if any) is not included in the asset URL. The CDN base URL is used as-is. This is aligned with current CDN url configurations for images, media and files. Installations running Ghost under a subpath (e.g. example.com/blog/) should ensure their CDN is configured to pull from the correct origin path.

Got some code for us? Awesome 🎊!

Please take a minute to explain the change you're making:

  • Why are you making it?
  • What does it do?
  • Why is this something Ghost users or developers need?

Please check your PR against these items:

  • I've read and followed the Contributor Guide
  • I've explained my change
  • I've written an automated test to prove my change works

We appreciate your contribution! 🙏

Ghost already supports CDN base URLs for uploaded content (urls:image,
urls:media, urls:files), but static theme assets (/assets/* and /public/*)
have always been served from the origin. This adds urls:assets to complete
the picture.

When urls:assets is configured, all URLs produced by the {{asset}} helper
and Ghost's own public asset tags (cards.min.js, member-attribution.min.js,
etc.) are rewritten to use the CDN as the base instead of the origin.

Benefits:
- Improved page load performance: assets can load in parallel from origin domain
- Cookie-free domain: serving assets from a separate domain means browsers
  don't send session cookies with every asset request, reducing request
  overhead
- Easier CDN cache management: With the changes on Ghost, no need to drop CDN
  caches for assets.

Configuration:

  "urls": {
    "assets": "https://cdn.example.com/my-site"
  }

Theme assets resolve to:
  https://cdn.example.com/my-site/assets/css/screen.css

Public assets resolve to:
  https://cdn.example.com/my-site/public/cards.min.js

Note: when urls:assets is set, the Ghost subdirectory (if any) is not
included in the asset URL. The CDN base URL is used as-is. This is aligned with current CDN url configurations for images, media and files.Installations
running Ghost under a subpath (e.g. example.com/blog/) should ensure their
CDN is configured to pull from the correct origin path.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 28, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 600030c2-19d0-4889-a547-5639a71ab37a

📥 Commits

Reviewing files that changed from the base of the PR and between d5fca1c and 5aeb303.

📒 Files selected for processing (2)
  • ghost/core/core/frontend/meta/asset-url.js
  • ghost/core/test/unit/frontend/meta/asset-url.test.js

Walkthrough

The getAssetUrl() function has been modified to support asset CDN configuration. When config.get('urls:assets') is available, it constructs an absolute base URL by trimming trailing slashes and appending a forward slash. If the configuration is absent, the function preserves previous behavior by using the subdirectory-relative base. Comprehensive test coverage has been added to validate URL construction for theme and public assets when the CDN base is configured, including edge cases for trailing slashes, cache-busting parameters, anchor fragments, and subdirectory handling.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely describes the main change: adding a urls:assets config option for serving static theme assets from a CDN.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, explaining the feature, its benefits, configuration, and behavior.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
3.3% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

@muratcorlu
Copy link
Copy Markdown
Contributor Author

I am not sure if I need to do anything about Sonar check, since it's mentioning repeating lines on test file, which is completely normal, I think.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant