サブフォルダインストールで「投稿全体を表示」ボタンが機能しない

最近、Discourseのインストールをサブフォルダに移動しました。その後、「投稿全体を表示」ボタンが機能しなくなりました。コンテンツを展開するためにクリックしても、投稿全体が読み込まれません。

WP Discourseの設定は何も変更していません。

https://tecnoblog.net/comunidade/t/paramount-oferece-us-108-bilhoes-em-dinheiro-para-tomar-warner-da-netflix/157441

埋め込みURLをブラウザで直接開くと、404エラーが返されます。

https://tecnoblog.net/comunidade/posts/483289/expand-embed

「いいね!」 1

これは無関係です。このルートは application/json コンテンツタイプでのみ応答します。https://tecnoblog.net/comunidade/posts/483289/expand-embed.json は以下を返しています。

"\"\u003cdiv\u003e\u003cdiv\u003e\u003c/div\u003e\u003c/div\u003e\\n\u003chr\u003e\\n\u003csmall\u003eEste é um tópico de discussão auxiliar para a entrada original em \u003ca href='https://tecnoblog.net/noticias/paramount-oferece-us-108-bilhoes-em-dinheiro-para-tomar-warner-da-netflix'\u003ehttps://tecnoblog.net/noticias/paramount-oferece-us-108-bilhoes-em-dinheiro-para-tomar-warner-da-netflix\u003c/a\u003e\u003c/small\u003e\\n\""

\u003cdiv\u003e\u003cdiv\u003e\u003c/div\u003e\u003c/div\u003e がコンテンツであるはずです。

ついでに、ブログのURLも変更しましたか?

Oneboxの表示も奇妙に感じます。キャッシュされた短縮コンテンツが表示されることを期待しますが、上記の条件文で body.present? が偽になっていると推測します。

Railsコンソールに入って、TopicEmbed.where(topic_id: 157441).pick(:embed_url) が正しいブログコンテンツURLを表示するかどうか確認できますか?

https://tecnoblog.net/comunidade/logs で関連するエラーを見つけられますか?

「いいね!」 2

ああ、わかりました!

投稿のURLが返されます。

discourse(prod) => TopicEmbed.where(topic_id: 157441).pick(:embed_url)
=> “``https://tecnoblog.net/noticias/paramount-oferece-us-108-bilhoes-em-dinheiro-para-tomar-warner-da-netflix”

ログに関連するエラーはないと思います。

いいえ!ブログのURLは常に tecnoblog.net でした。

また、サーバーのIPはCFのファイアウォールでバイパスされていることも言及する価値があります。

「いいね!」 2

これをデバッグするために、この問題を数回デバッグしなければならず、複雑なので、しばらくお待ちください。

以下のスクリプトを実行し、出力をここに共有してください

# デバッグしているトピックIDまたはURLに置き換えてください
topic_id = 386983

# 1. TopicEmbedが存在するか、およびそのコンテンツを確認する
te = TopicEmbed.find_by(topic_id: topic_id)
puts "TopicEmbedが存在する: #{te.present?}"
puts "埋め込みURL: #{te&.embed_url}"
puts "コンテンツキャッシュの有無: #{te&.embed_content_cache.present?}"
puts "コンテンツキャッシュの長さ: #{te&.embed_content_cache&.length || 0}"
puts "コンテンツSHA1: #{te&.content_sha1}"

# 2. 実際のキャッシュされたコンテンツ(最初の500文字)を確認する
puts "\n--- キャッシュされたコンテンツのプレビュー ---"
puts te&.embed_content_cache&.truncate(500)

# 3. リモートURLからフェッチを試みる
if te&.embed_url.present?
  puts "\n--- リモートフェッチを試行中 ---"
  begin
    response = TopicEmbed.find_remote(te.embed_url)
    puts "リモートフェッチ成功: #{response.present?}"
    puts "リモートボディの有無: #{response&.body.present?}"
    puts "リモートボディの長さ: #{response&.body&.length || 0}"
    puts "リモートタイトル: #{response&.title}"
    puts "リモートボディ: #{response&.body&.truncate(500)}"
  rescue => e
    puts "リモートフェッチ失敗: #{e.message}"
  end
