Feature Testing Guide

July 1, 2026 · View on GitHub

This guide shows how to manually verify that Shakapacker features are working correctly in your application.

Table of Contents

HTTP 103 Early Hints

Prerequisites

  • Rails 5.2+
  • HTTP/2-capable server (Puma 5+ recommended)
  • Modern browser (Chrome/Edge/Firefox 103+, Safari 16.4+)

⚠️ Development Mode Limitations

Early hints require HTTP/2 and will NOT work in standard Rails development mode, which uses HTTP/1.1 by default.

Why localhost testing doesn't work:

  • Early hints require HTTP/2
  • HTTP/2 requires HTTPS/TLS (not http://)
  • Plain http://localhost uses HTTP/1.1
  • Early hints are silently ignored on HTTP/1.1

Debug Mode: Enable debug: true in config to see HTML comments showing what hints were sent (or why they weren't):

# config/shakapacker.yml
development:
  early_hints:
    enabled: true
    debug: true # Outputs debug info as HTML comments

Testing recommendation: Use Method 1 (Browser DevTools) or Method 2 (curl) on production/staging environments with HTTPS enabled. You should see BOTH HTTP/2 103 (early hints) and HTTP/2 200 (final response).

  1. Enable early hints in config:

    # config/shakapacker.yml
    production:
      early_hints:
        enabled: true
    
  2. Open Chrome DevTools (F12 or Cmd+Option+I)

  3. Go to Network tab and reload your page

  4. Look for the initial document request (usually first row)

  5. Check the Status column - you should see:

    • 103 Early Hints (shown briefly before the final response)
    • Followed by 200 OK for the final HTML
  6. Verify Link headers:

    • Click on the document request
    • Go to the "Headers" tab
    • Scroll to "Response Headers" section
    • Look for Link: headers with rel=preload or rel=prefetch

Expected output:

Link: </packs/application-abc123.js>; rel=preload; as=script; crossorigin="anonymous"
Link: </packs/application-xyz789.css>; rel=preload; as=style; crossorigin="anonymous"

Method 2: curl (Command Line)

Test early hints with curl (requires HTTPS/HTTP2):

# Production/staging with HTTPS
curl -v --http2 https://your-app.com 2>&1 | grep -A5 "< HTTP"

# Look for:
# < HTTP/2 103
# < link: </packs/...>; rel=preload
# < HTTP/2 200

⚠️ Local testing limitations:

# This will NOT show early hints (returns HTTP/1.1):
RAILS_ENV=production bundle exec rails server
curl -v --http2 http://localhost:3000 2>&1 | grep -A5 "< HTTP"
# Output: < HTTP/1.1 200 OK (no 103 status)

# Why: Puma requires SSL certificates for HTTP/2
# Early hints need HTTP/2, which needs HTTPS

Expected output:

< HTTP/2 103
< link: </packs/application-abc123.js>; rel=preload; as=script; crossorigin="anonymous"
< link: </packs/application-xyz789.css>; rel=preload; as=style; crossorigin="anonymous"
<
< HTTP/2 200
< content-type: text/html; charset=utf-8

Method 3: Check HTML Source

Early hints don't appear in HTML source (they're sent as HTTP headers before HTML). However, you can verify the assets exist:

<!-- View page source and look for these tags in <head> or before </body> -->
<script src="/packs/application-abc123.js"></script>
<link rel="stylesheet" href="/packs/application-xyz789.css" />

The asset filenames in early hints should match those in your HTML.

Troubleshooting Early Hints

Not seeing 103 status?

  1. Enable debug mode to see what's happening:

    # config/shakapacker.yml
    production: # or development
      early_hints:
        enabled: true
        debug: true # Shows debug info as HTML comments
    

    View page source and look for <!-- Shakapacker Early Hints Debug --> comments showing what hints were sent or why they were skipped.

  2. Reverse proxies and CDNs often strip 103 responses:

    Most common cause: If debug mode shows hints are being sent but you don't see HTTP/2 103 in curl or DevTools, your reverse proxy or CDN is likely stripping the 103 status code before it reaches the client.

    Common culprits:

    • Control Plane (cpln.app)
    • Some Cloudflare configurations
    • nginx without explicit early hints support
    • AWS ALB/ELB
    • Other load balancers and proxies

    How to fix proxy stripping:

    nginx - Enable early hints support:

    # nginx.conf
    http {
      # Enable HTTP/2
      server {
        listen 443 ssl http2;
    
        # Pass through early hints (nginx 1.13+)
        proxy_pass_header Link;
    
        location / {
          proxy_pass http://rails_backend;
          proxy_http_version 1.1;
        }
      }
    }
    

    Cloudflare - Early hints are supported but must be enabled:

    • Go to Speed > Optimization in your Cloudflare dashboard
    • Enable "Early Hints"
    • Note: Only available on paid plans (Pro, Business, Enterprise)

    AWS ALB/ELB - Does NOT support HTTP/2 103:

    • AWS load balancers strip 103 responses
    • Workaround: Deploy without ALB/ELB, or accept Link headers in 200 response
    • Alternative: Use CloudFront with origin that supports 103

    Control Plane (cpln.app) - Appears to strip 103:

    • Control Plane supports HTTP/2 by default but early hints (103) don't appear to pass through
    • No documented configuration option for early hints passthrough
    • Contact Control Plane support if you need early hints support for your application
    • Workaround: Early hints will work server-side but won't be visible to clients
    • Link headers may still be included in 200 response

    General workaround when proxy strips 103:

    • Rails still sends Link headers in the 200 response
    • Browsers can use these for next-page prefetch (not early hints benefit)
    • Consider if early hints are worth the complexity for your setup
    • Use debug mode to verify Rails is sending hints correctly
  3. Check server supports HTTP/2 and early hints:

    # Puma version (need 5+)
    bundle exec puma --version
    
  4. Verify config is enabled:

    # In Rails console
    Shakapacker.config.early_hints
    # Should return: { enabled: true, css: "preload", js: "preload", debug: false }
    
  5. Check Rails log for debug messages:

    tail -f log/production.log | grep -i "early hints"
    
  6. Verify your server uses HTTP/2:

    curl -I --http2 https://your-app.com | grep -i "HTTP/2"
    

Asset Compilation

Verify Assets Compile Successfully

Check manifest.json:

# Development
cat public/packs/manifest.json | jq .

# Production (after precompile)
cat public/packs/manifest.json | jq '.entrypoints'

Expected output:

{
  "entrypoints": {
    "application": {
      "assets": {
        "js": [
          "/packs/vendors~application-abc123.chunk.js",
          "/packs/application-xyz789.js"
        ],
        "css": ["/packs/application-abc123.chunk.css"]
      }
    }
  }
}

Verify assets exist on disk:

ls -lh public/packs/
# Should see .js, .css, .map files with hashed names

Check HTML References

View page source and verify pack tags:

<!-- Should see hashed filenames -->
<link rel="stylesheet" href="/packs/application-abc123.css" />
<script src="/packs/application-xyz789.js"></script>

Test Environment Packs

Development and test builds can write to different output directories. A typical test section in config/shakapacker.yml uses public_output_path: packs-test, so bin/shakapacker in development refreshes public/packs while RAILS_ENV=test bin/shakapacker refreshes public/packs-test.

Before Rails tests that render pack helpers, prerender React components, or depend on React on Rails/RSC manifests, build the test packs explicitly:

RAILS_ENV=test bin/shakapacker
RAILS_ENV=test bin/rails test

If your app uses an external React renderer during tests, start it as your app requires and keep the Rails test command in the test environment:

RAILS_ENV=test REACT_RENDERER_URL=http://127.0.0.1:3800 bin/rails test

In CI, run the test-environment Shakapacker build before the Rails test job:

bundle install
yarn install --frozen-lockfile
RAILS_ENV=test bin/shakapacker
RAILS_ENV=test bin/rails test

For React on Rails or RSC test suites, the test build may also produce server bundles, client-reference manifests, or RSC manifests outside public/packs-test. Those files are still tied to RAILS_ENV=test; a development build does not prove the test manifests are fresh.

Repeated "stale test assets detected" messages are expected after source changes or after removing generated test assets. They point to a misconfiguration when every test run rebuilds immediately after a clean RAILS_ENV=test bin/shakapacker. In that case, compare the test public_output_path, any server/RSC manifest paths, and the environment variables used by the prebuild and test commands.

Code Splitting

Verify Chunks Are Created

Check manifest.json for chunks:

cat public/packs/manifest.json | jq '.entrypoints.application.assets.js'

Expected output (with code splitting):

[
  "/packs/vendors~application-abc123.chunk.js", // Vendor chunk
  "/packs/application-xyz789.js" // Main chunk
]

View Network tab in DevTools:

  • Should see multiple .chunk.js files loading
  • Chunks load in order (vendors first, then application code)

Subresource Integrity (SRI)

Verify Integrity Attributes

Enable SRI in config:

# config/shakapacker.yml
production:
  integrity:
    enabled: true

Check manifest.json for integrity hashes:

cat public/packs/manifest.json | jq '.application.js'

Expected output:

{
  "src": "/packs/application-abc123.js",
  "integrity": "sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
}

Check HTML for integrity attribute:

<!-- View page source -->
<script
  src="/packs/application-abc123.js"
  integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
  crossorigin="anonymous"
></script>

Verify SRI Works

Break the integrity (for testing):

  1. Edit public/packs/application-xyz789.js (add a space)
  2. Reload page
  3. Expected: Browser console shows SRI error:
    Failed to find a valid digest in the 'integrity' attribute
    

Source Maps

Verify Source Maps Generate

Check for .map files:

ls -lh public/packs/*.map
# Should see .js.map and .css.map files

Check HTML references source maps:

curl http://localhost:3000/packs/application-xyz789.js | tail -5

Expected output:

//# sourceMappingURL=application-xyz789.js.map

Verify Source Maps Work in DevTools

  1. Open DevTools → Sources tab
  2. Find your source files under webpack:// or src/
  3. Set a breakpoint in your original source code
  4. Trigger the code - debugger should stop at your source, not compiled output

Development Server

Verify Dev Server Running

Check server status:

# Start dev server
./bin/shakapacker-dev-server

# In another terminal, check it's running
curl http://localhost:3035
# Should return: "Shakapacker is running"

Check Rails connects to dev server:

# Start Rails
./bin/dev  # or rails server

# Check Rails log for:
[Shakapacker] Compiling...
[Shakapacker] Compiled all packs in /app/public/packs

Verify hot reloading:

  1. Edit a JavaScript file in app/javascript/
  2. Save the file
  3. Browser should automatically reload (if HMR configured)
  4. Or check terminal shows recompile message

Troubleshooting Dev Server

Connection refused?

# Check dev_server.yml
cat config/shakapacker.yml | grep -A10 "dev_server"

# Verify settings:
# host: localhost
# port: 3035
# https: false

Test connection manually:

curl http://localhost:3035/packs/application.js
# Should return JavaScript code (not 404)

Testing Checklist

Use this checklist to verify a complete Shakapacker setup:

  • Assets compile: bundle exec rake assets:precompile succeeds
  • Manifest exists: public/packs/manifest.json contains entrypoints
  • Test manifest exists: RAILS_ENV=test bin/shakapacker refreshes public/packs-test/manifest.json before Rails tests that need packs
  • Assets load: Page loads without 404s for pack files
  • Code splitting works: Multiple chunks load in Network tab
  • Source maps work: Can debug original source in DevTools
  • Dev server runs: ./bin/shakapacker-dev-server starts successfully
  • SRI enabled (if configured): HTML contains integrity attributes
  • Early hints work (if configured): DevTools shows 103 status

Common Issues

Assets Return 404

Check manifest:

cat public/packs/manifest.json | jq .

Recompile:

bundle exec rake assets:precompile

Old Assets Cached

Clear public/packs:

rm -rf public/packs
bundle exec rake assets:precompile

Dev Server Won't Start

Check port not in use:

lsof -i :3035
# Kill process if needed
kill -9 <PID>

Check dev_server config:

cat config/shakapacker.yml | grep -A10 dev_server

Additional Resources