基于Rails 5.1.4与Puma 3.11.0,Heroku环境内容密码保护方案咨询
Hey there! Let's figure out how to password-protect your public Rails content without breaking all those internal links. Since moving everything to controllers isn't feasible, here are a few practical solutions tailored to your Rails 5.1.4 + Puma 3.11.0 + Heroku stack:
This is the quickest way to lock down your entire app without touching existing controllers or routes. It uses Rack's built-in auth middleware, which works seamlessly with Heroku.
- Add an initializer for auth
Createconfig/initializers/basic_auth.rbwith this code:
if Rails.env.production? # Insert the auth middleware before static file handling to protect assets too Rails.application.config.middleware.insert_before ActionDispatch::Static, Rack::Auth::Basic, "Internal Protected Area" do |username, password| # Validate credentials against Heroku environment variables username == ENV["BASIC_AUTH_USER"] && password == ENV["BASIC_AUTH_PASSWORD"] end end
- Set environment variables on Heroku
Run these commands in your terminal:
heroku config:set BASIC_AUTH_USER=your_preferred_username heroku config:set BASIC_AUTH_PASSWORD=a_secure_password_here
That's it! Every request (including static assets and internal links) will prompt for the username/password before accessing content. No changes to your existing routes or controllers needed.
If you need to leave certain paths public (like robots.txt or a public API endpoint), adjust the middleware to skip auth for those routes:
Update config/initializers/basic_auth.rb:
if Rails.env.production? Rails.application.config.middleware.insert_before ActionDispatch::Static, Rack::Auth::Basic, "Internal Protected Area" do |username, password| # Skip auth for specific paths skip_paths = ["/robots.txt", "/public-api"] next true if skip_paths.include?(request.path) username == ENV["BASIC_AUTH_USER"] && password == ENV["BASIC_AUTH_PASSWORD"] end end
If you want a custom login page instead of the browser's default auth prompt, you can build a session-based system using a Rack middleware (still no changes to your existing controllers):
- Create a custom auth middleware
Makeapp/middlewares/auth_middleware.rb:
class AuthMiddleware def initialize(app) @app = app end def call(env) request = Rack::Request.new(env) # Skip auth for login/logout paths and authenticated sessions if request.path.in?(["/login", "/logout"]) || request.session[:authenticated] @app.call(env) else # Redirect unauthenticated GET requests to login page if request.get? [302, {"Location" => "/login", "Content-Type" => "text/plain"}, ["Redirecting to login..."]] else # Block non-GET requests with 401 [401, {"Content-Type" => "text/plain"}, ["Unauthorized: Please login first"]] end end end end
- Register the middleware
Add this toconfig/initializers/middleware.rb:
if Rails.env.production? Rails.application.config.middleware.use AuthMiddleware end
- Add a simple sessions controller
Createapp/controllers/sessions_controller.rb:
class SessionsController < ApplicationController def new # Login page view end def create if params[:username] == ENV["AUTH_USER"] && params[:password] == ENV["AUTH_PASSWORD"] session[:authenticated] = true # Redirect back to the page the user was trying to access redirect_to request.referer || root_path else flash[:error] = "Invalid username or password" render :new end end def destroy session[:authenticated] = nil redirect_to root_path end end
- Add routes for login/logout
Updateconfig/routes.rb:
get "/login", to: "sessions#new" post "/login", to: "sessions#create" delete "/logout", to: "sessions#destroy"
- Create a login view
Makeapp/views/sessions/new.html.erbwith your custom login form:
<h1>Please Login</h1> <% if flash[:error] %> <p style="color: red;"><%= flash[:error] %></p> <% end %> <%= form_tag login_path do %> <div> <%= label_tag :username %> <%= text_field_tag :username %> </div> <div> <%= label_tag :password %> <%= password_field_tag :password %> </div> <div> <%= submit_tag "Login" %> </div> <% end %>
- Set environment variables on Heroku
heroku config:set AUTH_USER=your_username heroku config:set AUTH_PASSWORD=your_secure_password
This approach gives users a nicer login experience while keeping all your existing internal links intact.
For most cases, 方案1 is the best fit—it's minimal, secure, and requires almost no code changes. All these solutions work with your Rails 5.1.4 and Puma setup, and deploy smoothly to Heroku.
内容的提问来源于stack exchange,提问作者Mark Fraser

