noindex for requireauth pages

This commit is contained in:
Michael Black
2026-06-09 11:17:17 -05:00
parent f41c237104
commit 9412646b8f
2 changed files with 66 additions and 10 deletions
+54
View File
@@ -43,6 +43,13 @@ func extractJSONLD(t *testing.T, html string) string {
return strings.TrimSpace(m[1])
}
func TestRenderBase_NoindexMeta(t *testing.T) {
html := renderTemplate(t, "base.html", pongo2.Context{"noindex": true, "nofollow": true})
if !strings.Contains(html, `<meta name="robots" content="noindex, nofollow">`) {
t.Errorf("expected combined noindex,nofollow meta; got:\n%s", html)
}
}
func TestRenderPost_EmitsJSONLD(t *testing.T) {
pv := makePostView("alice.bsky.social", "did:plc:alice", "abc123", "hello")
ld, err := buildPostJSONLD(pv, nil, "https://bsky.app/profile/alice.bsky.social/post/abc123", hideEmbedLabels, hideReplyLabels)
@@ -209,3 +216,50 @@ func TestRenderPost_VideoWithoutThumbnailEmitsOGVideo(t *testing.T) {
t.Errorf("og:video:type should emit even without imgThumbUrls; got:\n%s", html)
}
}
// Auth-required posts must emit noindex,nofollow so the stub page (no body
// text, no comments) is not indexed.
func TestRenderPost_AuthRequiredNoindex(t *testing.T) {
html := renderTemplate(t, "post.html", pongo2.Context{
"requiresAuth": true,
"profileHandle": "alice.bsky.social",
"requestURI": "https://bsky.app/profile/alice.bsky.social/post/abc123",
"canonicalURL": "https://bsky.app/profile/alice.bsky.social/post/abc123",
"noindex": true,
"nofollow": true,
})
if !strings.Contains(html, `<meta name="robots" content="noindex, nofollow">`) {
t.Errorf("auth-required post should emit noindex,nofollow; got:\n%s", html)
}
}
// Auth-required profiles must emit noindex,nofollow.
func TestRenderProfile_AuthRequiredNoindex(t *testing.T) {
pv := newProfileViewDetailed()
html := renderTemplate(t, "profile.html", pongo2.Context{
"profileView": pv,
"requestURI": "https://bsky.app/profile/alice.bsky.social",
"requiresAuth": true,
"noindex": true,
"nofollow": true,
})
if !strings.Contains(html, `<meta name="robots" content="noindex, nofollow">`) {
t.Errorf("auth-required profile should emit noindex,nofollow; got:\n%s", html)
}
}
// Public posts must NOT emit a robots meta tag. Guards against an accidental
// flip of the noindex flag for indexable pages.
func TestRenderPost_PublicNoNoindex(t *testing.T) {
pv := makePostView("alice.bsky.social", "did:plc:alice", "abc123", "hello")
ld, _ := buildPostJSONLD(pv, nil, "https://bsky.app/profile/alice.bsky.social/post/abc123", hideEmbedLabels, hideReplyLabels)
html := renderTemplate(t, "post.html", pongo2.Context{
"postView": pv,
"requestURI": "https://bsky.app/profile/alice.bsky.social/post/abc123",
"canonicalURL": "https://bsky.app/profile/alice.bsky.social/post/abc123",
"postJSONLD": ld,
})
if strings.Contains(html, `<meta name="robots"`) {
t.Errorf("public post should not emit robots meta; got:\n%s", html)
}
}
+12 -10
View File
@@ -499,24 +499,22 @@ type renderOptions struct {
// webGeneric returns a handler that renders the base SPA shell with the given
// render options applied to the template context.
func (srv *Server) webGeneric(o renderOptions) echo.HandlerFunc {
return func(c echo.Context) error {
data := srv.NewTemplateContext()
data["noindex"] = o.noindex
data["nofollow"] = o.nofollow
return c.Render(http.StatusOK, "base.html", data)
}
func (srv *Server) webGeneric(c echo.Context, o renderOptions) error {
data := srv.NewTemplateContext()
data["noindex"] = o.noindex
data["nofollow"] = o.nofollow
return c.Render(http.StatusOK, "base.html", data)
}
// handler for endpoint that have no specific server-side handling
func (srv *Server) WebGeneric(c echo.Context) error {
return srv.webGeneric(renderOptions{})(c)
return srv.webGeneric(c, renderOptions{})
}
// handler for routes that should not be indexed by search engines
// (e.g. auth-only user-state surfaces, internal/debug pages, action/intent dispatch URLs, search results)
func (srv *Server) WebGenericNoindex(c echo.Context) error {
return srv.webGeneric(renderOptions{noindex: true})(c)
return srv.webGeneric(c, renderOptions{noindex: true})
}
// handler for action/intent dispatch URLs (e.g. /intent/compose). These accept
@@ -524,7 +522,7 @@ func (srv *Server) WebGenericNoindex(c echo.Context) error {
// them as link-graph dead-ends in addition to noindex. Anything legitimately
// reachable from a hydrated intent page is also reachable via its canonical URL.
func (srv *Server) WebGenericNoindexNofollow(c echo.Context) error {
return srv.webGeneric(renderOptions{noindex: true, nofollow: true})(c)
return srv.webGeneric(c, renderOptions{noindex: true, nofollow: true})
}
func (srv *Server) WebHome(c echo.Context) error {
@@ -599,6 +597,8 @@ func (srv *Server) WebPost(c echo.Context) error {
data["canonicalURL"] = canonicalURL
}
data["requiresAuth"] = true
data["noindex"] = true
data["nofollow"] = true
data["profileHandle"] = pv.Handle
if pv.DisplayName != nil {
data["profileDisplayName"] = *pv.DisplayName
@@ -793,6 +793,8 @@ func (srv *Server) WebProfile(c echo.Context) error {
}
} else {
data["requiresAuth"] = true
data["noindex"] = true
data["nofollow"] = true
}
if jsonld, err := buildProfileJSONLD(pv, recentPosts, hideEmbedLabels, hideReplyLabels); err == nil {