Skip to content

Inquiry: absolute-form REQUEST_URI not stripped by DevServerProxy#normalize_uri β€” Hanami-only regex misses RFC 7230 absolute-formΒ #603

@nicholaspufal

Description

@nicholaspufal
  • I have tried upgrading by running bundle update vite_ruby.
  • I have read the troubleshooting section before opening an issue.

Description πŸ“–

Filing as an inquiry rather than a pure bug report β€” want to confirm whether this is known/intentional before sending a PR.

In our setup, env["REQUEST_URI"] sometimes arrives at ViteRuby::DevServerProxy as an RFC 7230 Β§5.3.2 absolute-form URI (https://host/vite-dev/...) rather than origin-form (/vite-dev/...). This causes DevServerProxy to forward an absolute-form request-target to the Vite dev server. Vite's baseMiddleware then returns the "public base URL of /vite-dev/ β€” did you mean to visit …" error page because req.url.startsWith('/vite-dev/') is false when req.url is a full URL.

normalize_uri already exists to defend against this class of problem (the comment reads "Hanami adds the host and port"), but the regex is narrow to host:port/:

https://github.com/ElMassimo/vite_ruby/blob/vite_ruby%403.10.2/vite_ruby/lib/vite_ruby/dev_server_proxy.rb

HOST_WITH_PORT_REGEX = %r{^(.+?)(:\d+)/}

def rewrite_uri_for_vite(env)
  uri = env.fetch("REQUEST_URI") { ... }
  env["PATH_INFO"], env["QUERY_STRING"] =
    (env["REQUEST_URI"] = normalize_uri(uri)).split("?")
end

def normalize_uri(uri)
  uri
    .sub(HOST_WITH_PORT_REGEX, "/") # Hanami adds the host and port.
    .sub(".ts.js", ".ts")
    .sub(/\.(sass|scss|styl|stylus|less|pcss|postcss)\.css$/, '.\1')
end

With REQUEST_URI = "https://our.frontend.dev/vite-dev/@vite/client":

  • HOST_WITH_PORT_REGEX requires an explicit :\d+. Default HTTPS (443) has no port in the URI, so the regex does not match.
  • normalize_uri returns the string unchanged.
  • PATH_INFO is rewritten to the full URL.
  • rack-proxy uses source_request.fullpath verbatim, sending GET https://our.frontend.dev/vite-dev/@vite/client HTTP/1.1 to the Vite dev server.
  • baseMiddleware rejects.

Questions:

  1. Is this a known limitation? I searched open/closed issues and didn't find a match (closest are Dev server is proxying requests starting with @ that should be handled by RailsΒ #194 and vite-plugin-ruby 3.1.1 breaks HMR proxy setupΒ #231, which are different).

  2. Was HOST_WITH_PORT_REGEX restricted to host:port/ intentionally, or simply because Hanami was the only shape encountered at the time? Absolute-form in REQUEST_URI seems plausibly common under TLS-terminating reverse proxies and Rack instrumentation libraries.

  3. Would a broadening like this be acceptable? Happy to send a PR with tests:

    def normalize_uri(uri)
      uri
        .sub(%r{\Ahttps?://[^/]+}, "") # RFC 7230 absolute-form
        .sub(HOST_WITH_PORT_REGEX, "/") # Hanami-style host:port/
        .sub(".ts.js", ".ts")
        .sub(/\.(sass|scss|styl|stylus|less|pcss|postcss)\.css$/, '.\1')
    end

Reproduction 🐞

No minimal repro yet β€” in our own app the absolute REQUEST_URI originates somewhere in a complex Rack/Puma load path we haven't fully isolated. The general-principle question is whether DevServerProxy should be resilient to the RFC absolute-form shape given it already has a defense for the Hanami variant.

If a reproduction is required before you want to look at this, I can work on isolating a minimal Rack middleware that reproduces the env shape.

Vite Ruby Info

Run bin/rake vite:info and provide the output:

bin/vite present?: false
vite_ruby: 3.10.2
vite_rails: 3.10.0
rails: 8.0.2
ruby: ruby 3.3.11 (2026-03-26 revision 1f2d15125a) +YJIT [aarch64-linux]
node: v25.9.0
yarn: 4.9.2

Additional versions from Gemfile.lock / yarn.lock:

Component Version
rack-proxy 0.7.7
rack 2.2.9
puma 6.4.2
vite (Node) 5.4.21
vite-plugin-ruby (Node) 5.0.0

Logs πŸ“œ

Symptom in the browser:

The server is configured with a public base URL of /vite-dev/ - did you mean to visit /vite-dev/… instead?

Debug confirming REQUEST_URI is absolute before DevServerProxy runs (prepended logger in front of forward_to_vite_dev_server):

[vite-proxy] REQUEST_URI="https://our.frontend.dev/vite-dev/@vite/client"
             PATH_INFO="/vite-dev/@vite/client"
             HTTP_HOST="0.0.0.0"

For comparison, a sibling app on the same infra produces origin-form and works:

[vite-proxy] REQUEST_URI="/vite-dev/@fs/.../some-dep.js?v=3347216f"
             PATH_INFO="/vite-dev/@fs/.../some-dep.js"
             HTTP_HOST="sibling.frontend.dev"

Vite's own debug (DEBUG=vite-plugin-ruby:*) confirms base: '/vite-dev/' resolves correctly and the plugin chain is intact β€” the issue is purely that the request-target arriving at Vite is absolute-form.

Screenshots πŸ“·

(None β€” error text above is the complete browser output.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions