From d2d6ac1d9962c49affd81a6d54e2fb211259e01a Mon Sep 17 00:00:00 2001 From: Sergei Poljanski Date: Sat, 10 Oct 2026 09:27:02 +0400 Subject: [PATCH] bingo: redirect /en/ to /en, follow the view links /en/ answered 200 with the same page as /en. The canonical tag covered it, but a 301 keeps crawlers and shared links on a single URL. The All / Operational toggle links were rel="nofollow". The filtered page is already noindex with a canonical back to the locale URL, so nofollow on our own links only threw away internal link signals. Assisted-by: Claude:opus-5 --- bingo_web.rb | 8 +++++++- test/bingo_test.rb | 7 +++++++ views/bingo/index.erb | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/bingo_web.rb b/bingo_web.rb index ee1369a..96ca286 100644 --- a/bingo_web.rb +++ b/bingo_web.rb @@ -97,7 +97,13 @@ class BingoWeb < Sinatra::Base erb :sitemap, layout: false end - get %r{/(ru|en|uk)/?} do |locale| + # One URL per page: /en/ is a duplicate of /en. + get %r{/(ru|en|uk)/} do |locale| + qs = request.query_string + redirect "/#{locale}#{"?#{qs}" unless qs.empty?}", 301 + end + + get %r{/(ru|en|uk)} do |locale| @locale = locale @view = Bingo::VIEWS.include?(params[:view]) ? params[:view] : 'all' @board = Bingo::Board.load(@view) diff --git a/test/bingo_test.rb b/test/bingo_test.rb index b81e22a..ef13ea2 100644 --- a/test/bingo_test.rb +++ b/test/bingo_test.rb @@ -136,6 +136,7 @@ class BingoTest < Minitest::Test assert last_response.ok? assert_includes last_response.body, 'noindex, follow' assert_includes last_response.body, 'href="/en?view=operational"' # language links keep the view + assert_includes last_response.body, '' # followed internal link assert_includes last_response.body, 'rel="canonical" href="https://example.org/ru"' get '/ru?view=bogus' assert_includes last_response.body, 'index, follow, max-image-preview' @@ -183,6 +184,12 @@ class BingoTest < Minitest::Test assert_equal 404, last_response.status end + def test_trailing_slash_redirects + get '/en/', view: 'operational' + assert_equal 301, last_response.status + assert_equal 'http://example.org/en?view=operational', last_response.location + end + def test_root_varies_on_language get '/' assert_equal 'Accept-Language', last_response.headers['Vary'] diff --git a/views/bingo/index.erb b/views/bingo/index.erb index 66c136a..c9c85d0 100644 --- a/views/bingo/index.erb +++ b/views/bingo/index.erb @@ -5,7 +5,7 @@