shakapacker 9.3.0.beta.4 → 9.3.0.beta.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2f2fafddfa10d364b8e62946786c16d9206ecffadf82b719e3ffeb2100608da0
4
- data.tar.gz: 0e0cdc08d53e64e8066d7232aba1022b6a7708067d56ff7ba738803bfbc49a78
3
+ metadata.gz: 9db05ecb02b4491f31eb4ec2374057933be5cf90a09b6dded781c036ff8e494e
4
+ data.tar.gz: a8f9b04812aff8a73275044f61a1ecbc096c5131a3bca69d5b75e292c1c31064
5
5
  SHA512:
6
- metadata.gz: dcbb4604f956d78acaea611bb760ecea97a1afcc15bc390b9c5a64c5d9654f5d08e83c57ed24da55aca0a1a98da074a0047a754cadf5f74ee8ba832fb6e93b3e
7
- data.tar.gz: 47b7d7887f770f5712b5f020afd260fcefcb4b09a0c707e1b2bc921c38d2944bf6045b6b9752295790806018d98cd95d43313f9079719bcb3c808ff9f6e6c2ad
6
+ metadata.gz: 88f63fdaaed2cfad42f000d97e08fec78dddaa24b304dd23ae515ba24b5b68845e33115adf13994f0a59f85b6be4003a442e8d5f88a1a9295484ca0b31e07fc1
7
+ data.tar.gz: 59069f26686a609a9df9994ec63a1cbc5905d18abbb7379c0460913f16ac58247e760d97eecc544a31a86e212d6af1d171b99f462f4b60b937d0da81d3415a7f
data/CHANGELOG.md CHANGED
@@ -11,6 +11,26 @@
11
11
 
12
12
  Changes since the last non-beta release.
13
13
 
