CDN Setup Guide for Shakapacker
This guide explains how to configure Shakapacker to serve your JavaScript bundles and other assets from a Content Delivery Network (CDN) like CloudFlare, CloudFront, or Fastly.
Table of Contents
- Overview
- Configuration Methods
- Step-by-Step Setup
- CloudFlare Specific Setup
- Verification
- Troubleshooting
- Advanced Configuration
Overview
When using a CDN with Shakapacker, your compiled JavaScript bundles and other assets will be served from the CDN's edge servers instead of your application server. This provides:
- Reduced latency for users around the world
- Decreased load on your application servers
- Better caching and faster asset delivery
- Improved scalability for high-traffic applications
Configuration Methods
Shakapacker supports CDN configuration through three methods (in order of precedence):
- Environment Variable (highest priority):
SHAKAPACKER_ASSET_HOST - Shakapacker Configuration File:
asset_hostsetting inconfig/shakapacker.yml - Rails Configuration:
Rails.application.config.asset_host
Step-by-Step Setup
1. Configure Your CDN
First, set up your CDN to pull assets from your application's /packs directory. The exact steps depend on your CDN provider, but generally you'll need to:
- Create a CDN distribution/zone
- Set your application's domain as the origin server
- Configure the CDN to cache files from
/packs/*path - Note your CDN URL (e.g.,
https://cdn.example.comorhttps://d1234567890.cloudfront.net)
2. Configure Shakapacker Asset Host
Choose one of the following methods:
Option A: Using Environment Variable (Recommended for Production)
Set the SHAKAPACKER_ASSET_HOST environment variable:
# For production deployment
export SHAKAPACKER_ASSET_HOST=https://cdn.example.com
# Or in your .env file
SHAKAPACKER_ASSET_HOST=https://cdn.example.com
Option B: Using shakapacker.yml
Add the asset_host setting to your config/shakapacker.yml:
production:
# ... other settings ...
asset_host: https://cdn.example.com
# You can also set different CDN hosts per environment
staging:
asset_host: https://staging-cdn.example.com
Option C: Using Rails Configuration
Configure in your Rails environment file (e.g., config/environments/production.rb):
Rails.application.configure do
# ... other settings ...
# This will be used by Shakapacker if SHAKAPACKER_ASSET_HOST
# and asset_host in shakapacker.yml are not set
config.action_controller.asset_host = 'https://cdn.example.com'
end
3. Compile Assets
During deployment, compile your assets as usual:
# The SHAKAPACKER_ASSET_HOST will be used during compilation
# to set the webpack publicPath
RAILS_ENV=production bundle exec rake assets:precompile
This ensures that:
- Webpack's
publicPathis set to your CDN URL - Dynamic imports and code-split chunks load from the CDN
- Asset manifest references use CDN URLs
4. Deploy and Sync Assets
After compilation, ensure your compiled assets in public/packs are accessible to your CDN:
- Push CDN: Upload the files to your CDN's storage
- Pull CDN: Deploy your application normally; the CDN will pull assets on first request
CloudFlare Specific Setup
For CloudFlare CDN setup:
1. Create a CloudFlare Account and Add Your Domain
- Sign up for CloudFlare (if you haven't already)
- Add your domain to CloudFlare
- Update your domain's nameservers to CloudFlare's
2. Configure Page Rules for Assets
Create a page rule for your assets:
- Go to Page Rules in CloudFlare dashboard
- Create a new rule for
*yourdomain.com/packs/* - Set the following settings:
- Cache Level: Cache Everything
- Edge Cache TTL: 1 month (or your preference)
- Browser Cache TTL: 1 month
3. Set Up CloudFlare for Assets Only (Optional)
If you want CloudFlare to only serve your assets (not your entire site):
- Create a CNAME record:
cdn.yourdomain.com→yourdomain.com - Set CloudFlare proxy (orange cloud) ON for this record
- Configure Shakapacker:
export SHAKAPACKER_ASSET_HOST=https://cdn.yourdomain.com
4. Configure CloudFlare Settings
Recommended CloudFlare settings for assets:
- SSL/TLS: Full or Full (Strict)
- Caching Level: Standard or Aggressive
- Browser Cache TTL: Respect Existing Headers
- Always Online: On
- Auto Minify: OFF (Shakapacker already minifies)
Verification
To verify your CDN setup is working:
1. Check Compiled Assets
After compilation, inspect a compiled JavaScript file:
# Look for the publicPath setting in your compiled bundles
grep -r "publicPath" public/packs/js/
You should see your CDN URL in the publicPath configuration.
2. Check Page Source
In production, view your page source and verify script tags use CDN URLs:
<!-- Correct: Assets loading from CDN -->
<script src="https://cdn.example.com/packs/js/application-abc123.js"></script>
<!-- Wrong: Assets loading from relative path -->
<script src="/packs/js/application-abc123.js"></script>
3. Check Network Tab
- Open browser DevTools
- Go to Network tab
- Reload the page
- Verify JavaScript files are loaded from CDN domain
4. Check Dynamic Imports
If using code splitting, verify dynamic chunks load from CDN:
// This dynamic import should load from CDN
import("./components/HeavyComponent").then((module) => {
// Check Network tab - chunk should load from CDN
})
Troubleshooting
Assets Not Loading from CDN
Problem: Assets are still loading from your application domain.
Solutions:
- Ensure you set
SHAKAPACKER_ASSET_HOSTbefore runningassets:precompile - Clear Rails cache:
bundle exec rake tmp:cache:clear - Check the manifest.json file includes CDN URLs:
cat public/packs/manifest.json
CORS Errors
Problem: Browser shows CORS errors when loading assets from CDN.
Solutions:
- Configure your CDN to add CORS headers:
Access-Control-Allow-Origin: * - Or configure for specific domain:
Access-Control-Allow-Origin: https://yourdomain.com
Fonts Not Loading
Problem: Web fonts fail to load from CDN due to CORS.
Solutions:
- Ensure CDN sends proper CORS headers for font files
- In CloudFlare, create a page rule for
*.woff2,*.woff,*.ttffiles with CORS headers - Consider hosting fonts separately or using base64 encoding
Development Environment Issues
Problem: CDN URLs appearing in development environment.
Solution: Only set SHAKAPACKER_ASSET_HOST in production:
# config/environments/development.rb
# Ensure asset_host is NOT set in development
# config/environments/production.rb
# Set asset_host only in production
Advanced Configuration
Using Different CDNs for Different Assets
You can use Rails asset host proc for dynamic CDN selection:
# config/environments/production.rb
config.action_controller.asset_host = Proc.new do |source|
if source =~ /\.(js|css)$/
'https://js-css-cdn.example.com'
else
'https://images-cdn.example.com'
end
end
CDN with Integrity Hashes
When using Subresource Integrity (SRI) with CDN:
# config/shakapacker.yml
production:
asset_host: https://cdn.example.com
integrity:
enabled: true
hash_functions: ["sha384"]
cross_origin: "anonymous"
Ensure your CDN serves files with CORS headers:
Access-Control-Allow-Origin: *
Multiple CDN Domains for Parallel Downloads
For HTTP/1.1 optimization (less relevant with HTTP/2):
# config/environments/production.rb
config.action_controller.asset_host = Proc.new do |source|
"https://cdn#{Digest::MD5.hexdigest(source)[0..2].to_i(16) % 4}.example.com"
end
# This creates cdn0.example.com through cdn3.example.com
Cache Busting
Shakapacker automatically includes content hashes in production:
# config/shakapacker.yml
production:
# This is already true by default in production
useContentHash: true
This ensures CDN caches are invalidated when content changes.
Preloading Critical Assets
Use Rails helpers to preload critical assets from CDN:
<%= preload_pack_asset 'application.js' %>
<%= preload_pack_asset 'application.css' %>
Security Considerations
- Use HTTPS: Always use HTTPS for your CDN URL to prevent mixed content warnings
- Configure CSP: Update Content Security Policy headers to allow CDN domain:
# config/initializers/content_security_policy.rb
Rails.application.config.content_security_policy do |policy|
policy.script_src :self, 'https://cdn.example.com'
policy.style_src :self, 'https://cdn.example.com'
end - Use SRI: Enable Subresource Integrity for additional security
- Monitor CDN: Set up monitoring for CDN availability and performance
Example Configuration
Here's a complete example for a production setup with CloudFlare:
# config/shakapacker.yml
production:
compile: false
cache_manifest: true
asset_host: <%= ENV.fetch('SHAKAPACKER_ASSET_HOST', 'https://cdn.example.com') %>
# Enable integrity checking
integrity:
enabled: true
hash_functions: ["sha384"]
cross_origin: "anonymous"
# config/environments/production.rb
Rails.application.configure do
# Fallback if SHAKAPACKER_ASSET_HOST is not set
config.action_controller.asset_host = 'https://cdn.example.com'
# Ensure proper headers for CDN
config.public_file_server.headers = {
'Cache-Control' => 'public, max-age=31536000',
'X-Content-Type-Options' => 'nosniff'
}
end
# Deployment script
export SHAKAPACKER_ASSET_HOST=https://cdn.example.com
RAILS_ENV=production bundle exec rake assets:precompile
Summary
Setting up a CDN with Shakapacker involves:
- Configuring your CDN service
- Setting the
SHAKAPACKER_ASSET_HOSTenvironment variable - Compiling assets with the CDN URL
- Deploying and verifying the setup
The key is ensuring SHAKAPACKER_ASSET_HOST is set during asset compilation so webpack's publicPath is configured correctly for dynamic imports and code-split chunks.