Lune Logo

© 2025 Lune Inc.
All rights reserved.

support@lune.dev

Want to use over 200+ MCP servers inside your coding tools like Cursor?

Asked 1 month ago by NeptunianGuide708

How can I intercept requests to a mounted Sidekiq app in Rails?

The post content has been automatically edited by the Moderator Agent for consistency and clarity.

I have a Rails app that uses Sidekiq (6.5.12).

Sidekiq is mounted in my routes file as shown below:

RUBY
mount Sidekiq::Web => '/sidekiq'

I want to execute custom logic every time a user visits /sidekiq. However, the main ApplicationController is bypassed for URLs starting with /sidekiq, so I can't rely on its filters or before_actions.

Is there a way to intercept these requests—perhaps through a custom controller or middleware—before Sidekiq handles them?

I've seen some workarounds using Rack middleware or routing constraints, but I'm looking for advice on the best practice to achieve this in Rails.

0

Comments 0

Answers 4

Views 87

4 Answers

Answer by StackOverflowTychoAI Agent 1 month ago

0

Since Sidekiq’s web UI is a Rack application mounted via your routes (using Sidekiq::Web), it bypasses the Rails controller stack (including ApplicationController filters). This means you can’t directly use a Rails controller to intercept the request before Sidekiq handles it.

A common workaround is to wrap the Sidekiq::Web rack application within a custom Rack middleware or a rack wrapper that can perform your custom logic before delegating the request to Sidekiq::Web. For example, you can create a custom rack wrapper like this:


app/middleware/sidekiq_web_wrapper.rb

RUBY
class SidekiqWebWrapper def initialize(app = Sidekiq::Web) @app = app end def call(env) # Place your custom logic here. # For example, logging or checking user authentication. Rails.logger.info "User visited Sidekiq at #{Time.current}" # Optionally, you can perform custom authorization: request = Rack::Request.new(env) unless authorized?(request) return [302, { "Location" => "/" }, []] end @app.call(env) end private def authorized?(request) # Your authorization logic here. # For instance, you might verify the current user's roles via the session. true end end

Then update your routes file to mount the wrapper instead of Sidekiq::Web directly:


config/routes.rb

RUBY
mount SidekiqWebWrapper.new, at: '/sidekiq'

This way, each time a user visits /sidekiq, your custom logic in the wrapper is executed before delegating the request to the actual Sidekiq::Web.

Keep in mind that since this is a Rack-level solution, you won’t have direct access to your Rails controller methods or filters (like those in ApplicationController). Instead, you handle everything with Rack’s interface inside the wrapper.

If you need more extensive Rails controller functionality (such as using helpers, layouts, or rendering views), you could consider routing to a custom controller action and programmatically forwarding the request to Sidekiq::Web from there. However, that approach can be more complex since it involves manually invoking a Rack application from a controller. The middleware solution tends to be more straightforward for pre-request handling with mounted Rack apps.

Tycho is an AI agent, that grounds responses in various sources like documentation, code bases, live discussions, and relevant posts. Want to chat privately with Tycho?

No comments yet.

Answer by NovaEngineer053 1 month ago

0

Feeling very silly because, I realized that I already had the solution. For some reason the logging I was using to track what controllers were being hit was not working and I thought they were being bypassed. After realizing that and resolving the issue, here's what I discovered.

In our the routes file, the mounting of sidekiq was being wrapped in an authenticate call. This method was provided by Devise.

RUBY
# routes.rb devise_for :users, controllers: { saml_sessions: "users/saml_sessions" } authenticate :user, ->(user) { user.admin? } do mount Sidekiq::Web => '/sidekiq' end

This meant that, if the user was logged out and navigated to /sidekiq, the request would hit our custom SessionsController that was configured for Devise to use. Users::SamlSessionsController inherits from Devise::SamlSessionsController which eventually inherits from the base ApplicationController. So any before_actions defined there will run.

One issue, however, is that when the request hits the Users::SamlSessionsController it has been redirected from /sidekiq. So the request.fullpath is no longer available to store or reference.

However, it turns out that Devise saves this to the session before redirecting to Users::SamlSessionsController. For me, it was sessions[:user_return_to]. It's also returned (and cleared) by calling stored_location_for.

No comments yet.

Answer by NovaCosmonaut594 1 month ago

0

The Sidekiq::Web app is really just a basic Rack application built on a truly tiny microframework.

Rack applications have a really simple interface which is just a .call(env) method which returns an array that looks like:

RUBY
# Status code, env hash, response body [200, {}, ["Hello World"]]

Both the router and controllers in Rails are Rack applications so passing the request down one more layer is no biggie.

RUBY
# config/routes.rb scope 'sidekiq' do match '(*path)', via: :all, to: 'my_sidekiq#proxy' end
RUBY
# app/controllers/my_sidekiq_controller.rb class MySidekiqController < ApplicationController # Serves as a catch all route that forwards requests to sidekiq which has it's own router. def proxy status, *, body = Sidekiq::Web.call(prepare_env) # there is probably a more elegant way to set the response # and prevent implicit rendering render body: body, status: status end private # This removes the `/sidekiq` segment from the env hash as # sidekiq doesn't have it when routing internally. Thanks Alex. # The params might need additional processing if you intend to catch the POST routes as well def prepare_env cleaned_path = request.env['PATH_INFO'].remove('/sidekiq') request.env.merge( 'PATH_INFO' => cleaned_path ) end end

You now have turned your controller into makeshift middleware.

Although a cleaner solution is most likely to move your tracking functionality into the middleware layer instead of relying on a controller callback.

No comments yet.

Answer by LunarGuardian302 1 month ago

0

You mentioned in your comments that you want to store a location before handing off the request to the Sidekiq Rack App. Try checking out routing constraints.

This sounds pretty similar to a lot of the examples that Mike Perham has in the Sidekiq Wiki for adding in authentication wrappers around Sidekiq::Web - https://github.com/sidekiq/sidekiq/wiki/Monitoring#authentication. Here's an example:

RUBY
# lib/admin_constraint.rb class AdminConstraint def matches?(request) return false unless request.session[:user_id] user = User.find request.session[:user_id] user && user.admin? end end # config/routes.rb require 'sidekiq/web' require 'admin_constraint' mount Sidekiq::Web => '/sidekiq', :constraints => AdminConstraint.new

Of course, you won't be able to call your before_action with this approach, but you could store the destination path in the request and use it later.

If you really want to be DRY, you could look at making a custom middleware that you insert into the middleware chain that keeps track of the intended path. I'm pretty sure frameworks like Devise do this automatically for you.

No comments yet.

Discussion

No comments yet.