You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于域名在Rails中条件性实现HSTS、SSL及安全Cookie

嘿,这个场景我之前帮不少Rails开发者处理过,在单应用多域名的情况下,既要给部分域名上SSL、配置HSTS和安全Cookie,又要保留其他域名的HTTP访问,确实不能用全局配置一刀切。Rails 4.x里有几个比只靠ApplicationController更优的方案,给你梳理清楚:

方案1:自定义Rack中间件(最推荐的Rails内方案)

中间件运行在Rails处理请求的早期和响应的晚期,正好能精准控制HSTS头和Cookie的安全属性,而且不会影响全局配置。

步骤1:创建中间件文件

在app/middlewares/目录下新建domain_ssl_middleware.rb(如果没有middlewares文件夹就自己建一个):

class DomainSslMiddleware
  # 把需要启用SSL的域名放到这个数组里
  SSL_DOMAINS = %w(your-ssl-domain.com another-ssl-domain.net)

  def initialize(app)
    @app = app
  end

  def call(env)
    status, headers, response = @app.call(env)
    request = Rack::Request.new(env)
    
    if SSL_DOMAINS.include?(request.host)
      # 给SSL域名的HTTPS响应添加HSTS头,max-age可以根据需求调整,includeSubDomains可选
      headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains' if status == 200 && request.ssl?

      # 自动给所有Cookie加上Secure标记,确保只有HTTPS环境下才会发送
      headers['Set-Cookie']&.gsub!(/([^;]+)/, '\1; Secure') if headers['Set-Cookie']
    else
      # 非SSL域名要确保不发送HSTS头,并且移除Cookie的Secure标记(避免浏览器拒绝保存)
      headers.delete('Strict-Transport-Security')
      headers['Set-Cookie']&.gsub!(/; Secure/, '') if headers['Set-Cookie']
    end

    [status, headers, response]
  end
end

步骤2:注册中间件

在config/application.rb里添加一行,把中间件加入Rails的中间件栈:

config.middleware.use DomainSslMiddleware

这个方案的好处是:

  • 早于控制器处理请求,能完美适配HSTS的浏览器要求(必须在HTTPS响应中返回)
  • 精准针对域名控制Cookie的Secure属性,不会搞混SSL和非SSL域名的Cookie
  • 完全避开全局配置的一刀切问题
方案2:Web服务器层面配置(更高效)

如果你的应用用Nginx或Apache做反向代理,直接在服务器层面分域名配置会更高效,毕竟Web服务器处理请求的性能比Rails好很多。

以Nginx为例:

针对SSL域名的server块

server {
  listen 443 ssl;
  server_name your-ssl-domain.com another-ssl-domain.net;

  # 这里放你的SSL证书配置、反向代理配置...

  # 添加HSTS头,always确保即使是跳转响应也会带上
  add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

  # 把请求协议传递给Rails,让Rails知道当前是HTTPS
  proxy_set_header X-Forwarded-Proto $scheme;
}

针对非SSL域名的server块

server {
  listen 80;
  server_name http-only-domain.org blog.http-domain.com;

  # 这里放你的反向代理配置...
  # 不需要添加HSTS头,保持默认即可
}

Rails端配合配置

在config/application.rb里关掉全局强制SSL,并信任反向代理的协议标识:

config.force_ssl = false # 必须关掉,不然会强制所有域名走HTTPS
config.action_dispatch.trusted_proxies = ['127.0.0.1'] # 替换成你的代理服务器IP

然后在config/initializers/session_store.rb里,让Cookie的Secure属性动态判断请求协议:

Rails.application.config.session_store :cookie_store, key: '_your_app_session',
  secure: ->(request) { request.ssl? }, # 只有HTTPS请求时才加Secure标记
  httponly: true

这个方案的优势:

  • Web服务器处理HSTS和协议转发更高效,减轻Rails的负担
  • 分域名配置更直观,每个server块对应一套策略
  • 动态Cookie属性配置精准适配不同域名的协议
方案3:控制器层面补充(配合前两个方案)

如果需要对特定动作做跳转(比如把SSL域名的HTTP请求强制跳转到HTTPS),可以在ApplicationController里加个前置动作,作为前两个方案的补充:

class ApplicationController < ActionController::Base
  SSL_DOMAINS = %w(your-ssl-domain.com another-ssl-domain.net)

  before_action :enforce_protocol_by_domain

  private

  def enforce_protocol_by_domain
    if SSL_DOMAINS.include?(request.host) && !request.ssl?
      # SSL域名的HTTP请求跳转到HTTPS,永久重定向
      redirect_to protocol: 'https://', status: :moved_permanently
    elsif !SSL_DOMAINS.include?(request.host) && request.ssl?
      # 非SSL域名的HTTPS请求跳转到HTTP(可选,根据你的需求决定)
      redirect_to protocol: 'http://', status: :moved_permanently
    end
  end
end

注意:单独用控制器跳转没法设置HSTS头,因为第一次跳转是HTTP 301响应,不会带HSTS,所以一定要配合中间件或服务器配置。

几个重要提醒

  1. Rails 4.x的config.force_ssl是全局生效的,一定要关掉,不然会强制所有域名走HTTPS
  2. HSTS头的max-age设置后浏览器会缓存很久,测试阶段可以用max-age=0来快速清除缓存
  3. 非SSL域名的Cookie绝对不能加Secure标记,不然浏览器会拒绝保存这些Cookie

内容的提问来源于stack exchange,提问作者Jason Logsdon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 08:10:45