end

# 4. expanded_forが何を返すかを確認する
if te.present?
  puts "\n--- expanded_forのテスト ---"
  post = Post.find(te.post_id)

  # 新しいフェッチを強制するためにキャッシュをクリアする
  Discourse.cache.delete("embed-topic:#{topic_id}")

  begin
    expanded = TopicEmbed.expanded_for(post)
    puts "展開されたコンテンツの有無: #{expanded.present?}"
    puts "展開されたコンテンツの長さ: #{expanded&.length || 0}"
  rescue => e
    puts "expanded_for失敗: #{e.message}"
  end
end

# 5. 関連する設定を確認する
puts "\n--- サイト設定 ---"
puts "embed_truncate: #{SiteSetting.embed_truncate}"
puts "allowed_embed_selectors: #{SiteSetting.allowed_embed_selectors}"
puts "blocked_embed_selectors: #{SiteSetting.blocked_embed_selectors}"

これにより、https://tecnoblog.net/comunidade/t/governo-renova-app-da-cnh-para-baratear-obtencao-do-documento/157462?u=falco が失敗する理由がわかります

「いいね!」 4
discourse(prod)> # Replace with the topic ID or URL you’re debugging
discourse(prod)> topic_id = 386983
discourse(prod)>
discourse(prod)> # 1. Check if TopicEmbed exists and its content
discourse(prod)> te = TopicEmbed.find_by(topic_id: topic_id)
discourse(prod)> puts “TopicEmbed exists: #{te.present?}”
discourse(prod)> puts “Embed URL: #{te&.embed_url}”
discourse(prod)> puts “Content cache present: #{te&.embed_content_cache.present?}”
discourse(prod)> puts “Content cache length: #{te&.embed_content_cache&.length || 0}”
discourse(prod)> puts “Content SHA1: #{te&.content_sha1}”
discourse(prod)>
discourse(prod)> # 2. Check the actual cached content (first 500 chars)
discourse(prod)> puts “\n— Cached content preview —”
discourse(prod)> puts te&.embed_content_cache&.truncate(500)
discourse(prod)>
discourse(prod)> # 3. Try fetching from the remote URL
discourse(prod)* if te&.embed_url.present?
discourse(prod)*   puts “\n— Attempting remote fetch —”
discourse(prod)*   begin
discourse(prod)*     response = TopicEmbed.find_remote(te.embed_url)
discourse(prod)*     puts “Remote fetch success: #{response.present?}”
discourse(prod)*     puts “Remote body present: #{response&.body.present?}”
discourse(prod)*     puts “Remote body length: #{response&.body&.length || 0}”
discourse(prod)*     puts “Remote title: #{response&.title}”
discourse(prod)*     puts “Remote body: #{response&.body&.truncate(500)}”
discourse(prod)*   rescue => e
discourse(prod)*     puts “Remote fetch FAILED: #{e.message}”
discourse(prod)*   end
discourse(prod)> end
discourse(prod)>
discourse(prod)> # 4. Check what expanded_for would return
discourse(prod)* if te.present?
discourse(prod)*   puts “\n— Testing expanded_for —”
discourse(prod)*   post = Post.find(te.post_id)
discourse(prod)*
discourse(prod)*   # Clear cache to force fresh fetch
discourse(prod)*   Discourse.cache.delete(“embed-topic:#{topic_id}”)
discourse(prod)*
discourse(prod)*   begin
discourse(prod)*     expanded = TopicEmbed.expanded_for(post)
discourse(prod)*     puts “Expanded content present: #{expanded.present?}”
discourse(prod)*     puts “Expanded content length: #{expanded&.length || 0}”
discourse(prod)*   rescue => e
discourse(prod)*     puts “expanded_for FAILED: #{e.message}”
discourse(prod)*   end
discourse(prod)* end
discourse(prod)>
discourse(prod)> # 5. Check relevant settings
discourse(prod)> puts “\n— Site Settings —”
discourse(prod)> puts “embed_truncate: #{SiteSetting.embed_truncate}”
discourse(prod)> puts “allowed_embed_selectors: #{SiteSetting.allowed_embed_selectors}”
discourse(prod)> puts “blocked_embed_selectors: #{SiteSetting.blocked_embed_selectors}”
TopicEmbed exists: false
Embed URL:
Content cache present: false
Content cache length: 0
Content SHA1:

— Cached content preview —

— Site Settings —
embed_truncate: true
allowed_embed_selectors:
blocked_embed_selectors:
=> nil
discourse(prod)>

:thinking:

「いいね!」 1

このトピックIDで本当に合っていますか? https://tecnoblog.net/comunidade/t/-/386983 は404になります。

「いいね!」 1

ああ、そうでしたか。私がリンクしたトピックは実際には 157462 です。

失礼しました!

正しいトピックIDの結果はこちらです

TopicEmbed exists: true
Embed URL: https://tecnoblog.net/noticias/governo-renova-app-da-cnh-para-baratear-obtencao-do-documento
Content cache present: true
Content cache length: 22
Content SHA1:

— Cached content preview —

<div><div></div></div>

— Attempting remote fetch —
Remote fetch success: true
Remote body present: true
Remote body length: 22
Remote title:
Remote body: 

— Testing expanded_for —
Expanded content present: true
Expanded content length: 309

— Site Settings —
embed_truncate: true
allowed_embed_selectors:
blocked_embed_selectors:
=> nil

Cloudflareバイパスは機能しましたか? https://tecnoblog.net/noticias/governo-renova-app-da-cnh-para-baratear-obtencao-do-documento の本文は22文字だけで、タイトルタグがないようです。

はい!Discourseサーバーからのすべてのリクエストがバイパスされました。

Screenshot 2025-12-12 at 14.44.21

私が気づいたのは、埋め込みURLの末尾にスラッシュがないことです。すべてのURLには末尾のスラッシュが必要です。

Screenshot 2025-12-12 at 14.45.51

では、Discourseはリダイレクトに従っていないのでしょうか?

しかし、なぜ末尾のスラッシュなしでURLを保存しているのでしょうか?

「いいね!」 1

それは簡単にテストできます。試してみてください。

url = "https://tecnoblog.net/noticias/governo-renova-app-da-cnh-para-baratear-obtencao-do-documento/"
response = TopicEmbed.find_remote(url)
puts "リモートフェッチ成功: #{response.present?}"
puts "リモートボディの存在: #{response&.body.present?}"
puts "リモートボディの長さ: #{response&.body&.length || 0}"
puts "リモートタイトル: #{response&.title}"
puts "リモートボディ: #{response&.body&.truncate(500)}"

機能しているようです。

discourse(prod)> url = “https://tecnoblog.net/noticias/governo-renova-app-da-cnh-para-baratear-obtencao-do-documento/”
discourse(prod)> response = TopicEmbed.find_remote(url)
discourse(prod)> puts “Remote fetch success: #{response.present?}”
discourse(prod)> puts “Remote body present: #{response&.body.present?}”
discourse(prod)> puts “Remote body length: #{response&.body&.length || 0}”
discourse(prod)> puts “Remote title: #{response&.title}”
discourse(prod)> puts “Remote body: #{response&.body&.truncate(500)}”
Remote fetch success: true
Remote body present: true
Remote body length: 3776
Remote title: Governo renova app da CNH para baratear obtenção do documento • Tecnoblog
Remote body: 

<figure><img src="https://files.tecnoblog.net/wp-content/uploads/2025/12/cnh-brasil-app-1060x596.jpg">

	<figcaption>Aplicativo CNH do Brasil (imagem: Emerson Alecrim/Tecnoblog)</figcaption></figure>

</div>

<details>
    Resumo
    <div><ul>
<li>App CNH do Brasil substitui CDT e passa a oferecer recursos para obtenção da CNH, em especial, aulas teóricas gratuitas;</li>
<li>Aulas práticas continuam obrigatórias, mas a carga horária mínima foi reduzida de ...
=> nil


末尾のスラッシュなしの場合:

discourse(prod)> url = “https://tecnoblog.net/noticias/governo-renova-app-da-cnh-para-baratear-obtencao-do-documento”
discourse(prod)> response = TopicEmbed.find_remote(url)
discourse(prod)> puts “Remote fetch success: #{response.present?}”
discourse(prod)> puts “Remote body present: #{response&.body.present?}”
discourse(prod)> puts “Remote body length: #{response&.body&.length || 0}”
discourse(prod)> puts “Remote title: #{response&.title}”
discourse(prod)> puts “Remote body: #{response&.body&.truncate(500)}”
Remote fetch success: true
Remote body present: true
Remote body length: 22
Remote title:
Remote body: 
=> nil

投稿のスラッグが変更された古い投稿でも同じエラーが発生します。

例えば、こちらの投稿では、以前使用されていたURLは次のとおりでした。

https://tecnoblog.net/486925/o-que-e-pirataria-digital/

現在は次のようになっています。

https://tecnoblog.net/responde/o-que-e-pirataria-digital/

「いいね!」 1

それが主な問題のようです。Embed Discourse comments on another website via Javascriptを使用する場合、パラメーターでそれを制御でき、修正は非常に簡単です。

WP-Discourseがこれをどのように決定するかについてはよくわかりません。投稿の正規URLを使用しているはずですが、確信がありません。@angus、何かご存知ですか?

カテゴリのすべての埋め込み URL を、最終的な宛先に追従するように強制的に更新する方法はありますか?

準備ができたら、埋め込み Discourse(あなたがテストしてきた完全な埋め込み)に移行するつもりですが、埋め込み URL が一致しない場合、おそらくすべての投稿に対して新しいトピックが作成され、コメントが失われることになります…

「いいね!」 1

以下を実行してください

te = TopicEmbed.find_by(topic_id: 157462)
te.embed_url = te.embed_url + "/"
te.save

これで https://tecnoblog.net/comunidade/t/governo-renova-app-da-cnh-para-baratear-obtencao-do-documento/157462 は修正されますか?

動作しました!

しかし、このようなケースの修正方法はありますか?

Geminiはこのコードを提案しました。

# 設定
CATEGORY_SLUG = 'tb' 
category = Category.find_by(slug: CATEGORY_SLUG)

unless category
  puts "エラー: カテゴリ '#{CATEGORY_SLUG}' が見つかりません。"
  exit
end

puts "カテゴリ '#{category.name}' の完全なURLスキャンを開始します..."
puts "トピックの数やサイトの応答によっては時間がかかる場合があります..."

count_updated = 0
count_errors = 0
count_ok = 0

Topic.where(category_id: category.id).find_each do |topic|
  current_url = topic.custom_fields["embed_url"]
  
  # embed_urlがない場合はスキップ
  next unless current_url.present?

  begin
    # リダイレクトを追跡してGETリクエストを実行
    response = Faraday.get(current_url)
    final_url = response.env.url.to_s

    # リクエストが成功した場合 (200 OK)
    if response.status == 200
      # 最終URLがデータベースに保存されているURLと異なるか確認
      # ここでは厳密な文字列比較を行いますが、必要に応じて微妙な違いを無視する比較も可能です
      if final_url != current_url
        puts "\n[更新] トピック ##{topic.id}:"
        puts "   元:   #{current_url}"
        puts "   先: #{final_url}"
        
        topic.custom_fields["embed_url"] = final_url
        topic.save_custom_fields(true)
        count_updated += 1
      else
        # print "." # 視覚的な進行状況(点)を表示する場合はコメントアウトを外してください
        count_ok += 1
      end
    else
      puts "\n[HTTPエラー #{response.status}] トピック ##{topic.id} - URL: #{current_url}"
      count_errors += 1
    end

  rescue Faraday::ConnectionFailed, Faraday::TimeoutError => e
    puts "\n[接続失敗] トピック ##{topic.id} - URL: #{current_url} - #{e.message}"
    count_errors += 1
  rescue StandardError => e
    puts "\n[一般エラー] トピック ##{topic.id} - #{e.message}"
    count_errors += 1
  end
  
  # オプション: WordPressサーバーへの負荷を避けるための短い一時停止
  # sleep 0.1 
end

puts "\n\n最終要約:"
puts "------------------------------------------------"
puts "確認済みトピック (OK): #{count_ok}"
puts "更新されたトピック:      #{count_updated}"
puts "見つかったエラー:        #{count_errors}"
puts "------------------------------------------------"

ついに進展が見られましたね :sweat_smile:

そのようなスクリプトは素晴らしいアイデアですが、実行する前に必ずバックアップを取ってください

この小さなテーブルだけでもバックアップがあると非常に助かります。

「いいね!」 1

[quote=“Falco, post:18, topic:390811”]そのようなスクリプトは素晴らしいアイデアですが、実行する前に必ずバックアップを取ってください

[/quote]

了解です!チームがシフトを終えた後で実行してみます。

「いいね!」 1

皆さん、またしても末尾のスラッシュが問題を引き起こしているようですね :slight_smile:

[末尾のスラッシュは] 主な問題のようです。Javascriptを使用して別のウェブサイトにDiscourseコメントを埋め込む場合、パラメータで制御でき、修正は非常に簡単です。

注意点として、Discourseのすべてのトピック埋め込みはembed_urlから末尾のスラッシュを削除します。詳細はTopicEmbed.normalize_urlを参照してください。Javascript埋め込みとWP Discourse埋め込みの交差に関連する別のケースの結果として、両方の埋め込み方法でこの処理を標準化しました。https://github.com/discourse/discourse/pull/30641を参照してください。

@Thiago_Mobilon この移行の過程で、あなたのDiscourseも更新しましたか?サブフォルダへの移行と同時に行われたDiscourseの更新の結果として、embed_url正規化の標準化がWP Discourseの埋め込みに適用されているのを見ている可能性があります。現在、Discourseのどのバージョンを実行していますか?(そして、もし分かれば、移行前のバージョンは何でしたか?)

ちょっとした補足ですが、最新バージョンのDiscourseでローカルでこれらの2つのコマンドを実行すると、同じ結果、つまり記事のHTML本文が得られます。

# 末尾のスラッシュあり
TopicEmbed.find_remote("https://tecnoblog.net/noticias/governo-renova-app-da-cnh-para-baratear-obtencao-do-documento/")

# 末尾のスラッシュなし
TopicEmbed.find_remote("https://tecnoblog.net/noticias/governo-renova-app-da-cnh-para-baratear-obtencao-do-documento")

# 同じ結果を生成する

WordPress側で何か変更を加えましたか?

「いいね!」 2

こんにちは、Angusさん!

いいえ、これらは別の問題です。末尾のスラッシュはサブフォルダに移動したときに発生しましたが、何年も前の古いURLで、現在スラッグが異なるものもあります。

インストールを再構築する必要があったので、はい、この新しい標準が原因であると考えています。

この問題を解決するための私の提案:Discourseはデータを取得するために少なくとも1つか2つのリダイレクトをたどることはできませんか?これにより、末尾のスラッシュの問題が解決し、将来的にURLが変更される可能性があってもウェブサイトが堅牢になります。

また、古いトピックを更新するためにスクリプトを実行する必要がなくなるため、データベースに損害を与える可能性もあるため、より安全です。