seo: fix canonical tags on post index and show page.
* Fix incorrect canonical tags. Before we were using `<meta name="canonical" content="...">`. This is wrong, it should have been `<link rel="canonical" href="...">`. * Add a default canonical tag on all pages. Fixes Google treating the same content on different subdomains (safebooru, shima, kagamihara, etc) as duplicate content. Also fixes Google sometimes treating similar but distinct content on the same domain as duplicate content.
This commit is contained in:
@@ -340,6 +340,19 @@ module ApplicationHelper
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def canonical_url(url = nil)
|
||||||
|
if url.present?
|
||||||
|
content_for(:canonical_url) { url }
|
||||||
|
elsif content_for(:canonical_url).present?
|
||||||
|
content_for(:canonical_url)
|
||||||
|
else
|
||||||
|
request_params = request.params.sort.to_h.with_indifferent_access
|
||||||
|
request_params.delete(:page) if request_params[:page].to_i == 1
|
||||||
|
request_params.delete(:limit)
|
||||||
|
url_for(**request_params, host: Danbooru.config.hostname, only_path: false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def atom_feed_tag(title, url = {})
|
def atom_feed_tag(title, url = {})
|
||||||
content_for(:html_header, auto_discovery_link_tag(:atom, url, title: title))
|
content_for(:html_header, auto_discovery_link_tag(:atom, url, title: title))
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon">
|
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon">
|
||||||
<%= render "meta_links", collection: @current_item %>
|
<%= render "meta_links", collection: @current_item %>
|
||||||
|
<%= tag.link rel: "canonical", href: canonical_url %>
|
||||||
|
|
||||||
<%= csrf_meta_tag %>
|
<%= csrf_meta_tag %>
|
||||||
<% unless CurrentUser.enable_desktop_mode? %>
|
<% unless CurrentUser.enable_desktop_mode? %>
|
||||||
|
|||||||
@@ -10,6 +10,10 @@
|
|||||||
<% atom_feed_tag "Posts: #{@post_set.tag_string}", posts_url(tags: @post_set.tag_string, format: :atom) %>
|
<% atom_feed_tag "Posts: #{@post_set.tag_string}", posts_url(tags: @post_set.tag_string, format: :atom) %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
|
<% if params[:tags].blank? && @post_set.current_page == 1 %>
|
||||||
|
<% canonical_url root_url(host: Danbooru.config.hostname) %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
<% if @post_set.hide_from_crawler? %>
|
<% if @post_set.hide_from_crawler? %>
|
||||||
<meta name="robots" content="nofollow,noindex">
|
<meta name="robots" content="nofollow,noindex">
|
||||||
<% end %>
|
<% end %>
|
||||||
@@ -18,8 +22,6 @@
|
|||||||
<meta name="rating" content="adult">
|
<meta name="rating" content="adult">
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%= tag.meta name: "canonical", content: posts_url(tags: params[:tags], host: Danbooru.config.hostname, protocol: "https") %>
|
|
||||||
|
|
||||||
<% if @post_set.best_post.present? %>
|
<% if @post_set.best_post.present? %>
|
||||||
<%= tag.meta property: "og:image", content: @post_set.best_post.open_graph_image_url %>
|
<%= tag.meta property: "og:image", content: @post_set.best_post.open_graph_image_url %>
|
||||||
<%= tag.meta name: "twitter:image", content: @post_set.best_post.open_graph_image_url %>
|
<%= tag.meta name: "twitter:image", content: @post_set.best_post.open_graph_image_url %>
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
<% page_title @post.presenter.humanized_essential_tag_string %>
|
<% page_title @post.presenter.humanized_essential_tag_string %>
|
||||||
<% meta_description "View this #{@post.image_width}x#{@post.image_height} #{number_to_human_size(@post.file_size)} image" %>
|
<% meta_description "View this #{@post.image_width}x#{@post.image_height} #{number_to_human_size(@post.file_size)} image" %>
|
||||||
|
<% canonical_url post_url(@post, host: Danbooru.config.hostname) %>
|
||||||
<% atom_feed_tag "Comments for post ##{@post.id}", comments_url(:atom, search: { post_id: @post.id }) %>
|
<% atom_feed_tag "Comments for post ##{@post.id}", comments_url(:atom, search: { post_id: @post.id }) %>
|
||||||
|
|
||||||
<%= render "posts/partials/common/secondary_links" %>
|
<%= render "posts/partials/common/secondary_links" %>
|
||||||
|
|
||||||
<% content_for(:sidebar) do %>
|
<% content_for(:sidebar) do %>
|
||||||
@@ -148,8 +149,6 @@
|
|||||||
<%= tag.meta property: "og:image", content: @post.open_graph_image_url %>
|
<%= tag.meta property: "og:image", content: @post.open_graph_image_url %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%= tag.meta name: "canonical", content: post_url(@post, host: Danbooru.config.hostname, protocol: "https") %>
|
|
||||||
|
|
||||||
<% if @post.twitter_card_supported? %>
|
<% if @post.twitter_card_supported? %>
|
||||||
<meta name="twitter:card" content="summary_large_image">
|
<meta name="twitter:card" content="summary_large_image">
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
require "test_helper"
|
require "test_helper"
|
||||||
|
|
||||||
class PostsControllerTest < ActionDispatch::IntegrationTest
|
class PostsControllerTest < ActionDispatch::IntegrationTest
|
||||||
|
def assert_canonical_url_equals(expected)
|
||||||
|
assert_equal(expected, response.parsed_body.css("link[rel=canonical]").attribute("href").value)
|
||||||
|
end
|
||||||
|
|
||||||
context "The posts controller" do
|
context "The posts controller" do
|
||||||
setup do
|
setup do
|
||||||
@user = travel_to(1.month.ago) {create(:user)}
|
@user = travel_to(1.month.ago) {create(:user)}
|
||||||
@@ -10,11 +14,29 @@ class PostsControllerTest < ActionDispatch::IntegrationTest
|
|||||||
context "index action" do
|
context "index action" do
|
||||||
setup do
|
setup do
|
||||||
mock_post_search_rankings(Date.today, [["1girl", 100], ["original", 50]])
|
mock_post_search_rankings(Date.today, [["1girl", 100], ["original", 50]])
|
||||||
|
create_list(:post, 2)
|
||||||
end
|
end
|
||||||
|
|
||||||
should "render" do
|
context "for an empty search" do
|
||||||
get posts_path
|
should "render the first page" do
|
||||||
assert_response :success
|
get root_path
|
||||||
|
assert_response :success
|
||||||
|
assert_canonical_url_equals(root_url(host: Danbooru.config.hostname))
|
||||||
|
|
||||||
|
get posts_path
|
||||||
|
assert_response :success
|
||||||
|
assert_canonical_url_equals(root_url(host: Danbooru.config.hostname))
|
||||||
|
|
||||||
|
get posts_path(page: 1)
|
||||||
|
assert_response :success
|
||||||
|
assert_canonical_url_equals(root_url(host: Danbooru.config.hostname))
|
||||||
|
end
|
||||||
|
|
||||||
|
should "render the second page" do
|
||||||
|
get posts_path(page: 2, limit: 1)
|
||||||
|
assert_response :success
|
||||||
|
assert_canonical_url_equals(posts_url(page: 2, host: Danbooru.config.hostname))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with a single tag search" do
|
context "with a single tag search" do
|
||||||
@@ -22,6 +44,7 @@ class PostsControllerTest < ActionDispatch::IntegrationTest
|
|||||||
get posts_path, params: { tags: "does_not_exist" }
|
get posts_path, params: { tags: "does_not_exist" }
|
||||||
assert_response :success
|
assert_response :success
|
||||||
assert_select "#show-excerpt-link", count: 0
|
assert_select "#show-excerpt-link", count: 0
|
||||||
|
assert_canonical_url_equals(posts_url(tags: "does_not_exist", host: Danbooru.config.hostname))
|
||||||
end
|
end
|
||||||
|
|
||||||
should "render for an artist tag" do
|
should "render for an artist tag" do
|
||||||
@@ -261,7 +284,7 @@ class PostsControllerTest < ActionDispatch::IntegrationTest
|
|||||||
get posts_path(format: :atom)
|
get posts_path(format: :atom)
|
||||||
|
|
||||||
assert_response :success
|
assert_response :success
|
||||||
assert_select "entry", 1
|
assert_select "entry", 3
|
||||||
end
|
end
|
||||||
|
|
||||||
should "render with tags" do
|
should "render with tags" do
|
||||||
@@ -272,7 +295,7 @@ class PostsControllerTest < ActionDispatch::IntegrationTest
|
|||||||
end
|
end
|
||||||
|
|
||||||
should "hide restricted posts" do
|
should "hide restricted posts" do
|
||||||
@post.update(is_banned: true)
|
Post.update_all(is_banned: true)
|
||||||
get posts_path(format: :atom)
|
get posts_path(format: :atom)
|
||||||
|
|
||||||
assert_response :success
|
assert_response :success
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ class ActionDispatch::IntegrationTest
|
|||||||
extend ControllerHelper
|
extend ControllerHelper
|
||||||
|
|
||||||
register_encoder :xml, response_parser: ->(body) { Nokogiri.XML(body) }
|
register_encoder :xml, response_parser: ->(body) { Nokogiri.XML(body) }
|
||||||
|
register_encoder :html, response_parser: ->(body) { Nokogiri.HTML5(body) }
|
||||||
|
|
||||||
def method_authenticated(method_name, url, user, **options)
|
def method_authenticated(method_name, url, user, **options)
|
||||||
post session_path, params: { name: user.name, password: user.password }
|
post session_path, params: { name: user.name, password: user.password }
|
||||||
|
|||||||
Reference in New Issue
Block a user