【问题标题】:Unable to pull complete header from external URL in Ruby on Rails app无法从 Ruby on Rails 应用程序中的外部 URL 提取完整的标头
【发布时间】:2026-01-25 01:50:01
【问题描述】:

背景:
我正在开发的 Rails 应用程序在 iframe 中打开来自其他网站的文章。但是一些发布商网站(如 pitchfork.com、vox.com、medium.com)通过在其标题中设置“X-Frame-Options: SAMEORIGIN”来阻止自己在 iframe 中打开。因此,给定文章的 URL,我正在尝试检查标题并在 iframe 中打开它(默认)或在新选项卡中打开原始站点(当我在标题中检测到 X-Frame-Options 时)。


问题:
当我使用以下代码拉动 Rails(并打印到控制台)时,我拉入 Rails 的标头有时不完整:

puts y['site'] # example: "vox.com"
puts y['head'] # example: "/2016/1/25/10829662/obama-on-clinton-media"
require 'net/http'
http = Net::HTTP.start(y['site'])
resp = http.head(y['head'])
resp.each { |k, v| puts "#{k}: #{v}" }
http.finish

示例:rails 为这篇vox.com 文章(http://www.vox.com/2016/1/25/10829662/obama-on-clinton-media)拉取的标题如下:

server: nginx/1.6.2
date: Fri, 29 Jan 2016 22:05:17 GMT
content-type: text/html
content-length: 184
connection: keep-alive
location: http://www.vox.com/2016/1/25/10829662/obama-on-clinton-media

但是当我尝试用 iframe 打开它时,chrome 控制台告诉我它不能,因为 X-Frame-Options 设置为 SAMEORIGIN。在“网络”选项卡中进一步调查后,我能够检查以检查完整的标题,如下所示:

HTTP/1.1 200 OK
Server: nginx
Content-Type: text/html; charset=utf-8
Status: 200 OK
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Cache-Control: max-age=0, must-revalidate
X-Request-Id: 693f75c9be4dde491ba3cd78232ac4870c4f82e2
X-Runtime: 0.404545
Content-Encoding: gzip
Via: 1.1 varnish-v4
Content-Length: 26450
Accept-Ranges: bytes
Date: Fri, 29 Jan 2016 22:10:47 GMT
Via: 1.1 varnish
Age: 106
Connection: keep-alive
X-Served-By: cache-jfk1034-JFK
X-Cache: MISS
X-Cache-Hits: 0
X-Timer: S1454105446.991771,VS0,VE12
Vary: Accept-Encoding, Origin, X-Forwarded-Proto

并非所有网站都会出现此问题。例如,我从 pitchfork.com 中提取的标题清楚地表明它设置了 x-frame-options。但是对于像 vox.com 和 medium.com 这样的网站,我拉出的标题不显示 x-frame-options(以及许多其他被遗漏的项目)。

如何在 Rails 控制器中提取正确/完整的标头,以始终检测 URL 标头中是否包含 X-Frame-Options?

【问题讨论】:

    标签: ruby-on-rails ruby net-http x-frame-options


    【解决方案1】:

    我在 IRB 控制台中尝试过,我注意到对 vox.com 网站的请求正在返回 301 Moved Permanently,并且它在标头中发送了新位置。

    irb(main):001:0> y = {}
    => {}
    irb(main):002:0> y['site'] = "vox.com"
    => "vox.com"
    irb(main):003:0> y['head'] = "/2016/1/25/10829662/obama-on-clinton-media"
    => "/2016/1/25/10829662/obama-on-clinton-media"
    irb(main):004:0> require 'net/http'
    => true
    irb(main):005:0> http = Net::HTTP.start(y['site'])
    => #<Net::HTTP vox.com:80 open=true>
    irb(main):006:0> resp = http.head(y['head'])
    => #<Net::HTTPMovedPermanently 301 Moved Permanently readbody=true> (HERE)
    irb(main):007:0> resp.each { |k, v| puts "#{k}: #{v}" }
    server: nginx/1.6.2
    date: Fri, 29 Jan 2016 22:40:07 GMT
    content-type: text/html
    content-length: 184
    connection: keep-alive
    location: http://www.vox.com/2016/1/25/10829662/obama-on-clinton-media
    => {"server"=>["nginx/1.6.2"], "date"=>["Fri, 29 Jan 2016 22:40:07 GMT"], "content-type"=>["text/html"], "content-length"=>["184"], "connection"=>["keep-alive"], "location"=>["http://www.vox.com/2016/1/25/10829662/obama-on-clinton-media"]}
    irb(main):008:0> http.finish
    => nil
    

    您使用的 URL 与服务器发送的重定向位置之间的唯一区别是“www”。尝试与“www”一起使用,看看它是否有效。

    您可以改进您的代码以读取响应代码,如果是 301,请使用服务器发送的 URL 重试。

    【讨论】:

    • 我可以确认同样的行为。 OP 向浏览器中的www.vox.com 和Rails 中的vox.com 发出请求,因此存在差异。
    • 啊,我明白这就是问题所在。所以 vox.com 应该是 www.vox.com 但我不知道应该重定向到什么 medium.com?如何读取响应代码和服务器发送的 URL?
    • 您可以使用resp.code获取响应状态(301)和resp['location']获取服务器发送的URL。
    • 谢谢,有一个例外:来自 medium.com 的文章(例如:medium.com/the-coffeelicious/…)给出了 301 响应状态,但是当我使用时给我与原始请求完全相同的 URL响应 ['位置']。知道那里发生了什么吗?