14
+ ### Added
15
+
16
+ - **HTTP 103 Early Hints support** for faster asset loading. [PR #722](https://github.com/shakacode/shakapacker/pull/722) by [justin808](https://github.com/justin808). Fixes [#721](https://github.com/shakacode/shakapacker/issues/721).
17
+ - **Automatic sending**: Early hints are sent automatically when `early_hints: enabled: true` in `shakapacker.yml`
18
+ - **Per-page configuration**: Configure hints per-controller/action with `preload`/`prefetch`/`none` options
19
+ - **Hero image/video preloading**: Use Rails' built-in `preload_link_tag` (automatically sends early hints)
20
+ - **Zero-config upgrade**: No changes to layouts or views needed - just enable the config!
21
+ - Works seamlessly with existing `append_javascript_pack_tag` / `append_stylesheet_pack_tag` pattern
22
+ - Automatically discovers packs from queues populated by views/partials
23
+ - New `configure_pack_early_hints` class method for controller-level configuration
24
+ - New `skip_send_pack_early_hints` helper to opt-out for specific controllers (e.g., JSON APIs)
25
+ - Optional manual control: `configure_early_hints` can be called in controllers or views
26
+ - Added `early_hints:` option to `javascript_pack_tag` for per-tag control
27
+ - New `early_hints` configuration in `shakapacker.yml` with per-environment settings (`enabled: false` by default)
28
+ - Requires Rails 5.2+ and HTTP/2-capable server (e.g., Puma 5+)
29
+ - Browser support: All modern browsers (Chrome/Edge/Firefox 103+, Safari 16.4+)
30
+ - Gracefully degrades if not supported
31
+ - **Performance note**: May improve or hurt page load performance depending on content - careful testing advised
32
+ - See [Early Hints Guide](docs/early_hints.md) for detailed usage and advanced patterns
33
+
14
34
  ## [v9.3.0-beta.0] - October 13, 2025
15
35
 
16
36
  ### Added
data/CLAUDE.md CHANGED
@@ -27,6 +27,19 @@
27
27
  - Create small, focused PRs that are easy to review
28
28
  - Always create a PR immediately after pushing changes
29
29
 
30
+ ## Changelog
31
+
32
+ - **Update CHANGELOG.md for user-visible changes only**
33
+ - User-visible changes include: new features, bug fixes, breaking changes, deprecations, performance improvements
34
+ - **Do NOT add changelog entries for**: linting fixes, code formatting, internal refactoring, test updates, documentation fixes
35
+ - Non-user-visible changes don't need changelog entries even if they modify code
36
+ - **Format requirements**:
37
+ - Always link to the PR: `[PR #123](https://github.com/shakacode/shakapacker/pull/123)`
38
+ - Always link to the author: `by [username](https://github.com/username)`
39
+ - Keep formatting consistent with existing entries
40
+ - When releasing a version, update the version diff links at the bottom of CHANGELOG.md
41
+ - **For breaking changes**: Use bold formatting and link to migration documentation (e.g., `**Breaking**: Description. See [Migration Guide](docs/vX_upgrade.md)`)
42
+
30
43
  ## Shakapacker-Specific
31
44
 
32
45
  - This gem supports both webpack and rspack configurations
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- shakapacker (9.3.0.beta.4)
4
+ shakapacker (9.3.0.beta.5)
5
5
  activesupport (>= 5.2)
6
6
  package_json
7
7
  rack-proxy (>= 0.6.1)
data/README.md CHANGED
@@ -78,6 +78,7 @@ Read the [full review here](https://clutch.co/profile/shakacode#reviews?sort_by=
78
78
  - [View Helper: `image_pack_tag`](#view-helper-image_pack_tag)
79
79
  - [View Helper: `favicon_pack_tag`](#view-helper-favicon_pack_tag)
80
80
  - [View Helper: `preload_pack_asset`](#view-helper-preload_pack_asset)
81
+ - [View Helper: `send_pack_early_hints`](#view-helper-send_pack_early_hints)
81
82
  - [Images in Stylesheets](#images-in-stylesheets)
82
83
  - [Server-Side Rendering (SSR)](#server-side-rendering-ssr)
83
84
  - [Development](#development)
@@ -563,6 +564,24 @@ If you want to preload a static asset in your `<head>`, you can use the `preload
563
564
  <%= preload_pack_asset 'fonts/fa-regular-400.woff2' %>
564
565
  ```
565
566
 
567
+ #### HTTP 103 Early Hints
568
+
569
+ Automatically send early hints to browsers for faster asset loading. Supports `preload`/`prefetch`/`none` configuration per-page.
570
+
571
+ ```yaml
572
+ # config/shakapacker.yml
573
+ production:
574
+ early_hints:
575
+ enabled: true
576
+ debug: false # Enable to see what hints are sent (as HTML comments)
577
+ ```
578
+
579
+ ⚠️ **Important**: May improve or hurt performance depending on content. See the [Early Hints Guide](./docs/early_hints.md) for configuration, performance guidance, and examples.
580
+
581
+ **Troubleshooting**: Enable `debug: true` to see HTML comments showing what hints were sent or why they were skipped.
582
+
583
+ **Requirements:** Rails 5.2+, HTTP/2 server, modern browsers. Gracefully degrades if not supported.
584
+
566
585
  ### Images in Stylesheets
567
586
 
568
587
  If you want to use images in your stylesheets:
@@ -11,6 +11,8 @@ This guide covers all configuration options available in `config/shakapacker.yml
11
11
  - [Development Server](#development-server)
12
12
  - [Compilation Options](#compilation-options)
13
13
  - [Advanced Options](#advanced-options)
14
+ - [Subresource Integrity](#integrity)
15
+ - [Early Hints](#early_hints)
14
16
  - [Environment-Specific Configuration](#environment-specific-configuration)
15
17
  - [Build Configurations (config/shakapacker-builds.yml)](#build-configurations-configshakapacker-buildsyml)
16
18
 
@@ -485,6 +487,37 @@ integrity:
485
487
 
486
488
  See [Subresource Integrity Guide](subresource_integrity.md) for details.
487
489
 
490
+ ### `early_hints`
491
+
492
+ **Type:** `object`
493
+ **Default:** `{ enabled: false, css: "preload", js: "preload" }`
494
+ **Requires:** Rails 5.2+, HTTP/2 server
495
+
496
+ Automatically send HTTP 103 Early Hints for faster asset loading.
497
+
498
+ ```yaml
499
+ early_hints:
500
+ enabled: true # Master switch (default: false)
501
+ css: "preload" # 'preload' | 'prefetch' | 'none' (default: 'preload')
502
+ js: "preload" # 'preload' | 'prefetch' | 'none' (default: 'preload')
503
+ ```
504
+
505
+ **Options:**
506
+
507
+ - `enabled`: Enable/disable early hints (default: `false`)
508
+ - `css`: Hint type for CSS - `'preload'`, `'prefetch'`, or `'none'` (default: `'preload'`)
509
+ - `js`: Hint type for JS - `'preload'`, `'prefetch'`, or `'none'` (default: `'preload'`)
510
+
511
+ ⚠️ **Performance note**: May improve or hurt page load depending on content. Configure per-page for best results.
512
+
513
+ See the [Early Hints Guide](early_hints.md) for:
514
+
515
+ - Performance considerations and warnings
516
+ - Per-page configuration (`configure_pack_early_hints`)
517
+ - Dynamic configuration examples
518
+ - Hero image preloading with `preload_link_tag`
519
+ - Troubleshooting and testing recommendations
520
+
488
521
  ## Environment-Specific Configuration
489
522
 
490
523
  Shakapacker supports per-environment configuration with fallback logic:
@@ -728,4 +761,6 @@ See [Troubleshooting Guide](troubleshooting.md) for more help.
728
761
  - [Deployment Guide](deployment.md)
729
762
  - [CDN Setup Guide](cdn_setup.md)
730
763
  - [Precompile Hook Guide](precompile_hook.md)
764
+ - [Early Hints Guide](early_hints.md)
765
+ - [Subresource Integrity Guide](subresource_integrity.md)
731
766
  - [Troubleshooting Guide](troubleshooting.md)
@@ -0,0 +1,440 @@
1
+ # HTTP 103 Early Hints Guide
2
+
3
+ This guide shows you how to use HTTP 103 Early Hints with Shakapacker to optimize page load performance.
4
+
5
+ ## What are Early Hints?
6
+
7
+ HTTP 103 Early Hints is emitted **after** Rails has finished rendering but **before** the final response is sent, allowing browsers to begin fetching resources (JS, CSS) prior to receiving the full HTML response. This may significantly improve page load performance or cause an equally significant regression, depending on the page's content.
8
+
9
+ ⚠️ **Critical**: Preloading JavaScript may hurt your LCP (Largest Contentful Paint) metric if you have large images, videos, or other content that should load first. **Careful experimentation and performance measurement is required.**
10
+
11
+ ### Priority Levels: Preload vs Prefetch vs None
12
+
13
+ Early hints let you control browser download priority for assets:
14
+
15
+ - **`preload`** - **Prioritize**: High priority, browser downloads immediately before HTML parsing. Use for critical assets needed for initial render.
16
+ - **`prefetch`** - **De-prioritize**: Low priority, browser downloads when idle (doesn't compete for bandwidth). Use for non-critical assets or future navigation.
17
+ - **`none`** - **Default behavior**: No hint sent. Browser discovers asset when parsing HTML (normal page load behavior).
18
+
19
+ ### Performance Considerations
20
+
21
+ ⚠️ **Important**: Different pages have different performance characteristics:
22
+
23
+ - **LCP Impact**: Preloading JS/CSS competes for bandwidth with images/videos, potentially delaying LCP
24
+ - **Hero Images**: Pages with large hero images usually perform **worse** with JS/CSS preload
25
+ - **Interactive Apps**: Dashboards and SPAs may benefit from aggressive JS preload
26
+ - **Content Sites**: Blogs and marketing sites often need conservative hints (prefetch or none)
27
+ - **Recommendation**: Configure hints **per-page** based on content, measure with real user data
28
+
29
+ ## Quick Start
30
+
31
+ ### 1. Global Configuration
32
+
33
+ ```yaml
34
+ # config/shakapacker.yml
35
+ production:
36
+ early_hints:
37
+ enabled: true # Master switch
38
+ css: "preload" # 'preload' | 'prefetch' | 'none'
39
+ js: "preload" # 'preload' | 'prefetch' | 'none'
40
+ ```
41
+
42
+ **Defaults**: When `enabled: true`, both `css` and `js` default to `'preload'` if not specified.
43
+
44
+ **Testing**: See the [Feature Testing Guide](feature_testing.md#http-103-early-hints) for detailed instructions on verifying early hints are working using browser DevTools or curl.
45
+
46
+ ### 2. Per-Page Configuration (Recommended)
47
+
48
+ Configure hints based on your page content:
49
+
50
+ ```ruby
51
+ class PostsController < ApplicationController
52
+ # Image-heavy landing page - don't compete with images
53
+ configure_pack_early_hints only: [:index], css: 'none', js: 'prefetch'
54
+
55
+ # Interactive post editor - JS is critical
56
+ configure_pack_early_hints only: [:edit], css: 'preload', js: 'preload'
57
+
58
+ # API endpoints - no hints needed
59
+ skip_send_pack_early_hints only: [:api_data]
60
+ end
61
+ ```
62
+
63
+ ### 3. Dynamic Configuration
64
+
65
+ Configure based on content:
66
+
67
+ ```ruby
68
+ class PostsController < ApplicationController
69
+ def show
70
+ @post = Post.find(params[:id])
71
+
72
+ if @post.has_hero_video?
73
+ # Video is LCP - don't compete
74
+ configure_pack_early_hints all: 'none'
75
+ elsif @post.interactive?
76
+ # JS needed for interactivity
77
+ configure_pack_early_hints css: 'prefetch', js: 'preload'
78
+ else
79
+ # Standard blog post
80
+ configure_pack_early_hints css: 'preload', js: 'prefetch'
81
+ end
82
+ end
83
+ end
84
+ ```
85
+
86
+ ### 4. Preloading Hero Images and Videos
87
+
88
+ Use Rails' built-in `preload_link_tag` to preload hero images, videos, and other LCP resources. Rails automatically sends these as early hints:
89
+
90
+ ```erb
91
+ <%# app/views/layouts/application.html.erb %>
92
+ <!DOCTYPE html>
93
+ <html>
94
+ <head>
95
+ <%= preload_link_tag image_path('hero.jpg'), as: 'image', type: 'image/jpeg' %>
96
+ <%= preload_link_tag video_path('intro.mp4'), as: 'video', type: 'video/mp4' %>
97
+ </head>
98
+ <body>
99
+ <%= yield %>
100
+ </body>
101
+ </html>
102
+ ```
103
+
104
+ **Dynamic preloading in views:**
105
+
106
+ ```erb
107
+ <%# app/views/posts/show.html.erb %>
108
+ <% if @post.hero_image_url.present? %>
109
+ <%= preload_link_tag @post.hero_image_url, as: 'image' %>
110
+ <% end %>
111
+ ```
112
+
113
+ **Benefits:**
114
+
115
+ - ✅ Standard Rails API - no custom Shakapacker code needed
116
+ - ✅ Automatically sends early hints when server supports it
117
+ - ✅ Works with `image_path`, `video_path`, `asset_path` helpers
118
+ - ✅ Supports all standard attributes: `as`, `type`, `crossorigin`, `integrity`
119
+
120
+ **When to preload images/videos:**
121
+
122
+ - Hero images that are LCP (Largest Contentful Paint) elements
123
+ - Above-the-fold images critical for initial render
124
+ - Background videos that play on page load
125
+
126
+ **Performance tip:** Don't over-preload! Each preload competes for bandwidth. Focus only on critical resources that improve LCP.
127
+
128
+ See [Rails preload_link_tag docs](https://api.rubyonrails.org/classes/ActionView/Helpers/AssetTagHelper.html#method-i-preload_link_tag) for full API.
129
+
130
+ ## Controller Configuration
131
+
132
+ #### Skip Early Hints Entirely
133
+
134
+ ```ruby
135
+ class ApiController < ApplicationController
136
+ # Skip for entire controller
137
+ skip_send_pack_early_hints
138
+ end
139
+
140
+ class PostsController < ApplicationController
141
+ # Skip for specific actions
142
+ skip_send_pack_early_hints only: [:api_endpoint, :feed]
143
+ end
144
+ ```
145
+
146
+ #### Configure Per Action (Class Method)
147
+
148
+ ```ruby
149
+ class PostsController < ApplicationController
150
+ # Configure specific actions
151
+ configure_pack_early_hints only: [:show], css: 'prefetch', js: 'preload'
152
+ configure_pack_early_hints only: [:gallery], css: 'none', js: 'none'
153
+
154
+ # Use 'all' shortcut
155
+ configure_pack_early_hints only: [:about], all: 'prefetch'
156
+
157
+ # Mix general and specific (specific wins)
158
+ configure_pack_early_hints only: [:dashboard], all: 'preload', css: 'prefetch'
159
+ # Result: css='prefetch', js='preload'
160
+ end
161
+ ```
162
+
163
+ #### Configure in Action Method
164
+
165
+ ```ruby
166
+ class PostsController < ApplicationController
167
+ def show
168
+ @post = Post.find(params[:id])
169
+
170
+ # Configure based on runtime logic
171
+ if @post.video_content?
172
+ configure_pack_early_hints css: 'none', js: 'none'
173
+ end
174
+ end
175
+ end
176
+ ```
177
+
178
+ #### Configure in Before Action
179
+
180
+ ```ruby
181
+ class PostsController < ApplicationController
182
+ before_action :optimize_for_images, only: [:gallery, :portfolio]
183
+
184
+ private
185
+
186
+ def optimize_for_images
187
+ configure_pack_early_hints css: 'prefetch', js: 'prefetch'
188
+ end
189
+ end
190
+ ```
191
+
192
+ ## Configuration Precedence
193
+
194
+ Settings are applied in this order (later overrides earlier):
195
+
196
+ 1. **Global** (shakapacker.yml) - project defaults
197
+ 2. **Controller class** (configure_pack_early_hints) - per-action defaults
198
+ 3. **Manual call** (send_pack_early_hints in view) - explicit override
199
+
200
+ Within a single configuration, `all:` is applied first, then specific `css:` and `js:` values override it.
201
+
202
+ ## Usage Examples by Scenario
203
+
204
+ ### Scenario 1: Image-Heavy Landing Page
205
+
206
+ **Problem**: Large hero image is LCP, JS/CSS hints compete for bandwidth and delay image loading
207
+
208
+ ```ruby
209
+ class HomeController < ApplicationController
210
+ def index
211
+ # Save bandwidth for hero image
212
+ configure_pack_early_hints css: 'none', js: 'prefetch'
213
+ end
214
+ end
215
+ ```
216
+
217
+ **Why**:
218
+
219
+ - `css: 'none'` - No hint sent, CSS discovered normally (saves bandwidth)
220
+ - `js: 'prefetch'` - Low priority hint, JS downloads when idle (doesn't compete)
221
+ - **Result**: Hero image gets full bandwidth priority for better LCP
222
+
223
+ ### Scenario 2: Interactive Dashboard
224
+
225
+ **Problem**: App is useless without JavaScript
226
+
227
+ ```ruby
228
+ class DashboardController < ApplicationController
229
+ # JS is critical for all actions
230
+ configure_pack_early_hints all: 'preload'
231
+ end
232
+ ```
233
+
234
+ **Why**: Fast JS load is more important than LCP
235
+
236
+ ### Scenario 3: Blog with Varied Content
237
+
238
+ **Problem**: Article pages have images, index doesn't
239
+
240
+ ```ruby
241
+ class ArticlesController < ApplicationController
242
+ # Index: no large images
243
+ configure_pack_early_hints only: [:index], css: 'preload', js: 'preload'
244
+
245
+ # Show: featured images
246
+ configure_pack_early_hints only: [:show], css: 'prefetch', js: 'prefetch'
247
+ end
248
+ ```
249
+
250
+ **Why**: Different pages have different performance needs
251
+
252
+ ### Scenario 4: Mixed Content Types
253
+
254
+ **Problem**: Posts contain videos, images, or interactive content
255
+
256
+ ```ruby
257
+ class PostsController < ApplicationController
258
+ def show
259
+ @post = Post.find(params[:id])
260
+
261
+ case @post.content_type
262
+ when 'video'
263
+ # Video is LCP
264
+ configure_pack_early_hints all: 'none'
265
+ when 'interactive'
266
+ # JS needed immediately
267
+ configure_pack_early_hints css: 'prefetch', js: 'preload'
268
+ when 'image_gallery'
269
+ # Images are LCP
270
+ configure_pack_early_hints all: 'prefetch'
271
+ else
272
+ # Standard text post
273
+ configure_pack_early_hints css: 'preload', js: 'prefetch'
274
+ end
275
+ end
276
+ end
277
+ ```
278
+
279
+ **Why**: Dynamic configuration based on actual content
280
+
281
+ ### Scenario 5: E-commerce Product Pages
282
+
283
+ **Problem**: Product images are critical, but checkout needs JS
284
+
285
+ ```ruby
286
+ class ProductsController < ApplicationController
287
+ # Product page: images are critical
288
+ configure_pack_early_hints only: [:show], css: 'prefetch', js: 'prefetch'
289
+
290
+ # Checkout: form validation needs JS
291
+ configure_pack_early_hints only: [:checkout], css: 'preload', js: 'preload'
292
+ end
293
+ ```
294
+
295
+ **Why**: Shopping vs checkout have different needs
296
+
297
+ ## How It Works
298
+
299
+ Shakapacker automatically sends early hints after your views render:
300
+
301
+ ```text
302
+ 1. Request arrives
303
+ 2. Controller action runs → Database queries, business logic
304
+ 3. Views render → append_javascript_pack_tag('admin')
305
+ 4. Layout renders → javascript_pack_tag, stylesheet_pack_tag
306
+ 5. after_action hook → Reads configuration and queues
307
+ 6. HTTP 103 sent → rel=preload or rel=prefetch based on config
308
+ 7. HTTP 200 sent → Full HTML response
309
+ ```
310
+
311
+ **Important timing note**: HTTP 103 is sent after rendering completes but before the final HTTP 200 response. This means:
312
+
313
+ - ✅ **Benefits**: Browser starts downloading assets while the server transmits the final HTML response
314
+ - ❌ **Limitations**: Does NOT help during database queries or view rendering—only helps with network transfer time
315
+ - 💡 **Best for**: Pages with large HTML responses where asset downloads can happen in parallel with HTML transmission
316
+
317
+ ## Advanced: Manual Control
318
+
319
+ Most apps should use controller configuration. Use manual control for view-specific logic:
320
+
321
+ ```erb
322
+ <%# app/views/layouts/application.html.erb %>
323
+ <% send_pack_early_hints css: 'prefetch', js: 'preload' %>
324
+
325
+ <!DOCTYPE html>
326
+ <html>
327
+ <body>
328
+ <%= yield %>
329
+ <%= javascript_pack_tag 'application' %>
330
+ </body>
331
+ </html>
332
+ ```
333
+
334
+ **Per-tag override:**
335
+
336
+ ```erb
337
+ <%= javascript_pack_tag 'application',
338
+ early_hints: { css: 'preload', js: 'prefetch' } %>
339
+ ```
340
+
341
+ **Use cases:** Layout-specific optimizations, conditional hints based on view variables, A/B testing
342
+
343
+ ## Requirements
344
+
345
+ - **Rails 5.2+** (for `request.send_early_hints` support)
346
+ - **Web server with HTTP/2 and early hints:**
347
+ - Puma 5+ ✅
348
+ - nginx 1.13+ with ngx_http_v2_module ✅
349
+ - Other HTTP/2 servers with early hints support
350
+ - **Modern browsers:**
351
+ - Chrome/Edge/Firefox 103+ ✅
352
+ - Safari 16.4+ ✅
353
+
354
+ If requirements not met, feature gracefully degrades with no errors.
355
+
356
+ ## Quick Reference
357
+
358
+ ### Priority levels and when to use each:
359
+
360
+ - **`preload`** (Prioritize): Critical assets on text-heavy pages, SPAs, pages without large images
361
+ - **`prefetch`** (De-prioritize): Non-critical assets, pages with large LCP images/videos (downloads when idle)
362
+ - **`none`** (Default behavior): Image/video-heavy pages, API endpoints, SSR pages (no hint sent)
363
+
364
+ ### Testing checklist:
365
+
366
+ 1. Measure LCP with Chrome DevTools Performance tab
367
+ 2. Test on real mobile devices
368
+ 3. A/B test configurations with real user data
369
+ 4. Monitor field data with RUM tools
370
+ 5. Test each page type separately
371
+
372
+ ## Troubleshooting
373
+
374
+ For comprehensive testing instructions including browser DevTools and curl methods, see the [Feature Testing Guide: HTTP 103 Early Hints](feature_testing.md#http-103-early-hints).
375
+
376
+ ### Debug Mode
377
+
378
+ Enable debug mode to see what early hints are being sent (or why they weren't sent):
379
+
380
+ ```yaml
381
+ # config/shakapacker.yml
382
+ production:
383
+ early_hints:
384
+ enabled: true
385
+ debug: true # Outputs debug info as HTML comments
386
+ ```
387
+
388
+ Debug mode adds HTML comments to your page showing:
389
+
390
+ - Whether hints were sent or skipped
391
+ - What pack names were processed
392
+ - What Link headers were sent
393
+ - HTTP/2 support status
394
+
395
+ View page source and look for `<!-- Shakapacker Early Hints Debug -->` comments.
396
+
397
+ **Early hints not appearing:**
398
+
399
+ - **Enable debug mode first** to see what's happening
400
+ - **Check for proxy stripping**: If debug shows hints sent but curl/DevTools don't show `HTTP/2 103`, your reverse proxy or CDN (Control Plane, Cloudflare, AWS ALB/ELB, nginx) is likely stripping 103 responses. This is the **most common cause** of "missing" early hints
401
+ - Check `early_hints: enabled: true` in shakapacker.yml
402
+ - Verify HTTP/2 server (Puma 5+, nginx 1.13+)
403
+ - Check Network tab shows "h2" protocol and 103 status
404
+
405
+ **Reverse proxy stripping 103 responses:**
406
+
407
+ If debug mode shows hints are sent but they're not reaching clients, configure your proxy:
408
+
409
+ - **nginx**: Add `proxy_pass_header Link;` to pass through early hints (nginx 1.13+)
410
+ - **Cloudflare**: Enable "Early Hints" in Speed > Optimization (paid plans only)
411
+ - **AWS ALB/ELB**: Not supported - ALBs strip 103 responses. Workaround: skip ALB or use CloudFront
412
+ - **Control Plane**: Appears to strip 103 - Contact their support if you need early hints
413
+
414
+ See the [Feature Testing Guide](feature_testing.md#troubleshooting-early-hints) for detailed proxy configuration examples.
415
+
416
+ **Performance got worse:**
417
+
418
+ - Page likely has large images/videos as LCP
419
+ - Try `css: 'prefetch', js: 'prefetch'` or `all: 'none'`
420
+ - Measure LCP before and after changes
421
+
422
+ **Wrong hints sent:**
423
+
424
+ - Check configuration precedence (global → controller → manual)
425
+ - Verify values are strings: `'preload'` not `:preload`
426
+ - Check for typos (case-sensitive)
427
+
428
+ ## References
429
+
430
+ ### Shakapacker Documentation
431
+
432
+ - [Feature Testing Guide: HTTP 103 Early Hints](feature_testing.md#http-103-early-hints) - Detailed testing instructions with browser DevTools and curl
433
+
434
+ ### External Resources
435
+
436
+ - [Rails API: send_early_hints](https://api.rubyonrails.org/classes/ActionDispatch/Request.html#method-i-send_early_hints)
437
+ - [RFC 8297: HTTP Early Hints](https://datatracker.ietf.org/doc/html/rfc8297)
438
+ - [MDN: rel=preload vs rel=prefetch](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel)
439
+ - [Web.dev: Optimize LCP](https://web.dev/optimize-lcp/)
440
+ - [HTTP 103 Explained](https://http.dev/103)